After banging away for a couple of days, I'm turning to this forum for some hints. I'm trying control a program my company has for which we have no sources, only an exe. Specifically, I'm trying to click buttons and make values appear in text boxes. The compiled app looks like it's written in C++, has that MFC look about it.
I've been able to get the button clicking part working, lots of Win32 API 'FindWindow(...)', 'FindWindowEx(...)', 'SendMessage(..., BM_CLICK,...)' stuff. Wotta hack!
My issue is specifically with setting the contents of a textbox. I can get the window handle of the textbox, but using 'SetWindowText(handle, "Test string")' results in nothing appearing in the TB. I can change the title of the parent form that's hosting the textbox, so at least I know I'm communicating.
I tried 'SendMessage( handle, WM_COPYDATA, 2,
I set a trap on the textbox Text_Changed event (did I mention I built a little test suite to figure this out, so I can instrument the target program, something I can't do with the compiled exe) but I get no event.
I'd be glad to supply any additional info that might be useful, and many thanks for any advice you can offer.
Regards,
JJ
John KellyPosted Oct 19, 2007, 10:22 PM
Thanks again for providing some guidance. I was just stuck, spinning my wheels, and the suggestions you guys made got me to a solution...and on to the next problem of course! But hey, that's what engineers do, right?
I owe you both a beer, you just have to come Boston to imbibe...
And Alan, that was a great explanation of what's happening under the hood. I didn't realize that 'SendMessage(...)' used the message number to choose the right overloaded method, kinda opened up a whole new way of thinking about this stuff.
Best,
John
AlanPosted Oct 19, 2007, 5:54 PM
Pleased to see that you've got that to work, John :)
Sorry, I didn't know how you'd defined the P/Invoke signature for SendMessage() but I should have warned you that the fourth parameter needed to be a string.
The reason why that works is because, when you use WM_SETTEXT, the underlying code is expecting the fourth parameter to be a pointer to the null terminated string that you want to send.
Strings are, of course, already pointers in C# (i.e. references to where the string is stored on the heap) and the P/Invoke marshaller takes care of converting the .NET length-prefixed unicode string to a zero terminated ANSI string automatically.
John KellyPosted Oct 19, 2007, 5:32 PM
1. Change the type of the final (lparam) parameter in SendMessage(...) from int to string; thusly:
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, string lParam);
(I'm astounded the compiler let me get away with just changing the type, but I was willing
to try anything!)
2. Then my code looks like:
string buffer = "sometext";
// get the window handle, assign to 'retVal', an int
SendMessage(retVal, WM_SETTEXT, 0, buffer);
and voila! In the textbox in my test target application, up pops "sometext"
Thanks all,
JJ
John KellyPosted Oct 19, 2007, 5:23 PM
Thanks for the suggestion. I made some slight progress using your suggestion of WM_SETTEXT: I was able to get the first character of my string "sometext" to appear in the target textbox, after I converted the string to a VarPtr...because SendMessage requires the lParam parameter to be an int, I couldn't just send the string.
So I'm onto something, just not quite there yet. Any further ideas?
Thanks,
JJ
AlanPosted Oct 19, 2007, 4:45 PM
To send data to a TextBox in another application, I'd also try WM_SETTEXT as you can send a simple string rather than a struct:
const int WM_SETTEXT = 0x0C;
SendMessage(handle, WM_SETTEXT, 0, textToSend);
John KellyPosted Oct 19, 2007, 4:26 PM
Thanks for that info. I'll give it a try and report back. I'm doing this in c#. One question I wanted to clarify: when you stated "wParam is the handle to the window passing the control or your apps window", am I correct in assuming you are referring to the window handle of the application that's *sending* the text?
Gracias,
JJ
Mike GoldPosted Oct 19, 2007, 4:20 PM
Try SendMessage WM_COPYDATA as you were going to
SendMessage(hControlDestination, WM_COPYDATA, wParam, lParam)
wParam is the handle to the window passing the control or your apps window
lParam is the COPYDATASTRUCT.
COPYDATASTRUCT contains
typedef struct tagCOPYDATASTRUCT { ULONG_PTR dwData; DWORD cbData; PVOID lpData; } COPYDATASTRUCT, *PCOPYDATASTRUCT;If you are marshaling this in C#, then use
C# Definition:
struct COPYDATASTRUCT {
public int dwData;
public int cbData;
public IntPtr lpData;
}
for a string, set dwData to 0, cbData is the length of the string + 1, lpData is an unmanaged byte array of the characters (you'll need to marshal this from a byte array if you are doing this in C#).
If you are doing this in C++ or an unmanaged environment like Delphi, then its a bit easier.
Best,