I need to read a Message Table resource in C#.
I'm basically trying to port to C# the code in the answer to this question: Listing message IDs and symbolic names stored in a resource-only library (DLL) using Win32 API
My problem is that I cannot correctly marshal the MESSAGE_RESOURCE_DATA
and MESSAGE_RESOURCE_BLOCK
structures, which are defined like this (in <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;
In MESSAGE_RESOURCE_DATA
, NumberOfBlocks
is the number of MESSAGE_RESOURCE_BLOCK
entries in the Blocks
array (even if it is declared as an array with a single element).
Since I do not know at compile time the array size, I tried to marshal the struct declaring Blocks
as a pointer, and then using Marshal.PtrToStructure
like this:
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);
}
}
}
This does not work, however, and I'm getting an access violation error.
How can I marshal a structure like this?