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

ale*_*exg 2 c++ dll winapi visual-c++

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

资源库的定义如MSDN 中所指定。

mc -s EventLogMsgs.mc
rc EventLogMsgs.rc
link /DLL /SUBSYSTEM:WINDOWS /NOENTRY /MACHINE:x86 EventLogMsgs.Res 
Run Code Online (Sandbox Code Playgroud)

示例 EventLogMsgs.mc 可能是:

; // - 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
.

...
Run Code Online (Sandbox Code Playgroud)

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

...
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\n", lpType); 
    else 
        sprintf(buffer, "%u\n", (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\n", lpName); 
    else 
        sprintf(buffer, "%u\n",(USHORT)lpName); 
    cout << "Name: " << buffer << std::endl;
    return true;
}
Run Code Online (Sandbox Code Playgroud)

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

...
Type: 11
Name: 1

Type: 16
Name: 1

Type: 24
Name: 2
...
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

Dav*_*nan 5

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

您现在需要使用FindResource,LoadResourceLockResource来获取指向消息表结构开头的指针。然后,您可以使用MESSAGE_RESOURCE_DATAstruct 、 、 和MESSAGE_RESOURCE_BLOCKMESSAGE_RESOURCE_ENTRY解压消息表的内容。这篇代码项目文章更详细地介绍了该过程。

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出

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

请原谅我糟糕的 C。C 和 C++ 都不是我能熟练使用的语言。但是,代码至少会告诉你如何提取你想要的信息。