パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
ソースコード(バージョン番号管理)

バージョン番号を設定あるいは統一管理するファイルサンプルを示す。

動作確認(3)のDesktop2プロジェクトで例示する。いくつかのファイルは動作確認(2)のDesktop1プロジェクトで作成配置したファイルからの修正として説明する。

バージョン番号設定テンプレートファイル

自作ツールKAutoVerはプロジェクトディレクトリのバージョン番号データファイルautoversion.datを読み込み、autoversion-templateディレクトリ内のバージョン番号設定テンプレートファイルのプレースホルダー文字列をバージョン番号に置き換え、プロジェクトディレクトリ内の対応するバージョン番号設定ファイルを上書きする。

バージョンマクロ定義インクルードテンプレートファイル

C++/C言語ソースコード/インクルードファイルにバージョン番号設定ファイルとしてインクルードされるインクルードファイルのテンプレートファイルを示す。このファイルはウィンドウズリソースファイルとInno Setupスクリプトファイルにもインクルードされるため共通構文の#define定数のみを使用する。バージョン番号は定数マクロとして定義される。

version_macro.h(テンプレートファイル)

#ifndef VERSION_MACRO_H
#define VERSION_MACRO_H
#define AUTOVERSION_MAJOR #{MAJOR}
#define AUTOVERSION_MINOR #{MINOR}
#define AUTOVERSION_BUILD #{BUILD}
#define AUTOVERSION_REVISION #{REVISION}
#define AUTOVERSION_BUILD_COUNT #{BUILD_COUNT}
#define AUTOVERSION_STR_DATE "#{DATE}"
#define AUTOVERSION_STR_MONTH "#{MONTH}"
#define AUTOVERSION_STR_MONTH_SHORT_NAME "#{MONTH_SHORT_NAME}"
#define AUTOVERSION_STR_YEAR "#{YEAR}"
#define AUTOVERSION_STR_YEAR_2DIGIT "#{YEAR_2DIGIT}"
#define AUTOVERSION_STR_STATUS "#{STATUS}"
#define AUTOVERSION_STR_STATUS_SHORT "#{STATUS_SHORT}"
#define AUTOVERSION_STR_VER "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}"
#define AUTOVERSION_STR_VER_STATUS_SHORT "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}(#{STATUS_SHORT})"
#define AUTOVERSION_STR_VER_STATUS "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}-#{STATUS}"
#define AUTOVERSION_STR_VER_BUILD_COUNT "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}(#{BUILD_COUNT})"
#define AUTOVERSION_STR_VER_BUILD_COUNT_STATUS_SHORT "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}(#{BUILD_COUNT})(#{STATUS_SHORT})"
#define AUTOVERSION_STR_VER_BUILD_COUNT_STATUS "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}(#{BUILD_COUNT})-#{STATUS}"
#define AUTOVERSION_STR_DATE_YMD "#{YEAR}/#{MONTH}/#{DATE}"
#define AUTOVERSION_STR_DATE_MDY "#{MONTH}/#{DATE}/#{YEAR}"
#define AUTOVERSION_STR_DATE_DMY "#{DATE}.#{MONTH}.#{YEAR}"
#define AUTOVERSION_STR_DATE_DMY_MONTH_SHORT_NAME "#{DATE}-#{MONTH_SHORT_NAME}-#{YEAR}"
#define AUTOVERSION_STR_GUID "{#{GUID}}"
#endif //VERSION_MACRO_H

PROJECT_NUMBER定義Doxygen設定テンプレートファイル

Doxygen設定ファイルにバージョン番号設定ファイルとしてインクルードされる設定ファイルのテンプレートファイルを示す。バージョン番号はPROJECT_NUMBERオプションに設定される。

Doxyfile_autoversion(テンプレートファイル)

PROJECT_NUMBER = "#{MAJOR}.#{MINOR}.#{BUILD}.#{REVISION}"

バージョン番号統一管理ファイル

