How can I correctly extract all Control texts from an MFC DLL with C# and PInvoke?
The problem is that there are two formats: DLGTEMPLATE
and DLGTEMPLATEEX
. In addition in these resources, there are Items and these also have two formats: DLGITEMTEMPLATE
and DLGITEMTEMPLATEEX
.
I use PInvoke to call Windows APIs: FindResource
, LoadResource
, LockResource
, EnumResourceNames
. I successfully could extract Texts from the Dialog. I enumerate through the Dialog resources, could retrieve the ID, the Caption, and many Control texts.
But still, there are many texts, which are not extracted correctly. And when I switch the DLL and use another, 50% work and the others don't.
So, I did research on the structs of the 4 Formats and saw that the extended Formats have variable elements. A few of them don't have a static size, but every dialog resource can have a different one. I work with pointers.
This is for the Items, because the code recognizes if it is an Extended format or not. Then when the normal Template is done, it iterates through the Items. But nevertheless, there are problems. And the normal Dialog Format DLGTEMPLATE
does not work. Something is wrong.
This problem has haunted me for days and I need to fix it.
if (isExtended)
{
// ---- DLGTEMPLATEEX ----
ushort dlgVer = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
ushort signature = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
uint helpID = (uint)Marshal.ReadInt32(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 4);
uint exStyle = (uint)Marshal.ReadInt32(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 4);
uint style = (uint)Marshal.ReadInt32(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 4);
ushort cDlgItems = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short x = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short y = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short cx = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short cy = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
// Variable Fields
SkipDialogField(ref currentPtr); // Menu
SkipDialogField(ref currentPtr); // Window Class
string title = ReadUnicodeString(ref currentPtr); // Dialog Title
result.DialogTitle = title;
if ((style & 0x40) != 0)
{
ushort fontSize = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
string fontName = ReadUnicodeString(ref currentPtr);
}
result.ControlTexts = ParseDialogItems(ref currentPtr, cDlgItems, true);
}
else
{
// ---- Standard-DLGTEMPLATE ----
uint style = (uint)Marshal.ReadInt32(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 4);
uint dwExtendedStyle = (uint)Marshal.ReadInt32(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 4);
ushort cdit = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short x = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short y = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short cx = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
short cy = (short)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
// Variable Fields: Menu, Window Class, Dialog Title
SkipDialogField(ref currentPtr); // Menu
SkipDialogField(ref currentPtr); // Window Class
string title = ReadUnicodeString(ref currentPtr); // Dialog Title
result.DialogTitle = title;
if ((style & 0x40) != 0)
{
ushort fontSize = (ushort)Marshal.ReadInt16(currentPtr);
currentPtr = IntPtr.Add(currentPtr, 2);
string fontName = ReadUnicodeString(ref currentPtr);
}
result.ControlTexts = ParseDialogItems(ref currentPtr, cdit, false);
}
private string ReadUnicodeString(ref IntPtr ptr)
{
StringBuilder sb = new StringBuilder();
long startAddress = ptr.ToInt64();
while (true)
{
char ch = (char)Marshal.ReadInt16(ptr);
ptr = IntPtr.Add(ptr, 2);
if (ch == '\0') break;
sb.Append(ch);
}
return sb.ToString();
}
// To Skip the variable Fields
private void SkipDialogField(ref IntPtr ptr)
{
long startPtr = ptr.ToInt64();
ushort check = (ushort)Marshal.ReadInt16(ptr);
ptr = IntPtr.Add(ptr, 2);
if (check == 0x0000)
{
ptr = IntPtr.Add(ptr, 2);
}
else if (check == 0xFFFF)
{
ptr = IntPtr.Add(ptr, 2);
}
else
{
ReadUnicodeString(ref ptr);
}
}
private List<string> ParseDialogItems(ref IntPtr ptr, ushort controlCount, bool isExtended)
{
List<string> controlTexts = new List<string>();
for (int i = 0; i < controlCount; i++)
{
uint style = 0, exStyle = 0;
if (isExtended)
{
ptr = IntPtr.Add(ptr, 4);
exStyle = (uint)Marshal.ReadInt32(ptr);
ptr = IntPtr.Add(ptr, 4);
style = (uint)Marshal.ReadInt32(ptr);
ptr = IntPtr.Add(ptr, 16);
}
else
{
style = (uint)Marshal.ReadInt32(ptr);
ptr = IntPtr.Add(ptr, 4);
exStyle = (uint)Marshal.ReadInt32(ptr);
ptr = IntPtr.Add(ptr, 10);
}
SkipDialogField(ref ptr);
string ctrlText = ReadUnicodeString(ref ptr);
controlTexts.Add(ctrlText);
ushort extraDataSize = (ushort)Marshal.ReadInt16(ptr);
ptr = IntPtr.Add(ptr, 2 + extraDataSize);
}
return controlTexts;
}
The Templates can be found in the Microsoft Documentation.
I think I have to rework the whole code.