Как читать STRING TABLE ресурсы для данного языка на карте - PullRequest
2 голосов
/ 06 марта 2020

Я перевел строки для нескольких языков в зависимости от ресурса .rc файлы, хранящиеся в STRINGTABLE

//english.rc

#pragma code_page(65001)
#define APSTUDIO_READONLY_SYMBOLS
#include "afxres.h"
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL

STRINGTABLE
BEGIN
    1000                    "SOME TEXT"
    7777                    "SOME TEXT"
END
#endif

//danish.rc
#pragma code_page(65001)
#define APSTUDIO_READONLY_SYMBOLS
#include "afxres.h"
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_DANISH, SUBLANG_NEUTRAL

STRINGTABLE
BEGIN
    1001                    "SOME TEXT IN DANISH"
    7777                    "SOME TEXT IN DANISH"
END
#endif

Я хочу загрузить эти ресурсы на карту [languageId: [(resourceId: value) , ...], ...]

std::map<long /*languageId*/, std::map<long /*resource id (1000, 7777, 1001)*/, std::string /*value*/>>

Я не знаю, сколько ресурсов содержится в файлах .rc, я также не знаю идентификаторов ресурсов (ожидаю есть какой-то способ получить все идентификаторы из файла, но я до сих пор не нашел способ сделать это)

Что я пытался

Итерация по ресурсам через обратный звонок.

#include <iostream>
#include <windows.h>
#include <map>

BOOL EnumResourceLanguagesCallback(HMODULE applicationModule, LPCTSTR resourceType, LPCTSTR resourceName, WORD languageId, std::map<long /*languageId*/, std::map<long /*resource id (e.g. 1000, 7777, 1001)*/, std::string /*resource value*/>> * pResources)
{
    HRSRC hResInfo = FindResourceEx(applicationModule, resourceType, resourceName, languageId);
    HGLOBAL hData = LoadResource(0, hResInfo);

    std::wstring values;
    values.assign((wchar_t*)LockResource(hData), SizeofResource(applicationModule, hResInfo));
    /* values string is either empty or contains garbage or sometimes partial string with some of the resources in the rc file - depends on the amount of resources in rc file*/
    return TRUE;
}


BOOL EnumResourceNamesCallback(HMODULE applicationModule, LPCTSTR resourceType, LPTSTR resourceName, std::map<long /*languageId*/, std::map<long /*resource id (e.g. 1000, 7777, 1001)*/, std::string /*resource value*/>> *pResources)
{
    return EnumResourceLanguages(applicationModule, resourceType, resourceName, (ENUMRESLANGPROC)EnumResourceLanguagesCallback, reinterpret_cast<LONG_PTR>(pResources));
}

int main()
{
    std::map<long /*languageId*/, std::map<long /*resource id (e.g. 1000, 7777, 1001)*/, std::string /*resource value*/>> resources;

    EnumResourceNames(::GetModuleHandleA(nullptr), RT_STRING, (ENUMRESNAMEPROC)EnumResourceNamesCallback, reinterpret_cast<LONG_PTR>(&resources));
}

Это дало мне все дескрипторы ресурсов с возможностью вызова LoadResource & LockResource, но здесь мне неясно, как получить

  1. Список доступных идентификаторов ресурсов для данный дескриптор ресурса
  2. Фактически получить значение ресурса

Кажется, что-то есть в wchar_t *data после блокировки ресурса, но, похоже, он не структурирован в в любом случае для STRINGTABLE, может быть, это будет работать для двоичного ресурса, но формат STRINGTABLE не кажется доступным для анализа

Что еще я пробовал

Просто вызывая LoadString, но для этого

  1. я не могу указать язык (и не могу связываться с языком системы или приложения)
  2. Я не знаю, как получить доступные идентификаторы ресурсов

РЕДАКТИРОВАТЬ: Добавлено больше исследований для получения данных через LockResource

Ответы [ 3 ]

1 голос
/ 16 марта 2020

STRING TABLE ресурсы здесь несколько документированы: https://devblogs.microsoft.com/oldnewthing/20040130-00/?p=40813

(PS: упомянутая статья КБ Q196774 кажется потерянной на вечность, нигде не может ее найти ...)

Строки, перечисленные в файле * .r c, сгруппированы в пакеты по шестнадцать. Таким образом, первый пакет содержит строки с 0 по 15, второй пакет содержит строки с 16 по 31 и так далее. Как правило, пакет N содержит строки (N-1) * 16 - (N-1) * 16 + 15.

Строки в каждом пакете хранятся как подсчитанные строки UNICODE, а не строки с нулевым символом в конце. Если в нумерации есть пробелы, используются пустые строки. Так, например, если в вашей строковой таблице есть только строки 16 и 31, то будет один пакет (номер 2), который состоит из строки 16, четырнадцати нулевых строк, а затем строки 31.

Вот некоторые Пример кода, который выводит все строковые ресурсы из данного файла:

void DumpStringTable(LPCWSTR filePath)
{
    HMODULE hModule = LoadLibraryEx(filePath, nullptr, LOAD_LIBRARY_AS_DATAFILE);
    if (!hModule)
    {
        wprintf(L"LoadLibraryEx failed err: %u\n", GetLastError());
        return;
    }

    if (!EnumResourceTypesEx(hModule, EnumResType, 0, 0, 0))
    {
        wprintf(L"EnumResourceTypesEx failed err: %u\n", GetLastError());
        return;
    }

    FreeLibrary(hModule);
}

