1

我的 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 标识为“标准”或“替代”。

目前,在没有实际执行 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 并使用 .res 链接到应用程序中{$R 'source\Version.res'}。这可以按预期编译,但是当我尝试InternalName从 EXE 中读取该字段时,它是空白的。如果我在项目属性中手动设置InternalName为“测试”,则将其设置为“测试”。我做错了什么,如何覆盖在项目属性中手动输入的内容?

4

1 回答 1

1

注意:这不是对如何确定特定 STRINGTABLE 资源是否包含在最终 EXE 中的问题的答案,而是解决所提出问题的替代方法。

这个问题部分基于这个答案

第 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 文件的。InternalNameVersion_Alt.rc 将是 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\0"
#define STRING_FILE_DESCRIPTION     "Software to do amazing things\0"
#define STRING_INTERNAL_NAME_STD    "Standard\0"    // ALT_LANG not defined
#define STRING_INTERNAL_NAME_ALT    "Alternate\0"   // ALT_LANG is defined
#define STRING_LEGAL_COPYRIGHT      "Copyright (C) YOUR-COMPANY\0"
#define STRING_LEGAL_TRADEMARKS     "LEGALISE STATEMENT?\0"
#define STRING_PRODUCT_NAME         "Groovy\0"
#define STRING_PRODUCT_VERSION      "SOME-VERSION-INFO\0"
#define STRING_COMMENTS             "SOME-COMMENTS\0"
#define STRING_HOME_PAGE            "OPTIONAL-YOUR-WEBSITE\0"

第 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 文件链接到最终的 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(将是“标准”或“替代”)。这可以按如下方式完成:

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)。第二个是您可能没有意识到您手动输入的版本信息容易出错……现在您已经包含了更多自动版本信息。

于 2018-02-07T16:14:40.910 回答