I'm currently working on automating a Win32 UI application that cannot be altered. So far my approach is using the standard message queue of the target application to inject my inputs. I've gotten quite far with that:
- "Clicking" buttons using
WM_COMMAND
works - Reading the names of tabs via
TCM_GETITEMA
and activating them via virtual mouse clicksWM_LBUTTONDOWN
/WM_LBUTTONUP
works - Reading enabled/disabled states of controls works
Where I'm stuck, however, is modifying the text of an editable ComboBox and its Edit control. I try using the WM_SETTEXT
message like so:
public static void SetText(IntPtr hWnd, string text) {
// Maximum item text buffer length
const int MAX_LEN = 512;
// Get process
uint ProcessId;
WinAPI.GetWindowThreadProcessId(hWnd, out ProcessId);
IntPtr process = WinAPI.OpenProcess(
WinAPI.ProcessAccessFlags.VMOperation | WinAPI.ProcessAccessFlags.VMRead |
WinAPI.ProcessAccessFlags.VMWrite | WinAPI.ProcessAccessFlags.QueryInformation,
false, ProcessId
);
if( process == IntPtr.Zero )
throw new Exception("Could not open process");
// Allocate memory in remote process
IntPtr farTextPtr = WinAPI.VirtualAllocEx(process, IntPtr.Zero, MAX_LEN,
WinAPI.AllocationType.Commit,
WinAPI.MemoryProtection.ReadWrite
);
try {
if( farTextPtr == IntPtr.Zero )
throw new Exception("Could not allocate memory in target process");
IntPtr nearTextPtr, pData;
int bytesRead;
// Write item text to remote memory (Unicode!)
nearTextPtr = Marshal.StringToHGlobalUni(text);
WinAPI.WriteProcessMemory(process, farTextPtr, nearTextPtr, MAX_LEN, out bytesRead);
Marshal.FreeHGlobal(nearTextPtr);
// Just for debugging purposes, read it back to verify it was set properly
pData = Marshal.AllocHGlobal(MAX_LEN);
WinAPI.ReadProcessMemory(process, farTextPtr, pData, MAX_LEN, out bytesRead);
text = Marshal.PtrToStringUni(pData);
Marshal.FreeHGlobal(pData);
// Set the text
int res = WinAPI.SendMessage(hWnd, Constants.WM_SETTEXT, IntPtr.Zero, farTextPtr);
if( res != 1 ) throw new Exception("SendMessage WM_SETTEXT failed");
} finally {
// Free remotely allocated memory
if( farTextPtr != IntPtr.Zero )
WinAPI.VirtualFreeEx(process, farTextPtr, 0, WinAPI.FreeType.Release);
WinAPI.CloseHandle(process);
}
}
This does not work! If I attach Spy++ to the target control I can see, that the message is received, but it is received with a different wParam
than I specified in the call to SendMessage
.
E.g. the call to VirtualAllocEx
returned a pointer within the target process of value 0x048b0000
. In the received message within Spy++ I see the value 0x0011AA88
which is wrong:
<000009> 0004065E S WM_SETTEXT lpsz:0011AA88 ("<random characters here>") [wParam:00000000 lParam:0011AA88]
Does the pointer somehow get altered? I do the same procedure in my routine to retrieve strings from controls, like with the TabControl. There it works flawlessly. Is there something different when using WM_SETTEXT
?