如何将给定语言的 STRING TABLE 资源读入地图
How to read STRING TABLE resources for given language into map
我在存储在 STRINGTABLE
中的 vs 资源 .rc
文件中翻译了多种语言的字符串
//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
文件中有多少资源,我也不知道资源 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 的资源句柄,但从这里我不清楚如何获得
- 给定资源句柄的可用资源 ID 列表
- 实际得到一个资源的值
锁定资源后 wchar_t *data
中似乎有一些东西,但它似乎没有以任何方式为 STRINGTABLE
构建,也许它适用于二进制资源,但是 STRINGTABLE
格式似乎无法解析
我还尝试了什么
只是调用 LoadString,但为此
- 我不能指定语言(我不能弄乱系统或应用程序区域设置)
- 我不知道如何获取可用资源 ID
编辑:
添加了更多关于通过 LockResource
获取数据的调查
windows 应用中的多语言资源加载由
完成
- 使用 SetThreadLocale 设置语言环境 API。
- 调用 LoadResource(或更具体的 API,如 LoadMenu、LoadString)
要加载资源,应知道其 ID。如果要枚举所有资源,请使用 EnumResource* APIs。
按照@SimonMourier 提供的link,我编写了读取所有资源的代码。
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;
}
STRING TABLE 资源在此处进行了一些记录:https://devblogs.microsoft.com/oldnewthing/20040130-00/?p=40813
(PS: 提到的知识库文章 Q196774 似乎永远丢失了,到处都找不到...)
The strings listed in the *.rc file are grouped together in bundles of
sixteen. So the first bundle contains strings 0 through 15, the second
bundle contains strings 16 through 31, and so on. In general, bundle N
contains strings (N-1)*16 through (N-1)*16+15.
The strings in each bundle are stored as counted UNICODE strings, not
null-terminated strings. If there are gaps in the numbering, null
strings are used. So for example if your string table had only strings
16 and 31, there would be one bundle (number 2), which consists of
string 16, fourteen null strings, then string 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;
}
我在存储在 STRINGTABLE
.rc
文件中翻译了多种语言的字符串
//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
文件中有多少资源,我也不知道资源 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 的资源句柄,但从这里我不清楚如何获得
- 给定资源句柄的可用资源 ID 列表
- 实际得到一个资源的值
锁定资源后 wchar_t *data
中似乎有一些东西,但它似乎没有以任何方式为 STRINGTABLE
构建,也许它适用于二进制资源,但是 STRINGTABLE
格式似乎无法解析
我还尝试了什么
只是调用 LoadString,但为此
- 我不能指定语言(我不能弄乱系统或应用程序区域设置)
- 我不知道如何获取可用资源 ID
编辑: 添加了更多关于通过 LockResource
获取数据的调查windows 应用中的多语言资源加载由
完成- 使用 SetThreadLocale 设置语言环境 API。
- 调用 LoadResource(或更具体的 API,如 LoadMenu、LoadString)
要加载资源,应知道其 ID。如果要枚举所有资源,请使用 EnumResource* APIs。
按照@SimonMourier 提供的link,我编写了读取所有资源的代码。
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;
}
STRING TABLE 资源在此处进行了一些记录:https://devblogs.microsoft.com/oldnewthing/20040130-00/?p=40813
(PS: 提到的知识库文章 Q196774 似乎永远丢失了,到处都找不到...)
The strings listed in the *.rc file are grouped together in bundles of sixteen. So the first bundle contains strings 0 through 15, the second bundle contains strings 16 through 31, and so on. In general, bundle N contains strings (N-1)*16 through (N-1)*16+15.
The strings in each bundle are stored as counted UNICODE strings, not null-terminated strings. If there are gaps in the numbering, null strings are used. So for example if your string table had only strings 16 and 31, there would be one bundle (number 2), which consists of string 16, fourteen null strings, then string 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;
}