I am trying to automate a process in a application. The Spy++ representation of this process when I do it manually looks like:
WINDOW 002F0B08
002F0B08 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPOS:# ypos:# (Start Pos)
002F0B08 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPOS:# ypos:#
...
002F0B08 P WM_LBUTTONUP fwKeys:MK_LBUTTON xPOS:# ypos:# (Final Pos)
WINDOW 007406BC
007406BC P WM_KEYDOWN fwKeys:VK_CONTROL ...
007406BC P WM_KEYDOWN fwKeys:VK_C ...
007406BC P WM_KEYUP fwKeys:VK_CONTROL ...
007406BC P WM_KEYUP fwKeys:VK_C ...
The goal is to select an area and copy the area to the clipbard. I am able to perform the first part by using PostMessage:
PostMessage(PanelHandle, WM_LBUTTONDOWN, (int)MK_LBUTTON, new IntPtr(lParam)); //send left mouse button down
and this is reflected in Spy++ Message Window, but for some reason it is not working for the window which reflects CTRL+C when I do it manually.
The entire code is:
class MouseControl
{
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
static extern void mouse_event(MouseEventFlags flags, uint dx, uint dy, uint delta, IntPtr extraInfo);
public static void Select(AutomationElement window)
{
AutomationElement main = null;
AutomationElement panel = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "2"));
main = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Session A - [24 x 80]"));
Console.WriteLine("Got Panel: {0} , {1}", panel.ToString(), (new IntPtr(panel.Current.NativeWindowHandle)).ToString());
Console.WriteLine("Got Main: {0} , {1}", main.ToString(), (new IntPtr(main.Current.NativeWindowHandle)).ToString());
if (main == null)
Console.WriteLine("Could not find main");
// GET REF POINT IN WINDOW
System.Windows.Point p = panel.GetClickablePoint();
// LeftMouseDown();
// LeftMouseUp();
double new_X = p.X-400;
double new_Y = p.Y-800;
const uint WM_LBUTTONDOWN = 0x0201;
const uint WM_LBUTTONUP = 0x0202;
const uint MK_LBUTTON = 0x1;
const uint WM_MOUSEMOVE = 0x0200;
const uint WM_KEYDOWN = 0x100;
const uint WM_KEYUP = 0x101;
const uint VK_C = 0x043;
const uint VK_CONTROL = 0x011;
IntPtr PanelHandle = new IntPtr(panel.Current.NativeWindowHandle);
IntPtr WindowHandle = new IntPtr(main.Current.NativeWindowHandle);
Console.WriteLine("Press Return To Start");
Console.ReadLine();
//LeftMouseDown();
int lParam = (((int)new_Y << 16) | ((int)new_X & 0xffff));
PostMessage(PanelHandle, WM_LBUTTONDOWN, (int)MK_LBUTTON, new IntPtr(lParam));//send left mouse button down
Console.WriteLine("Tried");
while (new_X < p.X + 100)
{
lParam = (((int)new_X << 16) | ((int)new_Y & 0xffff));
PostMessage(PanelHandle, WM_MOUSEMOVE, (int)MK_LBUTTON, new IntPtr(lParam));//send left mouse button down
new_X += 1;
new_Y += 1;
}
PostMessage(PanelHandle, WM_LBUTTONUP, (int)MK_LBUTTON, new IntPtr(lParam));//send left mouse button down
Console.WriteLine("WM_LBUTTONUP, MK_LBUTTON, {1} was sent to {0}", PanelHandle, lParam);
PostMessage(WindowHandle, WM_KEYDOWN, (int)VK_CONTROL, IntPtr.Zero);
Console.WriteLine("WM_KEYDOWN, VK_CONTROL was sent to {0}", WindowHandle);
PostMessage(WindowHandle, WM_KEYDOWN, (int)VK_C, IntPtr.Zero);
Console.WriteLine("WM_KEYDOWN, VK_C was sent to {0}", WindowHandle);
PostMessage(WindowHandle, WM_KEYUP, (int)VK_CONTROL, IntPtr.Zero);
Console.WriteLine("WM_KEYUP, VK_CONTROL was sent to {0}", WindowHandle);
PostMessage(WindowHandle, WM_KEYUP, (int)VK_C, IntPtr.Zero);
Console.WriteLine("WM_KEYUP, VK_C was sent to {0}", WindowHandle);
}
}
The Console Displays:
Got Panel: ... , 3083016 (handle displayed in Spy++: 002F0B08
Got Main: ... , 0 (handle displayed in Spy++: 007406BC
Press Return To Start
Tried
WM_LBUTTONUP, MK_LBUTTON, 61538539 was sent to 3213992
WM_KEYDOWN, VK_CONTROL was sent to 0
WM_KEYDOWN, VK_C was sent to 0
WM_KEYUP, VK_CONTROL was sent to 0
WM_KEYUP, VK_C was sent to 0
It successfully selects the area but is unable to copy the area. The CTRL + C message is not sending to the window which performs the copy function.
EDIT: I managed to get it to work (not as desired, but fine for now) by manually retrieving the handle from Spy++ using:
var hwnd = new IntPtr(Convert.ToInt32(Console.ReadLine(), 16));
IntPtr WindowHandle = hwnd;
However it does not perform a copy. This is the manual copy from Spy++:
<00271> 00390640 P WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00368> 00390640 P WM_KEYDOWN nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00375> 00390640 P WM_KEYUP nVirtKey:'C' cRepeat:1 ScanCode:2E fExtended:0 fAltDown:0 fRepeat:1 fUp:1
<00376> 00390640 P WM_KEYUP nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:1 fUp:1
And this is what my program does:
<00013> 00390640 P WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00014> 00390640 P WM_KEYDOWN nVirtKey:'C' cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00015> 00390640 P WM_KEYUP nVirtKey:VK_CONTROL cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00016> 00390640 P WM_KEYUP nVirtKey:'C' cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
Any ideas on why this isn't working?
Couple of things wrong here.
NativeWindowHandlecan often be 0 if the control in question is windowless. Since all windowless controls have to be hosted in some window (even if it's the top-level window), Spy++ will always show the (host) window that the message will go to. If you really need aNativeWindowHandle, you can walk up the automation parent chain until you find aNativeWindowHandlethat's not NULL.