バージョン番号が統一管理されるファイルはテンプレートファイルのプレースホルダー文字列をバージョン番号に置換したバージョン番号設定ファイルをインクルードする。なおウィザード生成ソースコードを修正してバージョン番号を統一管理するファイルは後述とする。

Doxygen設定ファイル

ソースコード解析用Doxygen設定ファイルはDesktop1のコピーを修正する。

Doxyfile(追加部分)

  • 既存のPROJECT_NUMBERオプション行を削除あるいはコメントアウトする。
  • Doxyfile_autoversionをインクルードする。
  • 文字列Desktop1をDesktop2に置換する。
#------------------------------------------------ [Additional Setting Start]
#---------------------------------------------------------------------------
# Customized configurations are placed here appending to those generated by
# "doxygen -g" above.
# Project
PROJECT_NAME = "パソコンでプログラミングしよう(Desktop2)"
#PROJECT_NUMBER = "0.0.0.0(0)"
PROJECT_BRIEF = "金をかけずにウィンドウズC++プログラミング環境を構築する"
# C++ source
FULL_PATH_NAMES = NO
BUILTIN_STL_SUPPORT = YES
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_ANON_NSPACES = YES
SOURCE_BROWSER = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = "WXUNUSED()=" \
"MY_PIMPL(smart_ptr,cls,name)=class cls;cls* name; /**<\brief smart_ptr<cls> at actual compilation, replaced with cls* when Doxygen analyzes.*/" \
"MY_CHECK_MAGIC_SELECTION()=1" \
"__attribute__()="
HAVE_DOT = YES
DOT_PATH = "C:\msys64\mingw64\bin"
CLASS_DIAGRAMS = NO
CLASS_GRAPH = NO
TEMPLATE_RELATIONS = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
# Warnings
WARN_IF_UNDOCUMENTED = NO
WARN_LOGFILE = doxygen.log
# Input files
INPUT = ../ \
./
FILE_PATTERNS = *.c \
*.cpp \
*.h \
*.hpp \
*.dox \
*.md
EXAMPLE_PATH = ../ \
./
IMAGE_PATH = ../ \
./
# Appearance
OUTPUT_LANGUAGE = Japanese
LAYOUT_FILE = my_layout.xml
HTML_HEADER = my_header.html
HTML_FOOTER = my_footer.html
HTML_EXTRA_STYLESHEET = my_customdoxygen.css
HTML_COLORSTYLE_HUE = 50
HTML_COLORSTYLE_SAT = 50
HTML_COLORSTYLE_GAMMA = 220
HTML_TIMESTAMP = YES
HTML_DYNAMIC_MENUS = NO
GENERATE_TREEVIEW = YES
TREEVIEW_WIDTH = 275
TOC_INCLUDE_HEADINGS = 2
SEARCHENGINE = YES
# Other outputs than html
GENERATE_LATEX = NO
# Include pages for Windows HTML help
INPUT += ./winchm/
ALIASES += "mainpage_winchm=\page mainpage_winchm"
# Include version numbers
@INCLUDE = Doxyfile_autoversion
#---------------------------------------------------------------------------
#-------------------------------------------------- [Additional Setting End]

HTLMヘルプ作成用Doxygen設定ファイルはソースコード解析用Doxygen設定ファイルをインクルードするのでDoxyfile_autoversionのインクルードは必要ない。独自にバージョン番号を参照する箇所は無くDesktop1のコピーへの修正は文字列置換だけとなる。

Doxyfile_winchm

  • 文字列Desktop1をDesktop2に置換する。
@INCLUDE = Doxyfile
# CHM_FILE path is relative to "./html_winchm" where index.hhp is in.
# It is not relateve to "." where this file is in.
WARN_LOGFILE = doxygen_winchm.log
HTML_OUTPUT = ./html_winchm/
INPUT = ./winchm/
GENERATE_HTMLHELP = YES
GENERATE_TREEVIEW = NO
SEARCHENGINE = NO
CHM_FILE = ../../bin/Desktop2.chm
HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop\hhc.exe"
CHM_INDEX_ENCODING = CP932
ALIASES += "mainpage_winchm=\mainpage"

