Open rainit2006 opened 6 years ago
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding1;
VS_FIXEDFILEINFO Value;
WORD Padding2;
WORD Children;
} VS_VERSIONINFO;
Children: An array of zero or one StringFileInfo structures, and zero or one VarFileInfo structures that are children of the current VS_VERSIONINFO structure.
typedef struct tagVS_FIXEDFILEINFO {
DWORD dwSignature;
DWORD dwStrucVersion;
DWORD dwFileVersionMS;
DWORD dwFileVersionLS;
DWORD dwProductVersionMS;
DWORD dwProductVersionLS;
DWORD dwFileFlagsMask;
DWORD dwFileFlags;
DWORD dwFileOS;
DWORD dwFileType;
DWORD dwFileSubtype;
DWORD dwFileDateMS;
DWORD dwFileDateLS;
} VS_FIXEDFILEINFO;
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding;
StringTable Children;
} StringFileInfo;
StringTable Represents the organization of data in a file-version resource. It contains language and code page formatting information for the strings specified by the Children member.
StringFileInfotypedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
//***********
An 8-digit hexadecimal number stored as a Unicode string. The four most significant digits represent the language identifier. The four least significant digits represent the code page for which the data is formatted. Each Microsoft Standard Language identifier contains two parts: the low-order 10 bits specify the major language, and the high-order 6 bits specify the sublanguage.
***********//
WORD Padding; // As many zero words as necessary to align the Children member on a 32-bit boundary.
String Children; //An array of one or more String structures.
} StringTable;
The Children member of the StringFileInfo structure contains at least one StringTable structure.
例:
StringTable()
{
ValueLength = 0;
Type = 1;
Key = "000004b0"; //Unicode code page
Strings = new List<String>();
}
typedef struct {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey;
WORD Padding;
Var Children; //Typically contains a list of languages that the application or DLL supports.
} VarFileInfo;
BeginUpdateResource 実行可能ファイル内のリソースの追加、削除、置き換えを行うときに UpdateResource 関数に渡すハンドルを取得します。
HANDLE BeginUpdateResource(
LPCTSTR pFileName, // 更新するリソースが入っているファイル名への
// ポインタ
BOOL bDeleteExistingResources // 削除オプション
);
UpdateResource 実行可能ファイル内のリソースの追加、削除、置き換えを行います。
BOOL UpdateResource(
HANDLE hUpdate, // 更新ファイルのハンドル
LPCTSTR lpType, // 更新するリソースタイプのアドレス。
//RT_VERSION であれば、バージョンリソースを表す。
LPCTSTR lpName, // 更新するリソース名のアドレス
WORD wLanguage, // リソースの言語識別子
LPVOID lpData, // リソースデータのアドレス
DWORD cbData // リソースデータの長さ( バイト数)
);
例:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr BeginUpdateResource(
[In] string pFileName,
[In, MarshalAs(UnmanagedType.Bool)] bool bDeleteExistingResources);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateResource(
[In] IntPtr hUpdate,
[In] IntPtr lpType,
[In] IntPtr lpName,
[In] UInt16 wLanguage,
[In] IntPtr lpData,
[In, MarshalAs(UnmanagedType.U4)] int cbData);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EndUpdateResource(
[In] IntPtr hUpdate,
[In, MarshalAs(UnmanagedType.Bool)] bool fDiscard);
.........................
public bool Save()
{
bool result = false;
byte[] buffer = _info.ToBinary();
int size = buffer.Length;
IntPtr data = Marshal.AllocHGlobal(buffer.Length);
try
{
Marshal.Copy(buffer, 0, data, size);
IntPtr hUpdate = Win32Native.BeginUpdateResource(_fileName, false);
if (hUpdate != IntPtr.Zero)
{
result = Win32Native.UpdateResource(hUpdate, Win32Native.RT_VERSION, _resourceID, _langID, data, size);
result = Win32Native.EndUpdateResource(hUpdate, false) && result;
}
}
finally
{
if(data != IntPtr.Zero)
{
Marshal.FreeHGlobal(data);
}
}
return result;
}
注意:[return: MarshalAs(UnmanagedType.Bool)]的声明!否则在C#里调用函数时返回的是1byte的bool,而C语言里返回的是4byte的bool。不声明的话会发生问题(信息缺失,导致bool的true或false的结果不对)。
//考虑到update失败的case,加上retry处理。
public bool Save()
{
bool result = false;
byte[] buffer = _info.ToBinary();
int size = buffer.Length;
IntPtr data = Marshal.AllocHGlobal(buffer.Length);
try
{
Marshal.Copy(buffer, 0, data, size);
int waitTime = 50;
int retry = 5;
result = false;
while (retry > 0 && !result)
{
IntPtr hUpdate = Win32Native.BeginUpdateResource(_fileName, false);
if (hUpdate != IntPtr.Zero)
{
var result1 = Win32Native.UpdateResource(hUpdate, Win32Native.RT_VERSION, _resourceID, _langID, data, size);
var lastError1 = Marshal.GetLastWin32Error();
var result2 = Win32Native.EndUpdateResource(hUpdate, false);
var lastError2 = Marshal.GetLastWin32Error();
result = result1 && result2;
}
if(!result)
{
System.Threading.Thread.Sleep(waitTime);
waitTime *= 2;
retry--;
}
}
}
finally
{
if(data != IntPtr.Zero)
{
Marshal.FreeHGlobal(data);
}
}
return result;
}
VerQueryValue https://msdn.microsoft.com/ja-jp/library/cc364858.aspx 指定されたバージョン情報リソースから、指定されたバージョン情報を取得します。
pBlock バッファを正しく初期化するために、VerQueryValue を呼び出す前に、GetFileVersionInfoSize と GetFileVersionInfo の各関数を呼び出してください。
BOOL VerQueryValue(
const LPVOID pBlock, // バージョン情報リソースのバッファ
LPTSTR lpSubBlock, // 取得対象の情報の種類
LPVOID *lplpBuffer, // バージョン情報へのポインタを格納するバッファ
PUINT puLen // バージョンの長さ
);
lpSubBlock:取得対象の情報を示す、NULL で終わる文字列へのポインタを指定します 「\VarFileInfo\Translation」であれば : 1 個の 可変情報構造体に含まれている変換テーブルを指定します。
NativeMethods.VerQueryValue(block, @"\VarFileInfo\Translation", out buffer, out bufferLength))
「\StringFileInfo\lang-codepage\string-name」であれば、言語固有の StringTable 構造体内の 1 つの値を指定します。
var subBlock = $@"\StringFileInfo\{langid:x4}{codepage:x4}\FileVersionまたはProductVersion";
NativeMethods.VerQueryValue(block, subBlock, out buffer, out bufferLength)
バージョン情報に関連してあらかじめ定義されている Unicode 文字列を示します。
例:次のサンプルコードは、利用可能なバージョン言語を列挙し、言語ごとに FileDescription 文字列の値を取得する方法を示します。
// 列挙された言語とコードページを格納するために使われる構造体。
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// 言語とコードページのリストを読み取る。
VerQueryValue(pBlock,
TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate,
&cbTranslate);
// 各言語とコードページのファイルの説明を読み取る。
for(i=0; i <(cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++)
{
wsprintf(SubBlock,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
// i 番目の言語とコードページのファイルの説明を取得する。
VerQueryValue(pBlock,
SubBlock,
&lpBuffer,
&dwBytes);
}
PS: wsprintf関数:文字列を書式化して、値をバッファに格納します。 int wsprintf( LPTSTR lpOut, // 出力バッファ LPCTSTR lpFmt, // 書式制御文字列 ... // オプションの引数 );
この時、どのようなデータ形式を取るかはプログラマしだいと言えます 独自の仕様の構造体を作成し、それをバイナリファイルとして保存することも考えられますし 筆者としては、独自の XML アプリケーションを策定し、XML で保存することを推奨します もっとも、この場合は、XML のプログラム技術を身につける必要があります
しかし、そんな面倒なことをしなくても Windows はその手段を用意しています アプリケーションが自分自身のための情報を保存する簡単な方法に レジストリ と 初期化ファイル *.ini のどちらかを選択することができます.
初期化ファイルは、アプリケーションが独自に保有するファイルと Windows が統合して保有する WIN.INI ファイルに分けられます.
初期化ファイルはセクションで項目を区切り、キーを与えることができます つまり、セクションとそれに属する複数のキーで表現されるのです.
[Section1]
KeyID1 = Value1
KeyID2 = Value2
...
[Section2]
...
キーの値を取得するには GetPrivateProfileInt() 関数を使います キーが文字列の値を持っている場合は GetPrivateProfileString() を使います
` ini文件 [ParamOption] ParamA= ParamB=/s /f2"../setup.log" ParamC=
代码: NativeMethods.GetPrivateProfileString("ParamOption", "ParamA", "", sbParamA, 1024, filePath); `