如何使用Win32 API在TreeView中选择项目

时间:2010-09-07 16:24:42

标签: c# api winapi c#-4.0

我正在尝试使用Win32 API自动化一系列用户输入到C#中的已编译应用程序。我没有任何我试图控制的应用程序的源代码,它正在运行,而我正在尝试控制它。在我的代码中,我有一个按钮,当单击时,需要为我试图控制的应用程序生成3个输入序列:

  1. 在树状视图中选择一个项目
  2. 点击按钮
  3. 点击另一个按钮
  4. 它的工作方式是步骤2中的按钮根据在步骤1中的树视图中选择的项目执行操作。我能够通过简单地发送消息让按钮点击工作得很好,但我无法想象如何选择我想要的TreeView项目。 TreeView是静态的,因此项目和布局永远不会改变。它有以下布局:

    -itemsA
    -itemsB
    --itemB1
    -itemsC

    其中itemB1是需要选择的项目,以便步骤2和3中的按钮点击工作。默认情况下,ItemsB已折叠,因此我可能需要先展开它才能选择ItemB1?这是我的代码。我真的很感激任何帮助!

    //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);
    
    
    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
    

    汉斯,

    你能举个例子说明我会怎么做吗?我真正遇到的问题是找到我想要选择的树视图项的句柄。如果我使用Spy ++查找当前句柄并将其硬编码到我的方法中,它可以正常工作,如下所示:

    SendMessage((int)treeChild, TV_SELECTITEM, TVGN_CARET, (IntPtr)0x092DCB30); 
    

    如果我使用SendMessage并将TVGN_ROOT发送到TreeView句柄,它是否会返回一个IntPtr,其中包含要在树视图中选择的项目的句柄,或者它是如何工作的?我也在尝试使用AutoIt,但我希望将所有代码保存在一个应用程序中。

3 个答案:

答案 0 :(得分:11)

我想通了,所以感兴趣的其他人的帖子不好,我很难找到这方面的文件。以下是我的大部分代码:

//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 hwnd = 0;
            int treeItem = 0;
            IntPtr hwndChild = IntPtr.Zero;
            IntPtr treeChild = IntPtr.Zero;

            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...
             }
        }//END Scan

我可能仍然不理解这100%,但希望这会有所帮助。 SendMessage返回值取决于您发送的消息,在这种情况下,它是一个包含TreeView项的句柄的int。第一个参数是TreeView本身的句柄。第二个参数是您要发送的消息。第3和第4个参数是标志。第3个指定项目的类型,第4个是当前树视图项目的句柄。

感谢Hans的帮助!其他人,请随时详细说明。

答案 1 :(得分:2)

从TVGN_ROOT开始,您需要使用TVM_GETNEXTITEM遍历节点。然后使用TVM_SELECTITEM选择它。通过TVGN_FIRSTVISIBLE以确保它是可见的,如果你只是自动化它就不是必需的。

看看AutoIt以避免像这样编写蹩脚的代码。

答案 2 :(得分:0)

我知道现在已经很晚了,但如果你遇到类似的问题(就像我一样)。您可以查看AutoHotKey,特别是如果您熟悉SendMessage。这样可以节省编译的需要和很多复杂性,但是对于您的观点,可以使用箭头键按下导航结构。

相关问题