Inno Setupスクリプトファイル

Inno SetupスクリプトファイルはDesktop1のコピーを修正する。

InnoSetup.iss

  • version_macro.hをインクルードする。
  • 変数MyAppVersionとMyAppCopyrightにインクルードされた変数利用に変更する。
  • 変数MyAppIdはユニークな文字列が必要だが、インクルードされた変数AUTOVERSION_STR_GUIDにプロジェクト固有のGUID文字列が代入されているのでこれを利用する。
  • 文字列Desktop1をDesktop2に置換する。
; Script initially generated by the Inno Setup Script Wizard, modified by Takeshi Kodama.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#include "..\version_macro.h"
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
#define MyAppId "{"+AUTOVERSION_STR_GUID
#define MyAppName "Desktop2"
#define MyAppVersion AUTOVERSION_STR_VER
#define MyAppPublisher "KodamaDeveloped"
;#define MyAppURL "http://www.example.com/"
#define MyAppExeName MyAppName+".exe"
#define MyAppCopyright "(C)"+AUTOVERSION_STR_YEAR+" Takeshi Kodama"
#define MyDir "..\bin\"
#define MyAppExePath32 MyDir+"Release32\"+MyAppExeName
#define MyAppExePath64 MyDir+"Release64\"+MyAppExeName
#define MyAppChmPath MyDir+MyAppName+".chm"
#define MyAppLangPattern MyDir+"*"
#define MyAppMoPath(lang) MyDir+lang+"\"+MyAppName+".mo"
[Setup]
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
;AppPublisherURL={#MyAppURL}
;AppSupportURL={#MyAppURL}
;AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppPublisher}\{#MyAppName}
DefaultGroupName={#MyAppPublisher}
OutputDir=.
OutputBaseFilename={#MyAppName}-{#MyAppVersion}
Compression=lzma
SolidCompression=yes
AppCopyright={#MyAppCopyright}
VersionInfoVersion={#MyAppVersion}
DisableWelcomePage=False
ArchitecturesInstallIn64BitMode=x64
UninstallDisplayIcon={app}\{#MyAppExeName}
WizardStyle=modern
#if !FileExists(MyAppExePath32)
ArchitecturesAllowed=x64
#endif
[Languages]
Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl"
[Files]
#if FileExists(MyAppExePath32)
#if FileExists(MyAppExePath64)
Source: "{#MyAppExePath32}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#MyAppExePath64}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
#else
Source: "{#MyAppExePath32}"; DestDir: "{app}"; Flags: ignoreversion
#endif
#else
#if FileExists(MyAppExePath64)
Source: "{#MyAppExePath64}"; DestDir: "{app}"; Flags: ignoreversion
#else
#error No execution files are found
#endif
#endif
#if FileExists(MyAppChmPath)
Source: "{#MyAppChmPath}"; DestDir: "{app}"; Flags: ignoreversion
#endif
#sub FindPoFileProc
#define MyLang FindGetFileName(MyFindHandle)
#if !(SameText(MyLang,".")||SameText(MyLang,".."))&&FileExists(MyAppMoPath(MyLang))
Source: "{#MyAppMoPath(MyLang)}"; DestDir: "{app}\{#MyLang}"; Flags: ignoreversion
#endif
#undef MyLang
#endsub
#define MyFindHandle FindFirst(MyAppLangPattern,faDirectory)
#define MyFindResult
#for {MyFindResult=MyFindHandle;MyFindResult;MyFindResult=FindNext(MyFindHandle)} FindPoFileProc
#if MyFindHandle
#expr FindClose(MyFindHandle)
#endif
#undef MyFindResult
#undef MyFindHandle
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[Registry]
Root: "HKA"; Subkey: "Software\{#MyAppPublisher}"; Flags: uninsdeletekeyifempty
Root: "HKA"; Subkey: "Software\{#MyAppPublisher}\{#MyAppName}"; Flags: uninsdeletekey

ソースコードの修正

Desktop2プロジェクトはwxWidgetsプロジェクトウィザード(K1)の生成するソースコードに国際化機能ヘルプファイル表示機能、および本項目が主題とする統一管理されたバージョン番号の表示機能を追加する。ヘルプファイルを表示する[Help|Help]メニュー項目は既に追加されているものとする。

Desktop2Appクラス

Desktop2Appクラスには国際化機能を追加する。

Desktop2App.h

  • プライベートメンバ変数にwxLocale型のlocale_を追加する。
#ifndef DESKTOP2APP_H
#define DESKTOP2APP_H
#include <wx/app.h>
class Desktop2App : public wxApp
{
private:
wxLocale locale_;
public:
virtual bool OnInit();
};
#endif // DESKTOP2APP_H

Desktop2App.cpp

  • Desktop2App::OnInit関数でlocale_メンバ変数を初期設定する。
#include "wx_pch.h"
#include "Desktop2App.h"
#include <wx/stdpaths.h>
//(*AppHeaders
#include "Desktop2Frame.h"
#include <wx/image.h>
//*)
IMPLEMENT_APP(Desktop2App);
bool Desktop2App::OnInit()
{
// To ignore application sub directory other than "Debug" and "Release".
// Only meaningful at development stage.
wxStandardPaths::Get().IgnoreAppSubDir("Debug*");
wxStandardPaths::Get().IgnoreAppSubDir("Release*");
locale_.Init(wxLANGUAGE_DEFAULT,wxLOCALE_DONT_LOAD_DEFAULT);
locale_.AddCatalog("Desktop2");
locale_.AddCatalog("wxstd");
//(*AppInitialize
bool wxsOK = true;
wxInitAllImageHandlers();
if ( wxsOK )
{
Desktop2Frame* Frame = new Desktop2Frame(0);
Frame->Show();
SetTopWindow(Frame);
}
//*)
return wxsOK;
}

Desktop2Frameクラス

Desktop2Frameクラスにはヘルプファイル表示機能と統一管理されたバージョン番号の表示機能を追加する。Desktop2Frame.cppはバージョン番号が統一管理されるファイルとしてバージョン番号設定ファイルversion_macro.hをインクルードする。

Desktop2Frame.h

  • wx/help.hをインクルードする。
  • プライベートメンバ変数にwxHelpController型のhelpController_を追加する。
#ifndef DESKTOP2MAIN_H
#define DESKTOP2MAIN_H
//(*Headers(Desktop2Frame)
#include <wx/frame.h>
#include <wx/menu.h>
#include <wx/statusbr.h>
//*)
#include <wx/help.h>
class Desktop2Frame: public wxFrame
{
public:
Desktop2Frame(wxWindow* parent,wxWindowID id = -1);
virtual ~Desktop2Frame();
private:
//(*Handlers(Desktop2Frame)
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnHelp(wxCommandEvent& event);
//*)
//(*Identifiers(Desktop2Frame)
static const long idMenuQuit;
static const long idMenuHelp;
static const long idMenuAbout;
static const long ID_STATUSBAR1;
//*)
//(*Declarations(Desktop2Frame)
wxStatusBar* StatusBar1;
//*)
wxHelpController helpController_;
DECLARE_EVENT_TABLE()
};
#endif // DESKTOP2MAIN_H

Desktop2Frame.cpp(抜粋)

  • wx/stdpaths.hとwx/filename.hをインクルードする。
  • コンストラクタにメンバ変数helpController_の初期化を加える。
  • version_macro.hをインクルードし、Desktop2Frame::OnAbout関数でそのマクロ変数を用いた統一管理バージョン番号を表示する。
  • 不要となるファイル冒頭のヘルパー関数wxbuildinfoを削除する。
  • Desktop2Frame::OnHelp関数でヘルプファイルを表示する。
#include "wx_pch.h"
#include "Desktop2Frame.h"
#include <wx/msgdlg.h>
#include <wx/stdpaths.h>
#include <wx/filename.h>
#include "version_macro.h"
//(*InternalHeaders(Desktop2Frame)
#include <wx/intl.h>
#include <wx/string.h>
//*)
//(*IdInit(Desktop2Frame)
const long Desktop2Frame::idMenuQuit = wxNewId();
const long Desktop2Frame::idMenuHelp = wxNewId();
const long Desktop2Frame::idMenuAbout = wxNewId();
const long Desktop2Frame::ID_STATUSBAR1 = wxNewId();
//*)
BEGIN_EVENT_TABLE(Desktop2Frame,wxFrame)
//(*EventTable(Desktop2Frame)
//*)
END_EVENT_TABLE()
Desktop2Frame::Desktop2Frame(wxWindow* parent,wxWindowID id)
:helpController_{this}
{
//(*Initialize(Desktop2Frame)
...
Connect(idMenuQuit,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&Desktop2Frame::OnQuit);
Connect(idMenuHelp,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&Desktop2Frame::OnHelp);
Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&Desktop2Frame::OnAbout);
//*)
helpController_.Initialize
(wxFileName{wxStandardPaths::Get().GetDataDir(),"Desktop2.chm"}
.GetFullPath());
}
...
void Desktop2Frame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg<<_("Desktop2")<<"\n"
<<_("Version: ")<<AUTOVERSION_STR_VER_BUILD_COUNT_STATUS<<"\n"
<<_("Date: ")<<AUTOVERSION_STR_DATE_DMY_MONTH_SHORT_NAME;
wxMessageBox(msg, _("Welcome to..."));
}
void Desktop2Frame::OnHelp(wxCommandEvent& event)
{
helpController_.DisplayContents();
}

ウィンドウズリソースファイル

ウィンドウズリソースファイルはバージョン番号が統一管理されるファイルとしてバージョン番号設定ファイルversion_macro.hをインクルードし各種のファイルメタデータを書き込む。FILEVERSION、PRODUCTVERSION、StringFileInfo\040904E4ブロックのFileVersion、LegalCopyright、ProductVersionがインクルードされた変数を利用する。メタデータはエクスプローラ上のファイルアイコン右クリック[プロパティ]で開く[Desktop2.exeのプロパティ]ダイアログ[詳細]ページに表示される。

resource.rc

aaaa ICON "wx/msw/std.ico"
#include "wx/msw/wx.rc"
#include "version_macro.h"
1 VERSIONINFO
FILEVERSION AUTOVERSION_MAJOR,AUTOVERSION_MINOR,AUTOVERSION_BUILD,AUTOVERSION_REVISION
PRODUCTVERSION AUTOVERSION_MAJOR,AUTOVERSION_MINOR,AUTOVERSION_BUILD,AUTOVERSION_REVISION
FILETYPE VFT_APP
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName","KodamaDeveloped"
VALUE "FileVersion",AUTOVERSION_STR_VER_BUILD_COUNT_STATUS
VALUE "FileDescription","Desktop2 Execution"
VALUE "InternalName","Desktop2.exe"
VALUE "LegalCopyright","(C)" AUTOVERSION_STR_YEAR " Takeshi Kodama"
VALUE "LegalTrademarks",""
VALUE "OriginalFilename","Desktop2.exe"
VALUE "ProductName","Desktop2"
VALUE "ProductVersion",AUTOVERSION_STR_VER_BUILD_COUNT_STATUS
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation",0x0,1200
END
END
覚え書き
マイクロソフトのドキュメントを含み多くの参考がリソースファイルBLOCKステートメントに書き込まれる文字列データにヌルターミネーター(\0)を付加するが、本来その必要は無く本サイトはこれを省く。文字列データは実行形式ファイル内にヌルターミネーターを付加せず連続領域に書き込まれ、文字数情報を別領域に記憶する。この文字列データはAPI関数LoadStringで文字列先頭アドレスと文字数を取得して読み込む。しかし不完全な実装は先頭アドレスだけを用いるため、ヌルターミネーターが付加されていないと後続の文字列データを連続して取得する。ヌルターミネーターはそのような不完全な実装への安全弁にすぎない。