c++ 如何将给定语言的STRING TABLE资源读取到map中

vqlkdk9b  于 2023-05-24  发布在  其他
关注(0)|答案(3)|浏览(152)

我在vs资源.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:值),...],...]
std::map<long /*languageId*/, std::map<long /*resource id (1000, 7777, 1001)*/, std::string /*value*/>>
我不知道.rc文件中有多少资源,我也不知道资源ID(我希望有某种方法可以从文件中获取所有ID,但到目前为止我还没有找到这样做的方法)

我努力

通过回调循环访问资源。

#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.给定资源句柄的可用资源id列表
1.实际获取资源的值
在锁定资源后,wchar_t *data中似乎有一些东西,但它似乎没有以任何方式为STRINGTABLE进行结构化,也许它可以用于二进制资源,但STRINGTABLE似乎不可解析

我还尝试了什么

只是调用LoadString,但是
1.我不能指定语言(也不能乱用系统或应用程序区域设置)
1.我不知道如何获取可用资源ID
编辑:增加了更多的调查对通过LockResource获取数据

6vl6ewon

6vl6ewon1#

STRING TABLE资源在这里有一些记录:https://devblogs.microsoft.com/oldnewthing/20040130-00/?p=40813
(PS:这里有一个链接到上述知识库文章Q196774:https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/196774
在 *.rc文件中列出的字符串以十六个一组的形式组合在一起。因此,第一束包含字符串0到15,第二束包含字符串16到31,依此类推。通常,束N包含字符串(N-1)*16到(N-1)*16+15。
每个包中的字符串都存储为计数的UNICODE字符串,而不是以null结尾的字符串。如果编号中存在间隙,则使用空字符串。例如,如果你的字符串表只有字符串16和31,那么就会有一个bundle(2号),它由字符串16、14个null字符串和字符串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;
}
v440hwme

v440hwme2#

Windows应用程序中的多语言资源加载由
1.使用SetThreadLocale API设置区域设置。
1.调用LoadResource(或者更具体的API,如LoadMenu、LoadString)
要加载资源,应该知道其ID。如果要枚举所有资源,请使用EnumResource* API。

apeeds0o

apeeds0o3#

下面的链接由@SimonMourier提供我写的代码读取所有资源。
EnumResourceLanguagesCallback可能会被多次调用,即使是对于一个资源文件,它似乎每次调用都要解析大约33-34个资源,但它可能会因为某种原因注册一些资源两次。
此代码仍然无法获取资源ID。

#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;
}

相关问题