编组消息表资源

问题描述 投票:0回答:2

我需要读取 C# 中的消息表资源。

我基本上试图将这个问题的答案中的代码移植到C#:Listing message IDs and symbolic namestored in a Resource-only library (DLL) using Win32 API

我的问题是我无法正确编组

MESSAGE_RESOURCE_DATA
MESSAGE_RESOURCE_BLOCK
结构,它们的定义如下(在
<winnt.h>
中):

typedef struct _MESSAGE_RESOURCE_BLOCK {
    DWORD LowId;
    DWORD HighId;
    DWORD OffsetToEntries;
} MESSAGE_RESOURCE_BLOCK, *PMESSAGE_RESOURCE_BLOCK;

typedef struct _MESSAGE_RESOURCE_DATA {
    DWORD NumberOfBlocks;
    MESSAGE_RESOURCE_BLOCK Blocks[ 1 ];
} MESSAGE_RESOURCE_DATA, *PMESSAGE_RESOURCE_DATA;

MESSAGE_RESOURCE_DATA
中,
NumberOfBlocks
MESSAGE_RESOURCE_BLOCK
数组中
Blocks
条目的数量(即使它被声明为具有单个元素的数组)。

由于我在编译时不知道数组大小,因此我尝试封送将

Blocks
声明为指针的结构,然后像这样使用
Marshal.PtrToStructure

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
struct MESSAGE_RESOURCE_BLOCK {
    public IntPtr LowId;
    public IntPtr HighId;
    public IntPtr OffsetToEntries;
}

[StructLayout(LayoutKind.Sequential)]
struct MESSAGE_RESOURCE_DATA {
    public IntPtr NumberOfBlocks;
    public IntPtr Blocks;
}

class Program {

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr LoadLibrary(string fileName);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr FindResource(IntPtr hModule, int lpID, int lpType);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

    [DllImport("kernel32.dll")]
    public static extern IntPtr LockResource(IntPtr hResData);

    static void Main(string[] args) {
        const int RT_MESSAGETABLE = 11;
        IntPtr hModule = LoadLibrary(@"C:\WINDOWS\system32\msobjs.dll");
        IntPtr msgTableInfo = FindResource(hModule, 1, RT_MESSAGETABLE);
        IntPtr msgTable = LoadResource(hModule, msgTableInfo);
        var data = Marshal.PtrToStructure<MESSAGE_RESOURCE_DATA>(LockResource(msgTable));
        int blockSize = Marshal.SizeOf<MESSAGE_RESOURCE_BLOCK>();
        for (int i = 0; i < data.NumberOfBlocks.ToInt32(); i++) {
            IntPtr blockPtr = IntPtr.Add(data.Blocks, blockSize * i);
            // the following line causes an access violation
            var block = Marshal.PtrToStructure<MESSAGE_RESOURCE_BLOCK>(blockPtr);
        }
    }
}

但这不起作用,我收到了访问冲突错误。

我怎样才能编组这样的结构?

c# resources pinvoke
2个回答
5
投票

你没接近,这些结构体不包含IntPtr。 DWORD 是 32 位整数。资源格式中使用的可变长度结构在 C# 中非常尴尬,没有合适的方法来声明它们。最好的办法是使用 Marshal.ReadXxx() 读取字段。

唯一仍然有用的结构声明是:

[StructLayout(LayoutKind.Sequential)]
struct MESSAGE_RESOURCE_BLOCK {
    public int LowId;
    public int HighId;
    public int OffsetToEntries;
}

然后你就这样一决胜负:

