Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
350 views
in Technique[技术] by (71.8m points)

c++ - Listing message IDs and symbolic names stored in a resource-only library (DLL) using Win32 API

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...