博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#实现对外部程序的调用操作
阅读量:6258 次
发布时间:2019-06-22

本文共 5636 字,大约阅读时间需要 18 分钟。

测试工具,首先也是一个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
listWnd = new List
();  //获取窗体上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));

  

转载于:https://www.cnblogs.com/microtiger/p/7842822.html

你可能感兴趣的文章
我在博客园的这一年
查看>>
红黑树
查看>>
Jackson使用ObjectManage#readValue传入泛型T的问题
查看>>
Python正则表达式中的re.S的作用
查看>>
从零开始构建一个centos+jdk7+tomcat7的docker镜像文件
查看>>
Source Insight 中文注释为乱码解决办法(完美解决,一键搞定)
查看>>
【LoadRunner】安装LoadRunner
查看>>
Linux内存管理 (15)页面迁移
查看>>
在高并发、高负载的情况下,如何给表添加字段并设置DEFAULT值?
查看>>
Cocos2d-x 3.0final 终结者系列教程13-贪食蛇游戏案例(全)
查看>>
Nginx的try_files指令和命名location使用实例
查看>>
IO多路复用之select
查看>>
pd_ds中的hash
查看>>
买书不读是一种什么病?
查看>>
微信接口开发报错invalid credential, access_token is invalid or not latest hint
查看>>
nohup 部署springboot 使用命令
查看>>
MQ产品比较-ActiveMQ-RocketMQ
查看>>
暂时没有想好呢。
查看>>
windows服务 MVC之@Html.Raw()用法 文件流的读写 简单工厂和工厂模式对比
查看>>
PHP解析URL并得到URL中的参数
查看>>