使用 Win32 API 列出存储在纯资源库 (DLL) 中的消息 ID 和符号名称

2021-12-22 00:00:00 dll winapi visual-c++ c++

我们想列出嵌入在纯资源库 (DLL) 中的消息的内容(键/值对)

资源库定义为中MSDN.

mc -s EventLogMsgs.mcrc EventLogMsgs.rc链接/DLL/SUBSYSTEM:WINDOWS/NOENTRY/MACHINE:x86 EventLogMsgs.Res

一个示例 EventLogMsgs.mc 可能是:

<代码>;//- 事件类别 -;//类别必须从 1 开始连续编号.;//************************************************************消息 ID=0x1严重性=成功SymbolicName=INSTALL_CATEGORY语言=英语安装.消息 ID=0x2严重性=成功SymbolicName=QUERY_CATEGORY语言=英语数据库查询....

我们尝试使用 EnumResourceTypes() Win32 API 如下:

<代码>...HMODULE hMod=NULL;hMod = LoadLibraryA("C:\temp\EventLogMsgs.dll");如果(hMod != NULL){EnumResourceTypes( hMod, (ENUMRESTYPEPROC)TypesCallback, 0) ;自由图书馆(hMod);}...BOOL WINAPI TypesCallback( HMODULE hModule, LPTSTR lpType, LONG lParam ){字符缓冲区[100];if ((ULONG)lpType & 0xFFFF0000)sprintf(缓冲区,%s
",lpType);别的sprintf(buffer, "%u
", (USHORT)lpType);cout<<类型:" <<缓冲<

结果是资源类型及其名称/标识符"的高级列表,例如,

<代码>...类型:11姓名:1类型:16姓名:1类型:24名称:2...

11 (RT_MESSAGETABLE) 是消息表资源类型 (查看所有资源类型)

理想情况下,我们希望在资源库中列出 ALL 实际消息的符号名称和标识符.

谢谢

解决方案

您已经枚举了模块中的资源,该模块告诉您特定类型的每个资源的名称.完成后,您需要加载资源以检查其内容.在您的情况下,您需要名为 1RT_MESSAGETABLE 类型的资源.

您现在需要使用FindResourceLoadResourceLockResource 来获取指向消息表结构开头的指针.然后您可以使用 MESSAGE_RESOURCE_DATA 结构体,依次使用 MESSAGE_RESOURCE_BLOCKMESSAGE_RESOURCE_ENTRY 来解压消息表的内容.这篇代码项目文章详细介绍了该过程.

这是一个枚举消息表的相当简单的 C 程序:

#include #include int ProcessBlock(MESSAGE_RESOURCE_DATA* 数据,MESSAGE_RESOURCE_BLOCK* 块){MESSAGE_RESOURCE_ENTRY* entry = (MESSAGE_RESOURCE_ENTRY*) ((unsigned char*)data + block->OffsetToEntries);for (DWORD id = block->LowId; id <= block->HighId; id++){if (entry->Flags == 0x0001)//宽字符printf("%d, %ls", id, entry->Text);else if (entry->Flags == 0x0000)//ANSIprintf("%d, %s", id, entry->Text);entry = (MESSAGE_RESOURCE_ENTRY*) ((unsigned char*)entry + entry->Length);}返回 1;}int main(void){HMODULE hMod = LoadLibrary("C:\desktop\EventLogMsgs.dll");如果(hMod == NULL)返回1;HRSRC hRsrc = FindResource(hMod, MAKEINTRESOURCE(1), RT_MESSAGETABLE);如果 (hRsrc == NULL) 返回 1;HGLOBAL hGlobal = LoadResource(hMod, hRsrc);如果(hGlobal == NULL)返回1;MESSAGE_RESOURCE_DATA* 数据 = (MESSAGE_RESOURCE_DATA*)LockResource(hGlobal);如果(数据== NULL)返回1;for (DWORD block = 0; block NumberOfBlocks; block++)if (!ProcessBlock(data, &data->Blocks[block]))返回 1;返回0;}

输出

<前>1、安装2、数据库查询3、数据刷新1000,我的应用程序消息文本,英文,消息 ID 1000,从 %1 调用.1002,我的英文通用信息消息,消息 ID 为 1002.1004, %%5002 的更新周期已完成.5001,示例事件日志5002, SVC_UPDATE.EXE-2147482647,我的应用程序消息文本,英文,消息 ID 1001,从 %1 调用.-2147482645,我的英语通用警告消息,消息 ID 1003,从 %1 调用.-2147482643, 由于无法建立到服务器 %1 的连接,刷新操作没有完成.

请原谅我可怕的 C.C 和 C++ 都不是我远程流利的语言.但是,代码至少会告诉你如何提取你想要的信息.

We would like to list the contents (key/value pairs) of messages embedded in a resource-only library (DLL)

The resource library is defined as specified in MSDN.

mc -s EventLogMsgs.mc
rc EventLogMsgs.rc
link /DLL /SUBSYSTEM:WINDOWS /NOENTRY /MACHINE:x86 EventLogMsgs.Res 

A sample EventLogMsgs.mc may be:

; // - Event categories -
; // Categories must be numbered consecutively starting at 1.
; // ********************************************************

MessageId=0x1
Severity=Success
SymbolicName=INSTALL_CATEGORY
Language=English
Installation
.

MessageId=0x2
Severity=Success
SymbolicName=QUERY_CATEGORY
Language=English
Database Query
.

...

We tried to use EnumResourceTypes() Win32 API as following:

...
HMODULE hMod=NULL;
hMod = LoadLibraryA( "C:\temp\EventLogMsgs.dll" ); 
if (hMod != NULL) 
{
    EnumResourceTypes( hMod, (ENUMRESTYPEPROC)TypesCallback, 0) ;
    FreeLibrary(hMod);
}
...

BOOL WINAPI TypesCallback( HMODULE hModule, LPTSTR lpType, LONG lParam )
{
    char buffer[100];
    if ((ULONG)lpType & 0xFFFF0000) 
        sprintf( buffer, "%s
", lpType); 
    else 
        sprintf(buffer, "%u
", (USHORT)lpType); 

    cout << "Type: " << buffer << std::endl;

    EnumResourceNames( hModule, lpType, (ENUMRESNAMEPROC)NamesCallback, 0 );
    return true;
}

BOOL WINAPI NamesCallback( HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam )
{
    char buffer[100];
    if ((ULONG)lpName & 0xFFFF0000) 
        sprintf(buffer,"%s
", lpName); 
    else 
        sprintf(buffer, "%u
",(USHORT)lpName); 
    cout << "Name: " << buffer << std::endl;
    return true;
}

The result is a high level listing of the resource types and their "names/identifiers", e.g.,

...
Type: 11
Name: 1

Type: 16
Name: 1

Type: 24
Name: 2
...

11 (RT_MESSAGETABLE) is the message table resource type (See all resource types)

Ideally we would want to list ALL the actual messages' symbolic names and identifiers in the resource library.

Thanks

解决方案

You've enumerated the resources in the module which tells you the name of each resource of a specific type. Once you've done that you need to load the resource to examine its content. In your case you need the resource of type RT_MESSAGETABLE that is named 1.

You now need to use FindResource, LoadResource and LockResource to get a pointer to the beginning of the message table structures. You can then use the MESSAGE_RESOURCE_DATA struct , and in turn MESSAGE_RESOURCE_BLOCK and MESSAGE_RESOURCE_ENTRY to unpack the content of the message table. This Code Project article goes into more detail on the process.

Here's a rather naff C program that enumerates your message table:

#include <windows.h>
#include <stdio.h>

int ProcessBlock(MESSAGE_RESOURCE_DATA* data, MESSAGE_RESOURCE_BLOCK* block)
{
    MESSAGE_RESOURCE_ENTRY* entry = (MESSAGE_RESOURCE_ENTRY*) ((unsigned char*)data + block->OffsetToEntries);
    for (DWORD id = block->LowId; id <= block->HighId; id++)
    {
        if (entry->Flags == 0x0001) // wide char
            printf("%d, %ls", id, entry->Text);
        else if (entry->Flags == 0x0000) // ANSI
            printf("%d, %s", id, entry->Text);
        entry = (MESSAGE_RESOURCE_ENTRY*) ((unsigned char*)entry + entry->Length);
    }
    return 1;
}

int main(void)
{
    HMODULE hMod = LoadLibrary("C:\desktop\EventLogMsgs.dll"); 
    if (hMod == NULL) return 1;

    HRSRC hRsrc = FindResource(hMod, MAKEINTRESOURCE(1), RT_MESSAGETABLE);
    if (hRsrc == NULL) return 1;

    HGLOBAL hGlobal = LoadResource(hMod, hRsrc);
    if (hGlobal == NULL) return 1;

    MESSAGE_RESOURCE_DATA* data = (MESSAGE_RESOURCE_DATA*)LockResource(hGlobal);
    if (data == NULL) return 1;

    for (DWORD block = 0; block < data->NumberOfBlocks; block++)
        if (!ProcessBlock(data, &data->Blocks[block]))
            return 1;

    return 0;
}

Output

1, Installation
2, Database Query
3, Data Refresh
1000, My application message text, in English, for message id 1000, called from %1.
1002, My generic information message in English, for message id 1002.
1004, The update cycle is complete for %%5002.
5001, Sample Event Log
5002, SVC_UPDATE.EXE
-2147482647, My application message text, in English, for message id 1001, called from %1.
-2147482645, My generic warning message in English, for message id 1003, called from %1.
-2147482643, The refresh operation did not complete because the connection to server %1 could not be established.

Please excuse my appalling C. Neither C nor C++ are languages that I am remotely fluent in. However, the code will at least show you how to extract the information you desire.

相关文章