views:

71

answers:

1

I'm trying to programmatically make an external WinForms application copy its data to the clipboard, and it's driving me crazy.

Since pressing Ctrl-C in the application does the right thing, I've been trying to send ctrl-c using my C# application to the other application.

Here is my sequence of commands. I compared the messages using Spy++, and it matches except for my SendMessages are showing up as Send and Receive, vs. the same operation done on the WinForm is showing up as posted.

My code :

          NativeMethods.SendMessage(hwnd, WM_KEYDOWN, 0x00000011, 0x001D0001);
          NativeMethods.SendMessage(hwnd, WM_KEYDOWN, 0x00000043, 0x002E0001);
          NativeMethods.SendMessage(hwnd, WM_CHAR, 0x00000003, 0x002E0001);
          NativeMethods.SendMessage(hwnd, WM_KEYUP, 0x00000043, 0xC02E0001);
          NativeMethods.SendMessage(hwnd, WM_KEYUP, 0x00000011, 0xC01D0001);

Spy++ auto Run ->

 0059043C P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:322 yPos:54 [wParam:00000001 lParam:00360142]
 0059043C P WM_LBUTTONUP fwKeys:0000 xPos:322 yPos:54 [wParam:00000000 lParam:00360142]
 0059043C P WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000011 lParam:001D0001]
 0059043C P WM_KEYDOWN nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000043 lParam:002E0001]
 0059043C P WM_CHAR chCharCode:'3' (3) cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000003 lParam:002E0001]
 0059043C P WM_KEYUP nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000043 lParam:C02E0001]
 0059043C P WM_KEYUP nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000011 lParam:C01D0001]

My Spy++ sequence :

 0059043C S WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:299 yPos:52 [wParam:00000001 lParam:0034012B]
 0059043C R WM_LBUTTONDOWN
 0059043C S WM_LBUTTONUP fwKeys:MK_LBUTTON xPos:299 yPos:52 [wParam:00000001 lParam:0034012B]
 0059043C R WM_LBUTTONUP
 0059043C S WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000011 lParam:001D0001]
 0059043C R WM_KEYDOWN
 0059043C S WM_KEYDOWN nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000043 lParam:002E0001]
 0059043C R WM_KEYDOWN
 0059043C S WM_CHAR chCharCode:'3' (3) cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000003 lParam:002E0001]
 0059043C R WM_CHAR
 0059043C S WM_KEYUP nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000043 lParam:C02E0001]
 0059043C R WM_KEYUP
 0059043C S WM_KEYUP nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000011 lParam:C01D0001]
 0059043C R WM_KEYUP
A: 

You could use PostMessage

Or try SendInput, although then you have to give the target window the focus first.

Ben Voigt
Tried PostMessage, and now I'm able to see in Spy++ the same status code 'P' instead of 'S' or 'R' for messages, but still not working.
Gooose
Kindly help. This is driving me crazy
Gooose
Do I need to use Clipboard functions ?
Gooose
`SendInput` will work correctly as long as you can guarantee that the user isn't interacting with the computer at the same time: it uses a number of global settings including focus and modifier key state. If you need to work completely in the background and not interfere with the user, your only option is `SendMessage` combined with `SetKeyboardState`, and this has to be done from the target thread. And, hooking other applications using .NET has really bad consequences (although .NET 4 alleviates some of these by allowing side-by-side CLR in a single process).
Ben Voigt
However, if your reason for sending Ctrl-C is to copy something onto the clipboard, then there are definitely better ways. You can either put the data on the clipboard yourself by using `SendMessage` with `WM_GETTEXT` or `EM_GETSELTEXT` or similar and then clipboard functions (but if you get the text this way, do you still need the clipboard?) or ask the other application to copy-to-clipboard either with `WM_COPY` or by using Spy++ to find out what `WM_COMMAND` arguments are generated by its Edit->Copy menu item.
Ben Voigt
In the other application, most likely Ctrl-C is handled by TranslateMessage and an accelerator table and becomes a `WM_COMMAND` message, so the application can share processing between the Ctrl-C hotkey, the copy toolbar button, and the Edit->Copy menu item.
Ben Voigt
Thanks Ben. Testing WM_GETTEXT now. Will update here in few.
Gooose
The frustrating part is I'm seeing in Spy++ that I'm sending the exact same set of keyboard commands (ctrl+a, ctrl+c) in my program to this external application vs. executing the commands on the application itself. The latter copies the data, and the former does not.
Gooose
The fact that you're trying to get another application to copy data to the clipboard should have been stated in the question, since it leads to some simpler solutions than the general case of sending modified keypresses. See if you can capture (with Spy++) the `WM_COMMAND` message generated when you select Edit->Copy in the application, and whether using `PostMessage` to duplicate that doesn't copy the data the way you want.
Ben Voigt
Ben Voigt
The external application does not have and Edit menu. I cannot see Edit->Copy in the application.
Gooose
There is a datagridview control in the external WinForm application. I have got the handle to the datagridview control, and selected the first row using SendMessage-> WM_LMBUTTONDOWN, and WM_LBUTTONUP. Now, I need to copy the data of this selected row. Here I'm stuck.
Gooose
Ok... is there a toolbar button for Copy or maybe a Copy item on the right-click menu?
Ben Voigt
All mouse right click items are disabled.
Gooose
I tried the following ..no luck. StringBuilder sg = new StringBuilder(1000);NativeMethods.SendMessage(hwnd, EM_GETSELTEXT, new IntPtr(500), sg);string str = sg.ToString();
Gooose
I tried the WM_COPY, and WM_PASTE; but no luck.
Gooose
The strangest part is when I send EM_GETSELTEXT, or WM_COPY, or WM_PASTE, I don't see the corresponding signals in Spy ++
Gooose
Ben..sincerely appreciate you helping me out here.
Gooose
Oh, with a datagridview control it's potentially very ugly since the app may be handling keystrokes directly instead of through the usual hotkey mechanisms. Since the other app is also .NET, it might be worth using Red Gate Reflector to decompile it and see whether there's custom code to write to the clipboard. Reflector is pretty cool in that you can browse to System.Windows.Clipboard.SetText and then find all places where that function is used by the application.
Ben Voigt
Are there no other options ? What is really stumping me is I'm seeing the exact same ctrl-c messages when I do a ctrl-c on the external application, and the latter works but me sending the ctrl-c from my application does not.
Gooose
In Spy++, are you watching the Form or the DataGridView? KeyPreview settings in the Form may affect how hotkeys are processed, but won't affect other messages. I presume when you're getting the handle to the control that's using `ChildWindowFromPoint` or `GetDlgItem`, or do you possibly have a handle to the main window?
Ben Voigt
I have a handle to the datagridview control. I had to recrusively locate this control.
Gooose
If you're changing the selection in the app anyway, then using `SetFocus` followed by `SendInput`, the older simpler `keybd_event`, or the .NET Wrapper `SendKeys` (as suggesting in a comment by Albin) to actually send Ctrl-C may be the quickest route to a solution. Careful though because you can only set focus if you currently own it, and once you give it away you can't get it back automatically. So a solution that doesn't use keypress-emulation would definitely be better, if one exists.
Ben Voigt
I am able to select the first row on this control using the following ...SendMessage(hwnd, WM_LBUTTONDOWN, 0x00000001, 0x0034012B);SendMessage(hwnd, WM_LBUTTONUP, 0x00000001, 0x0034012B);hwnd = datagridview handle
Gooose
I'll have to explore the emulation further since SendKeys might not be a desiable solution. Ben, appreciate all the info here. I'll post the final solution, if I find one. Thanks again.
Gooose