I am trying to automate a sequence of user inputs to a compiled application in C# using Win32 API. I do not have any source code for the application I am trying to control and it is running while I am trying to control it. In my code, I have a single button that, when clicked, needs to make a sequence of 3 inputs to the application I am trying to control:
- Select an item in a treeview
- Click a button
- Click another button
The way it works is the button in step 2 performs an action depending on the item selected in the treeview in step 1. I am able to get the button clicks to work just fine by simply sending a message, but I cannot figure out how to select the TreeView item I want. The TreeView is static, so the items and layout will never change. It has the following layout:
-itemsA
-itemsB --itemB1 -itemsCWhere itemB1 is the item that needs to be selected in order for the button clicks in steps 2 and 3 to work. By default ItemsB is collapsed, so I probably need to expand it before I can select ItemB1? Here is my code. I really appreciate any help!!
//Find Window API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
//Find WindowEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//Send Message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int w开发者_运维问答Param, IntPtr lParam);
private const int BN_CLICKED = 245;
//Method called by button click
public static void Start()
{
int hwnd = 0;
int prod = 0;
IntPtr hwndChild = IntPtr.Zero;
IntPtr treeChild = IntPtr.Zero;
IntPtr prodChild = IntPtr.Zero;
hwnd = FindWindow(null, "Application");
if (hwnd > 0)
{
//Get Handle for TreeView, THIS IS WHERE I AM STUCK!!
treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
//Need to Add code to select item in TreeView ???
//Click First Button
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "AfxMDIFrame80", null);
hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "#32770", null);
IntPtr scanBtn = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "Button", "&Scan");
SendMessage((int)scanBtn, BN_CLICKED, 0, IntPtr.Zero);
//Click Second Button
prod = FindWindow("#32770", "Product: WPC");
prodChild = FindWindowEx((IntPtr)prod, IntPtr.Zero, "Button", "&Collect");
SendMessage((int)prodChild, BN_CLICKED, 0, IntPtr.Zero);
}
}//END Start
Hans,
Can you give me an example of how I would do this? The problem I am really having is finding the handle for the treeview item I want to select. If I use Spy++ to find the current handle and hardcode it into my method, it works fine, like this:
SendMessage((int)treeChild, TV_SELECTITEM, TVGN_CARET, (IntPtr)0x092DCB30);
If I use SendMessage and send TVGN_ROOT to the TreeView Handle, will it return an IntPtr with the handle for the item to select in the treeview, or how does that work? I am also experimenting with AutoIt, but I was hoping to keep all of my code in one application.
I figured it out, so I'll post for anyone else who is interested, I have had a hard time finding documentation on this. Here is the majority of my code:
//Define TreeView Flags and Messages
private const int BN_CLICKED = 0xF5;
private const int TV_FIRST = 0x1100;
private const int TVGN_ROOT = 0x0;
private const int TVGN_NEXT = 0x1;
private const int TVGN_CHILD = 0x4;
private const int TVGN_FIRSTVISIBLE = 0x5;
private const int TVGN_NEXTVISIBLE = 0x6;
private const int TVGN_CARET = 0x9;
private const int TVM_SELECTITEM = (TV_FIRST + 11);
private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
private const int TVM_GETITEM = (TV_FIRST + 12);
//Find Window API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
//Find WindowEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//Send Message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
public static void Start()
{
//Handle variables
int treeItem = 0;
IntPtr treeChild = IntPtr.Zero;
int hwnd = FindWindow(null, "Application"); //Handle for the application to be controlled
if (hwnd > 0)
{
//Select TreeView Item
treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_ROOT, IntPtr.Zero);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_NEXT, (IntPtr)treeItem);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_CHILD, (IntPtr)treeItem);
SendMessage((int)treeChild, TVM_SELECTITEM, TVGN_CARET, (IntPtr)treeItem);
// ...Continue with my automation...
}
}
I may still not understand this 100%, but hopefully this helps. The SendMessage
returns value will depend on what message you are sending, in this case, it was an int
containing the handle of a TreeView item. The first argument is the handle to the TreeView itself. The second argument is the Message you want to send. The 3rd and 4th arguments are flags. The 3rd specifies the type of item, the 4th is the handle of the current TreeView item.
Thanks for the help Hans! Anyone else, please feel free to elaborate.
You'll need to walk the nodes with TVM_GETNEXTITEM, starting at TVGN_ROOT. Then select it with TVM_SELECTITEM. Pass the TVGN_FIRSTVISIBLE to ensure it is visible, shouldn't be necessary if you just automate it.
Take a look at AutoIt to avoid writing grungy code like this.
I know this is quite late in coming, but if you are having a similar issue (as am I). You might take a look at AutoHotKey, especially if you are familiar with SendMessage. This would save you the need to compile and a lot of complexity, but to your point it would be possible to navigate through the structure using arrow key presses.
精彩评论