Определите, какой STRINGTABLE включен в EXE

Приложение My Delphi использует компилятор ресурсов Microsoft (rc.exe) для составления списка строк (текстовый формат в .rc файл) вот так:

Language LANG_KOREAN, SUBLANG_KOREAN
STRINGTABLE
BEGIN
    cszLanguageName "Korean"
    <etc>
END

в .res файл. Все таблицы содержат одинаковые идентификаторы (например, cszLanguageName). Я поддерживаю два отдельных файла строки ресурса. Один содержит в основном европейские языки (английский, чешский и т. Д.), Которые я называю "Standard.rc". Другой называется Alternate.rc и содержит все другие языки, такие как корейский, тайский и т. Д.

Переключатель времени компиляции определяет, какой файл связан с EXE:

{$IFDEF ALT_LANG}
    {$R 'source\Alternate.res'}
{$ELSE}
    {$R 'source\Standard.res'}
{$ENDIF}

Это был фон, теперь к вопросу!

Можно ли определить, учитывая путь к EXE и используя что-то вроде Windows GetFileVersionInfo метод, какие STRINGTABLE ресурсы находятся в EXE? Если бы было возможно определить:

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 Resource Compiler) в Version.res и связан с приложением, используя {$R 'source\Version.res'}, Это компилируется, как и ожидалось, но когда я пытаюсь прочитать InternalName поле из EXE пустое. Если я вручную установить InternalName в свойствах проекта "Тест", затем он устанавливается как "Тест". Что я делаю не так и как мне переписать то, что было введено вручную в свойствах проекта?

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. InternalName будет иметь значение STRING_INTERNAL_NAME_ALT для Version_Alt.rc, в остальном все остальное будет таким же.

Шаг 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). Второе - это то, что вы, вероятно, не понимали, что введенная вами информация о версии была подвержена ошибкам... теперь вы включили более автоматизированную информацию о версии.

Другие вопросы по тегам