BOOL EnumresLang(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage, LONG_PTR lParam)
{
    if (lpType == RT_STRING)
    {
        const HRSRC res = FindResourceEx(hModule, lpType, lpName, wLanguage);
        if (!res)
        {
            wprintf(L"FindResourceEx failed err: %u\n", GetLastError());
            return FALSE;
        }

        const DWORD size = SizeofResource(hModule, res);
        if (!size)
        {
            wprintf(L"SizeofResource failed err: %u\n", GetLastError());
            return FALSE;
        }

        HGLOBAL hMem = LoadResource(hModule, res);
        if (!hMem)
        {
            wprintf(L"LoadResource failed err: %u\n", GetLastError());
            return FALSE;
        }

        LPWORD data = (LPWORD)LockResource(hMem);
        if (!data)
        {
            wprintf(L"LockResource failed err: %u\n", GetLastError());
            return FALSE;
        }

        const WORD nameInt = (WORD)(((ULONG_PTR)lpName) & 0xFFFF);
        for (WORD i = 0; i < 16; i++)
        {
            const WORD len = *data;
            data++;
            if (len)
            {
                const WORD id = (nameInt - 1) * 16 + i;
                std::wstring str;
                str.append((const wchar_t*)data, len);
                data += len;
                wprintf(L"id:%u: %s\n", id, str.c_str());
            }
        }

        return TRUE;
    }
    return TRUE;
}

BOOL EnumResName(HMODULE hModule, LPCWSTR lpType, LPWSTR lpName, LONG_PTR lParam)
{
    if (!EnumResourceLanguagesEx(hModule, lpType, lpName, EnumresLang, 0, 0, 0))
    {
        wprintf(L"EnumResourceLanguagesEx failed err: %u\n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

BOOL EnumResType(HMODULE hModule, LPWSTR lpType, LONG_PTR lParam)
{
    if (!EnumResourceNamesEx(hModule, lpType, EnumResName, 0, 0, 0))
    {
        wprintf(L"EnumResourceNamesEx failed err: %u\n", GetLastError());
        return FALSE;
    }

    return TRUE;
}
0 голосов
/ 16 марта 2020

По ссылке, предоставленной @SimonMourier, я написал код, который читает все ресурсы.

EnumResourceLanguagesCallback может вызываться несколько раз даже для одного файла ресурсов, кажется, что он анализирует около 33-34 ресурсов на вызов, но по некоторым причинам может регистрировать некоторые ресурсы дважды.

Этот код все еще не может получить идентификатор ресурса.

#include <windows.h>
#include <map>
#include <iostream>

BOOL EnumResourceLanguagesCallback(HMODULE applicationModule, LPCTSTR resourceType, LPCTSTR resourceName, WORD languageId, std::map<long /*languageId*/, std::map<unsigned long /*resource id (e.g. 1000, 7777, 1001)*/, std::wstring /*resource value*/>> *pResources)
{
    HRSRC hResInfo = FindResourceEx(applicationModule, resourceType, resourceName, languageId);
    HGLOBAL hData = LoadResource(applicationModule, hResInfo);

    if (NULL != hData)
    {
        DWORD resourceSize = SizeofResource(applicationModule, hResInfo);
        wchar_t* data = (wchar_t*)LockResource(hData);

        size_t offset = 0;
        while (offset < resourceSize) {

            const wchar_t *resourceValue = data + offset;
            if (resourceValue != nullptr)
            {
                const size_t dataSize = static_cast<size_t>(*resourceValue);
                if (dataSize > 0 && (offset + dataSize) < resourceSize) {
                    std::wstring value;
                    value.assign(resourceValue + 1, dataSize);

                    // TODO how to get resource id ???
                    long id = (*pResources)[languageId].size();

                    (*pResources)[languageId][id] = value;
                }
                offset += dataSize;
            }

            offset++;
        }

        UnlockResource(data);
    }

    return TRUE;
}


BOOL EnumResourceNamesCallback(HMODULE applicationModule, LPCTSTR resourceType, LPTSTR resourceName, std::map<unsigned long /*languageId*/, std::map<long /*resource id (e.g. 1000, 7777, 1001)*/, std::wstring /*resource value*/>> *pResources)
{
    return EnumResourceLanguages(applicationModule, resourceType, resourceName, (ENUMRESLANGPROC)EnumResourceLanguagesCallback, reinterpret_cast<LONG_PTR>(pResources));
}

int main()
{
    std::map<unsigned long /*languageId*/, std::map<long /*resource id (e.g. 1000, 7777, 1001)*/, std::wstring /*resource value*/>> resources;

    EnumResourceNames(::GetModuleHandleA(nullptr), RT_STRING, (ENUMRESNAMEPROC)EnumResourceNamesCallback, reinterpret_cast<LONG_PTR>(&resources));

    return 0;
}
0 голосов
/ 12 марта 2020

Загрузка многоязычных ресурсов в приложениях windows выполняется с помощью

  1. Установка языка с помощью API SetThreadLocale.
  2. Вызовите LoadResource (или более конкретно c API, такие как LoadMenu, LoadString)

Чтобы загрузить ресурс, его идентификатор должен быть известен. Если вы хотите перечислить все ресурсы, используйте API EnumResource *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...