确定EXE中包含哪个STRINGTABLE
Determine which STRINGTABLE is included in EXE
我的 Delphi 应用程序使用 Microsoft 资源编译器 (rc.exe) 编译字符串列表(.rc
文件中的文本格式),如下所示:
Language LANG_KOREAN, SUBLANG_KOREAN
STRINGTABLE
BEGIN
cszLanguageName "Korean"
<etc>
END
到 .res
文件中。所有表都包含相同的 ID(例如 cszLanguageName
)。我维护两个单独的资源字符串文件。其中一个主要包含欧洲语言(英语、捷克语等),我称之为 "Standard.rc"。另一个叫做 "Alternate.rc",包含所有其他语言,如韩语、泰语等。
编译时开关决定哪个文件链接到 EXE:
{$IFDEF ALT_LANG}
{$R 'source\Alternate.res'}
{$ELSE}
{$R 'source\Standard.res'}
{$ENDIF}
这是背景,现在进入问题!
在给定 EXE 的路径并使用类似 Windows GetFileVersionInfo
方法的情况下,是否可以确定 EXE 中有哪些 STRINGTABLE 资源?如果可以确定是否:
Language LANG_KOREAN, SUBLANG_KOREAN
或
Language LANG_CZECH, SUBLANG_DEFAULT
被包含,那么EXE可以被识别为"standard"或"alternate"。
目前,在没有实际执行 EXE 的情况下,唯一的区别是以字节为单位的大小,这是一种不可靠的启发式方法。 C++ 或 C# 都可以。我可以适应 Delphi 或编写一个单独的实用程序,该实用程序从 Delphi.
调用
更新
根据 LU RD 的评论,我创建了以下 Version.rc 文件:
// Version information resource file
VS_VERSION_INFO VERSIONINFO
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "InternalName", "Standard"
END
END
END
然后将其编译(使用 Microsoft 资源编译器)到 Version.res 并使用 {$R 'source\Version.res'}
链接到应用程序中。这按预期编译,但当我尝试从 EXE 读取 InternalName
字段时,它是空白的。如果我在项目属性中手动设置InternalName
为"Test",那么设置为"Test"。我做错了什么以及如何覆盖在项目属性中手动输入的内容?
注意:这不是如何确定特定 STRINGTABLE 资源是否包含在最终 EXE 中的问题的答案,而是解决所提出问题的替代方法。
本题部分基于此answer。
第 1 步:输入您的项目属性和 Version Info
选项卡。如果设置了版本信息(如版本号或CompanyName
),将相关信息全部复制下来。现在关闭 Include version information in project
.
第 2 步:创建 Version.rc 和 Version_Alt.rc 文件。您的确切版本可能不同,但这是一个很好的起始模板:
// Version information resource file
#include "windows.h"
#include "VersionCommon.txt"
1 VERSIONINFO
FILEVERSION VER_NUM_MAJOR, VER_NUM_MINOR, VER_NUM_RELEASE, VER_NUM_BUILD
FILEOS VER_FILEOS
FILETYPE VFT_APP
{
BLOCK "VarFileInfo"
{
VALUE "Translation", TRANSLATION_LANG_ID, TRANSLATION_CHARSET
}
BLOCK "StringFileInfo"
{
// Note: The block address is a concatenation of the locale ID and code page. This would be "040904E4" for English (US).
BLOCK "080904E4"
{
VALUE "CompanyName", STRING_COMPANY_NAME
VALUE "FileDescription", STRING_FILE_DESCRIPTION
VALUE "InternalName", STRING_INTERNAL_NAME_STD
VALUE "LegalCopyright", STRING_LEGAL_COPYRIGHT
VALUE "LegalTrademarks", STRING_LEGAL_TRADEMARKS
VALUE "ProductName", STRING_PRODUCT_NAME
VALUE "ProductVersion", STRING_PRODUCT_VERSION
VALUE "Comments", STRING_COMMENTS
VALUE "Homepage", STRING_HOME_PAGE
}
}
}
这是针对 Version.rc 文件的。对于 Version_Alt.rc,InternalName
将是 STRING_INTERNAL_NAME_ALT,否则其他一切都是一样的。
第 3 步:创建 VersionCommon.txt 可能如下所示:
// Common version information (between standard and alternate language)
// Version info
#define VER_NUM_MAJOR 1
#define VER_NUM_MINOR 2
#define VER_NUM_RELEASE 3
#define VER_NUM_BUILD 4
#define VER_FILEOS 0x00000004L // 32-bit Windows
// Translation info
#define TRANSLATION_LANG_ID 0x0809 // Locale: English (UK)
#define TRANSLATION_CHARSET 0x04E4 // Code Page: 1252
// String file info
#define STRING_COMPANY_NAME "YOUR-COMPANY[=11=]"
#define STRING_FILE_DESCRIPTION "Software to do amazing things[=11=]"
#define STRING_INTERNAL_NAME_STD "Standard[=11=]" // ALT_LANG not defined
#define STRING_INTERNAL_NAME_ALT "Alternate[=11=]" // ALT_LANG is defined
#define STRING_LEGAL_COPYRIGHT "Copyright (C) YOUR-COMPANY[=11=]"
#define STRING_LEGAL_TRADEMARKS "LEGALISE STATEMENT?[=11=]"
#define STRING_PRODUCT_NAME "Groovy[=11=]"
#define STRING_PRODUCT_VERSION "SOME-VERSION-INFO[=11=]"
#define STRING_COMMENTS "SOME-COMMENTS[=11=]"
#define STRING_HOME_PAGE "OPTIONAL-YOUR-WEBSITE[=11=]"
第 4 步:编写一个编译资源脚本的批处理文件。请注意,更高版本的 Delphi 可以配置为为您编译资源。批处理文件可能如下所示:
@echo off
rem Version.rc and Version_Alt.rc are used to create version information that is linked into
rem the main Delphi application. "InternalName" is used to indicate whether ALT_LANG is defined.
echo Setting the program path (change this if your path is different)
set SOURCE_PATH=<PATH-TO-FOLDER-CONTAINING-RC-FILES>
echo .
echo Use Visual Studio tools to generate the version .RES files
@echo on
cd <PATH-TO-VISUAL-STUDIO-BIN-FOLDER-CONTAINING-RC.EXE>
call vcvars32
rc /r %SOURCE_PATH%\Version.rc
rc /r %SOURCE_PATH%\Version_Alt.rc
echo .
@echo off
echo .
rem pause <- uncomment this to debug errors
exit
第 5 步:在文本编辑器中打开您的 Delphi 项目 (.dpr),并将这些 .RES 文件 link 放入最终的 EXE 中:
{$IFDEF ALT_LANG}
{$R 'source\Strings_Alt.res'}
{$R 'source\Version\Version_Alt.res'}
{$ELSE}
{$R 'source\Strings.res'}
{$R 'source\Version\Version.res'}
{$ENDIF}
第 6 步:现在您的文件中已包含版本信息,只需阅读 InternalName
(即 "Standard" 或 "Alternate")。这可以按如下方式完成:
strLanguage := GetSpecificFileVersionInfo('YOUR-EXE.exe', 'InternalName');
GetSpecificFileVersionInfo
的代码是:
function GetSpecificFileVersionInfo(szFile: PChar; strInfo: String) : String;
var
pstrBuffer: PChar;
dwSize, dwLength: DWORD;
pVersion: pointer;
strKey: String;
begin
// Return the specified file version information
// Adapted from: http://www.swissdelphicenter.com/en/showcode.php?id=1047
// Typical values in the VERSIONINFO resource of the application include:
// * CompanyName
// * FileDescription
// * InternalName
// * LegalCopyright
// * LegalTrademarks
// * ProductName
// * ProductVersion
// * Comments
// Additional fields may be available if the version information has been updated
Result := '';
dwSize := GetFileVersionInfoSize(szFile, dwSize);
if (dwSize > 0) then
begin
pstrBuffer := AllocMem(dwSize);
try
if ( (GetFileVersionInfo(szFile, 0, dwSize, pstrBuffer)) and
(VerQueryValue(pstrBuffer, '\VarFileInfo\Translation', pVersion, dwLength))) then
begin
strKey := Format('\StringFileInfo\%.4x%.4x\%s', [
LoWord(Integer(pVersion^)),
HiWord(Integer(pVersion^)), strInfo]);
if (VerQueryValue(pstrBuffer, PChar(strKey), pVersion, dwLength)) then
Result := StrPas(pVersion);
end;
finally
FreeMem(pstrBuffer, dwSize);
end;
end;
end;
工作完成。现在你已经解决了两个问题。第一个是您的原始版本,用于确定 EXE 的编译方式(实际上没有 运行 EXE)。第二个是您可能没有意识到的一个,您手动输入的版本信息容易出错...现在您包含了更多的自动化版本信息。
我的 Delphi 应用程序使用 Microsoft 资源编译器 (rc.exe) 编译字符串列表(.rc
文件中的文本格式),如下所示:
Language LANG_KOREAN, SUBLANG_KOREAN
STRINGTABLE
BEGIN
cszLanguageName "Korean"
<etc>
END
到 .res
文件中。所有表都包含相同的 ID(例如 cszLanguageName
)。我维护两个单独的资源字符串文件。其中一个主要包含欧洲语言(英语、捷克语等),我称之为 "Standard.rc"。另一个叫做 "Alternate.rc",包含所有其他语言,如韩语、泰语等。
编译时开关决定哪个文件链接到 EXE:
{$IFDEF ALT_LANG}
{$R 'source\Alternate.res'}
{$ELSE}
{$R 'source\Standard.res'}
{$ENDIF}
这是背景,现在进入问题!
在给定 EXE 的路径并使用类似 Windows GetFileVersionInfo
方法的情况下,是否可以确定 EXE 中有哪些 STRINGTABLE 资源?如果可以确定是否:
Language LANG_KOREAN, SUBLANG_KOREAN
或
Language LANG_CZECH, SUBLANG_DEFAULT
被包含,那么EXE可以被识别为"standard"或"alternate"。
目前,在没有实际执行 EXE 的情况下,唯一的区别是以字节为单位的大小,这是一种不可靠的启发式方法。 C++ 或 C# 都可以。我可以适应 Delphi 或编写一个单独的实用程序,该实用程序从 Delphi.
调用更新
根据 LU RD 的评论,我创建了以下 Version.rc 文件:
// Version information resource file
VS_VERSION_INFO VERSIONINFO
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "InternalName", "Standard"
END
END
END
然后将其编译(使用 Microsoft 资源编译器)到 Version.res 并使用 {$R 'source\Version.res'}
链接到应用程序中。这按预期编译,但当我尝试从 EXE 读取 InternalName
字段时,它是空白的。如果我在项目属性中手动设置InternalName
为"Test",那么设置为"Test"。我做错了什么以及如何覆盖在项目属性中手动输入的内容?
注意:这不是如何确定特定 STRINGTABLE 资源是否包含在最终 EXE 中的问题的答案,而是解决所提出问题的替代方法。
本题部分基于此answer。
第 1 步:输入您的项目属性和 Version Info
选项卡。如果设置了版本信息(如版本号或CompanyName
),将相关信息全部复制下来。现在关闭 Include version information in project
.
第 2 步:创建 Version.rc 和 Version_Alt.rc 文件。您的确切版本可能不同,但这是一个很好的起始模板:
// Version information resource file
#include "windows.h"
#include "VersionCommon.txt"
1 VERSIONINFO
FILEVERSION VER_NUM_MAJOR, VER_NUM_MINOR, VER_NUM_RELEASE, VER_NUM_BUILD
FILEOS VER_FILEOS
FILETYPE VFT_APP
{
BLOCK "VarFileInfo"
{
VALUE "Translation", TRANSLATION_LANG_ID, TRANSLATION_CHARSET
}
BLOCK "StringFileInfo"
{
// Note: The block address is a concatenation of the locale ID and code page. This would be "040904E4" for English (US).
BLOCK "080904E4"
{
VALUE "CompanyName", STRING_COMPANY_NAME
VALUE "FileDescription", STRING_FILE_DESCRIPTION
VALUE "InternalName", STRING_INTERNAL_NAME_STD
VALUE "LegalCopyright", STRING_LEGAL_COPYRIGHT
VALUE "LegalTrademarks", STRING_LEGAL_TRADEMARKS
VALUE "ProductName", STRING_PRODUCT_NAME
VALUE "ProductVersion", STRING_PRODUCT_VERSION
VALUE "Comments", STRING_COMMENTS
VALUE "Homepage", STRING_HOME_PAGE
}
}
}
这是针对 Version.rc 文件的。对于 Version_Alt.rc,InternalName
将是 STRING_INTERNAL_NAME_ALT,否则其他一切都是一样的。
第 3 步:创建 VersionCommon.txt 可能如下所示:
// Common version information (between standard and alternate language)
// Version info
#define VER_NUM_MAJOR 1
#define VER_NUM_MINOR 2
#define VER_NUM_RELEASE 3
#define VER_NUM_BUILD 4
#define VER_FILEOS 0x00000004L // 32-bit Windows
// Translation info
#define TRANSLATION_LANG_ID 0x0809 // Locale: English (UK)
#define TRANSLATION_CHARSET 0x04E4 // Code Page: 1252
// String file info
#define STRING_COMPANY_NAME "YOUR-COMPANY[=11=]"
#define STRING_FILE_DESCRIPTION "Software to do amazing things[=11=]"
#define STRING_INTERNAL_NAME_STD "Standard[=11=]" // ALT_LANG not defined
#define STRING_INTERNAL_NAME_ALT "Alternate[=11=]" // ALT_LANG is defined
#define STRING_LEGAL_COPYRIGHT "Copyright (C) YOUR-COMPANY[=11=]"
#define STRING_LEGAL_TRADEMARKS "LEGALISE STATEMENT?[=11=]"
#define STRING_PRODUCT_NAME "Groovy[=11=]"
#define STRING_PRODUCT_VERSION "SOME-VERSION-INFO[=11=]"
#define STRING_COMMENTS "SOME-COMMENTS[=11=]"
#define STRING_HOME_PAGE "OPTIONAL-YOUR-WEBSITE[=11=]"
第 4 步:编写一个编译资源脚本的批处理文件。请注意,更高版本的 Delphi 可以配置为为您编译资源。批处理文件可能如下所示:
@echo off
rem Version.rc and Version_Alt.rc are used to create version information that is linked into
rem the main Delphi application. "InternalName" is used to indicate whether ALT_LANG is defined.
echo Setting the program path (change this if your path is different)
set SOURCE_PATH=<PATH-TO-FOLDER-CONTAINING-RC-FILES>
echo .
echo Use Visual Studio tools to generate the version .RES files
@echo on
cd <PATH-TO-VISUAL-STUDIO-BIN-FOLDER-CONTAINING-RC.EXE>
call vcvars32
rc /r %SOURCE_PATH%\Version.rc
rc /r %SOURCE_PATH%\Version_Alt.rc
echo .
@echo off
echo .
rem pause <- uncomment this to debug errors
exit
第 5 步:在文本编辑器中打开您的 Delphi 项目 (.dpr),并将这些 .RES 文件 link 放入最终的 EXE 中:
{$IFDEF ALT_LANG}
{$R 'source\Strings_Alt.res'}
{$R 'source\Version\Version_Alt.res'}
{$ELSE}
{$R 'source\Strings.res'}
{$R 'source\Version\Version.res'}
{$ENDIF}
第 6 步:现在您的文件中已包含版本信息,只需阅读 InternalName
(即 "Standard" 或 "Alternate")。这可以按如下方式完成:
strLanguage := GetSpecificFileVersionInfo('YOUR-EXE.exe', 'InternalName');
GetSpecificFileVersionInfo
的代码是:
function GetSpecificFileVersionInfo(szFile: PChar; strInfo: String) : String;
var
pstrBuffer: PChar;
dwSize, dwLength: DWORD;
pVersion: pointer;
strKey: String;
begin
// Return the specified file version information
// Adapted from: http://www.swissdelphicenter.com/en/showcode.php?id=1047
// Typical values in the VERSIONINFO resource of the application include:
// * CompanyName
// * FileDescription
// * InternalName
// * LegalCopyright
// * LegalTrademarks
// * ProductName
// * ProductVersion
// * Comments
// Additional fields may be available if the version information has been updated
Result := '';
dwSize := GetFileVersionInfoSize(szFile, dwSize);
if (dwSize > 0) then
begin
pstrBuffer := AllocMem(dwSize);
try
if ( (GetFileVersionInfo(szFile, 0, dwSize, pstrBuffer)) and
(VerQueryValue(pstrBuffer, '\VarFileInfo\Translation', pVersion, dwLength))) then
begin
strKey := Format('\StringFileInfo\%.4x%.4x\%s', [
LoWord(Integer(pVersion^)),
HiWord(Integer(pVersion^)), strInfo]);
if (VerQueryValue(pstrBuffer, PChar(strKey), pVersion, dwLength)) then
Result := StrPas(pVersion);
end;
finally
FreeMem(pstrBuffer, dwSize);
end;
end;
end;
工作完成。现在你已经解决了两个问题。第一个是您的原始版本,用于确定 EXE 的编译方式(实际上没有 运行 EXE)。第二个是您可能没有意识到的一个,您手动输入的版本信息容易出错...现在您包含了更多的自动化版本信息。