static void Main(string[] args) {
    const int RT_MESSAGETABLE = 11;
    IntPtr hModule = LoadLibrary(@"C:\WINDOWS\system32\msobjs.dll");
    IntPtr msgTableInfo = FindResource(hModule, 1, RT_MESSAGETABLE);
    IntPtr msgTable = LoadResource(hModule, msgTableInfo);
    IntPtr memTable = LockResource(msgTable);

    int numberOfBlocks = Marshal.ReadInt32(memTable);
    IntPtr blockPtr = IntPtr.Add(memTable, 4);
    int blockSize = Marshal.SizeOf<MESSAGE_RESOURCE_BLOCK>();

    for (int i = 0; i < numberOfBlocks; i++) {
        var block = Marshal.PtrToStructure<MESSAGE_RESOURCE_BLOCK>(blockPtr);
        IntPtr entryPtr = IntPtr.Add(memTable, block.OffsetToEntries);

        for (int id = block.LowId; id <= block.HighId; id++) {
            var length = Marshal.ReadInt16(entryPtr);
            var flags = Marshal.ReadInt16(entryPtr, 2);
            var textPtr = IntPtr.Add(entryPtr, 4);
            var text = "Bad flags??";
            if (flags == 0) {
                text = Marshal.PtrToStringAnsi(textPtr);
            }
            else if (flags == 1) {
                text = Marshal.PtrToStringUni(textPtr);
            }
            text = text.Replace("\r\n", "");
            Console.WriteLine("{0} : {1}", id, text);
            entryPtr = IntPtr.Add(entryPtr, length);
        }
        blockPtr = IntPtr.Add(blockPtr, blockSize);
    }
}

32位和64位模式下的输出:

279 : Undefined Access (no effect) Bit 7
1536 : Unused message ID
1537 : DELETE
1538 : READ_CONTROL
1539 : WRITE_DAC
1540 : WRITE_OWNER
1541 : SYNCHRONIZE
1542 : ACCESS_SYS_SEC
1543 : MAX_ALLOWED
1552 : Unknown specific access (bit 0)
1553 : Unknown specific access (bit 1)
1554 : Unknown specific access (bit 2)
1555 : Unknown specific access (bit 3)
1556 : Unknown specific access (bit 4)
1557 : Unknown specific access (bit 5)
...etc...

请记住,当您使用抖动强制在 32 位模式下运行程序时,您不会读取您认为的文件。文件系统重定向器将使您改为读取 C:\WINDOWS\SysWow64\msobjs.dll。


0
投票

汉斯·帕桑特的回答:

DWORD 是一个 32 位整数。

实际上,

DWORD
是一个32位无符号整数。

Hans Passant 在接受的答案中对

struct
MESSAGE_RESOURCE_BLOCK
定义是不正确的,虽然它在某些情况下可能有效,但最终也会导致错误。

LowId
HighId
属性应定义为
uint
。 (虽然技术上对于
OffsetToEntries
属性也是如此,但这样做与标准
IntPrt
加法不兼容。由于偏移量不会超过
Int32.MaxValue
,因此
OffsetToEntries
可以保持
int
。因此:

[StructLayout(LayoutKind.Sequential)]
struct MESSAGE_RESOURCE_BLOCK
{
    public uint LowId;
    public uint HighId;

    /// <summary>This should technically be a uint, but IntPtr addition doesn't accept that.</summary>
    public int OffsetToEntries;
}

如果加载

C:\Windows\system32\kernel32.dll
的消息表,您可以看到差异。在那里,你会发现:

2205548545 Invalid operation performed by the protocol.

如果

MESSAGE_RESOURCE_BLOCK
struct
定义使用
int
,它将返回
-2089418751
作为
id
值。但作为用法示例,在
Microsoft-Windows-Hyper-V-Compute-Admin
事件日志中,事件 ID 10018 包含数据参数
%%2205548545
,如以下屏幕截图所示:

因此,当您处理该事件记录时,如果您尝试将该参数的消息表 ID 与您的消息表 ID 进行匹配,如果您的

MESSAGE_RESOURCE_BLOCK
struct
是用
int
定义的,您将找不到匹配项,而不是
uint

© www.soinside.com 2019 - 2024. All rights reserved.