zoukankan      html  css  js  c++  java
  • C#实现对外部程序的调用操作

    测试工具,首先也是一个C#的程序,它的主要目的是:

      1:获取上文应用程序的窗口句柄,继而获取TextBox句柄及Button句柄;

      2:为TextBox随机填入一些字符;

      3:模拟点击Button;

    1.1:EnumChildWindows介绍

    在这里需要介绍下EnumChildWindows,

    EnumChildWindows可是个好东西,可以枚举一个父窗口的所有子窗口:
    
    BOOL EnumChildWindows( 
      HWND hWndParent, // handle to parent window // 父窗口句柄 
      WNDENUMPROC lpEnumFunc, // callback function // 回调函数的地址 
      LPARAM lParam // application-defined value // 你自已定义的参数 
    );
    

      

    就这么简单,让我们再定义一个回调函数,像下面这样:

    BOOL CALLBACK EnumChildProc( 
      HWND hwnd, // handle to child window 
      LPARAM lParam // application-defined value 
    );

    在调用EnumChildWindows 这个函数时,直到调用到最个一个子窗口被枚举或回调函数返回一个false,否则将一直枚举下去。

    1.2:简单例子的主要源码

    测试工具的主要代码如下:

    private void button1_Click(object sender, EventArgs e)
    {
      //获取测试程序的窗体句柄
      IntPtr mainWnd = FindWindow(null, "FormLogin");
      List<IntPtr> listWnd = new List<IntPtr>();
      //获取窗体上OK按钮的句柄 
      IntPtr hwnd_button = FindWindowEx(mainWnd, new IntPtr(0), null, "OK");
      //获取窗体上所有控件的句柄
      EnumChildWindows(mainWnd, new CallBack(delegate(IntPtr hwnd, int lParam)
      {
      listWnd.Add(hwnd);
      return true;
      }), 0);
      foreach (IntPtr item in listWnd)
      {
        if (item != hwnd_button)
        {
          char[] UserChar = "luminji".ToCharArray();
          foreach (char ch in UserChar)
          {
            SendChar(item, ch, 100);
          }
        }
      }
    SendMessage(hwnd_button, WM_CLICK, mainWnd, "0");
    }
    
    public void SendChar(IntPtr hand, char ch, int SleepTime)
    {
      PostMessage(hand, WM_CHAR, ch, 0);
      System.Threading.Thread.Sleep(SleepTime);
    }
    
    public static int WM_CHAR = 0x102;
    public static int WM_CLICK = 0x00F5;
    
    [DllImport("User32.dll", EntryPoint = "SendMessage")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
    
    [DllImport("user32.dll")]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
    string lpszClass, string lpszWindow);
    
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("user32.dll")]
    public static extern int AnyPopup();
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    
    [DllImport("user32.dll")]
    public static extern int EnumThreadWindows(IntPtr dwThreadId, CallBack lpfn, int lParam);
    
    [DllImport("user32.dll")]
    public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);
    
    [DllImport("user32.dll", CharSet = CharSet.Ansi)]
    publicstaticexternIntPtrPostMessage(IntPtr hwnd,int wMsg,int wParam,int lParam);
    
    [DllImport("user32.dll",CharSet=CharSet.Ansi)]
    publicstaticexternIntPtrSendMessage(IntPtr hwnd,int wMsg,IntPtr wParam,IntPtr lParam);
    
    [DllImport("user32.dll",CharSet=CharSet.Unicode)]
    publicstaticexternIntPtrSendMessageA(IntPtr hwnd,int wMsg,int wParam,int lParam);
    
    [DllImport("user32.dll",CharSet=CharSet.Auto)]
    staticexternintGetClassName(IntPtr hWnd,StringBuilder lpClassName,int nMaxCount);
    
    [DllImport("user32.dll",SetLastError=true,CharSet=CharSet.Auto)]
    publicstaticexternintGetWindowTextLength(IntPtr hWnd);
    
    [DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=false)]
    publicstaticexternIntPtrGetParent(IntPtr hWnd);
    
    publicdelegateboolCallBack(IntPtr hwnd,int lParam);
    

      

    C#实现对外部程序的调用操作 - 空客 - Program Management
    2:难点:如何获取指定的控件句柄

    细心的人可能已经发现,上文中,给文本框赋值的地方,使用了如下代码:

    foreach (IntPtr item in listWnd)
    {
      if (item != hwnd_button)
      {
        char[] UserChar = "luminji".ToCharArray();
        foreach (char ch in UserChar)
        {
          SendChar(item, ch, 100);
        }
       }
    }
    

      

    假设我们的窗体上有多个文本框,那么事实上,这段代码会给所有的文本框输入"luminji”字样。这在多数应用程序中都是不允许的,我们需要精确定位需要控制的控件。
    我们在得到OK按钮的句柄的时候,使用了函数:

    IntPtr hwnd_button = FindWindowEx(mainWnd, new IntPtr(0), null, "OK");
    而想要获取文本框句柄的时候,这个函数却不能使用,因为,所有文本框都是没有标题的,也就是类似"OK"这个值。有人说,那就使用控件ID吧。且看:

    2.1:获取控件ID

    非.NET程序,一旦程序被生成,控件ID就是固定的,所以这一招,用在非.NET程序中,那是再好也不过了。

    C实现对外部程序的调用操作 - 空客 - Program Management
    根据ID来得到控件句柄的函数声明如下:

    [DllImport("user32.dll ", EntryPoint = "GetDlgItem")] public static extern IntPtr GetDlgItem( IntPtr hParent, int nIDParentItem);
    其中,第一个参数就是窗体的句柄,第二个参数就是控件ID。

    但是,显然,这种方法不适用于我们的.NET程序,因为我们会发现,我们的.NET程序没运行一次,这个ID是变化的。

    2.2:获取控件位置

    所以,最终的一个方案是:根据控件位置,人工比对后得到我们想要的控件句柄。该函数的声明如下:

    好了,现在的关键就是怎么取得这个控件的位置。我们在VS中查看,某个控件有X坐标和Y坐标,以上面程序的这个TextBox来说,其在VS中显示的位置是“70,83”,但是而VS中显示的,是不包含标题和边框的坐标值。但是这个坐标值可以作为我们人工比对的参考。

    更精确的坐标值,我们写代码来实现,如下:

    EnumChildWindows(mainWnd, new CallBack(delegate(IntPtr hwnd, int lParam)
    {
    listWnd.Add(hwnd);
    StringBuilder className = new StringBuilder(126);
    StringBuilder title = new StringBuilder(200);
    GetWindowText(hwnd, title, 200);
    RECT clientRect;
    GetClientRect(hwnd, out clientRect);
    int controlWidth = clientRect.Width;
    int controlHeight = clientRect.Height;
    int x = 0, y = 0;
    IntPtr parerntHandle = GetParent(hwnd);
    if (parerntHandle != IntPtr.Zero)
    {
    GetWindowRect(hwnd, out clientRect);
    RECT rect;
    GetWindowRect(parerntHandle, out rect);
    x = clientRect.X - rect.X;
    y = clientRect.Y - rect.Y;
    Debug.Print(x.ToString());
    Debug.Print(y.ToString());
    }
    return true;
    }), 0);
    

      

    注意,上面代码中的X和Y就是某个控件的精确的X和Y值,记录下来,比对一下,我们就能得到精确的坐标值了。在上文的例子中,我们的文本框的坐标最终得到为“78,113”。
    有了这个坐标值,我们便知道这个控件的句柄,也就是hwnd是属于哪个控件的了。

    2.3:根据EnumChildWindows枚举次序得到句柄

    如果你不想这么麻烦,还有一种简单的方案,那就是利用EnumChildWindows的枚举顺序。要知道,在不同的机器上,EnumChildWindows枚举一个窗体上子控件的顺序是相同的,也就是说,如果有两个文本框,它们在这台机器上被枚举的顺序一个是2,一个是3,那么,它们在其它机器上被枚举的顺序,也是这个固定次序。通过比对,我们也能得到它们各自的句柄。当然,如果我们有了这些句柄,还有什么是不能做到的呢
    2.4:使用SPY++

    SPY++是微软的一个工具,用户获取窗体上的ID或者类型或者句柄等信息。因为在我们的这个例子里,ID和句柄在每台机器上都是不变的,所以这个工具对于我们来说,没有多大的用处。但是,当你HACK别人的程序的时候,它会发挥一定作用。

    C实现对外部程序的调用操作 - 空客 - Program Management

    IntPtr p = IntPtr.Zero; //循环查找出同一层次上的所有#32770的句柄
    do
    {
      p = FindWindowEx(hwndCalcFrame, p, "#32770", null);
      Console.WriteLine(p.ToString());
    } while (!p.Equals(IntPtr.Zero));
    

      

  • 相关阅读:
    Python中的类(上)
    Django REST Framework API Guide 07
    Django REST Framework API Guide 06
    Django REST Framework API Guide 05
    Django REST Framework API Guide 04
    Django REST Framework API Guide 03
    Django REST Framework API Guide 02
    Django REST Framework API Guide 01
    Django 详解 信号Signal
    Django 详解 中间件Middleware
  • 原文地址:https://www.cnblogs.com/microtiger/p/7842822.html
Copyright © 2011-2022 走看看