测试工具,首先也是一个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));

  

C#实现对外部程序的调用操作的更多相关文章

  1. 如何通过JAVA让DB2调用操作系统命令

    引言:我们在工作中常用操作系统命令和DB2命令对数据库做数据的导入.导出等操作,但是DB2不支持复合SQL 语句调用操作系统命令,因此我们需要利用 UDF 来执行SQL 中不可用的操作(例如:执行一些 ...

  2. Util应用程序框架公共操作类(七):Lambda表达式公共操作类

    前一篇扩展了两个常用验证方法,本文将封装两个Lambda表达式操作,用来为下一篇的查询扩展服务. Lambda表达式是一种简洁的匿名函数语法,可以用它将方法作为委托参数传递.在Linq中,大量使用La ...

  3. [AIR] AIR 应用程序的调用和终止

    本节讨论几种对已安装的 Adobe® AIR® 应用程序进行调用的方法,以及关闭运行中的应用程序的选项和注意事项. 注: NativeApplication.InvokeEvent 和 Browser ...

  4. 发布在IIS上的Web程序,调用服务器的COM组件

    场景大致是这样的,在工厂中分布着许多的PDA点,这些PDA点都要进行实时的扫描--打印操作.实现方法是采用网络打印机,然后服务器安装驱动,管理着所有的打印机.然后服务器,发布一个WebService, ...

  5. Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

    文章目录:                   1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Ap ...

  6. Util应用程序框架公共操作类

    随笔分类 - Util应用程序框架公共操作类 Util应用程序框架公共操作类 Util应用程序框架公共操作类(五):异常公共操作类 摘要: 任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务 ...

  7. C++程序中调用其他exe可执行文件方法

    在编程过程中有个需求,点击某个按钮需要弹出系统的声音控制面板.在网上查了下代码中调用其他exe程序或者打开其他文件的方法. 自己借鉴网上的文章稍微总结下,加深下印象,也给方便自己用. 在代码中调用其他 ...

  8. Delphi XE7的安卓程序如何调用JAVA的JAR,使用JAVA的类?

    本文使用工具和全部源码下载: http://download.csdn.net/detail/sunylat/8190765 为什么我们要在Delphi XE7的安卓程序调用JAVA的JAR,使用JA ...

  9. 利用 gnuplot_i 在你的 c 程序中调用 GNUPLOT

    这是一篇非常早曾经写的小文章,最初发表于我的搜狐博客(2008-09-23 22:55).由于自从转移到这里后,sohu 博客就不再维护了,所以把这篇文章也一起挪了过来. GNUPLOT 是一款功能强 ...

随机推荐

  1. Android 使用SwipeBackLayout实现滑动返回上一级页面——实战来袭

    我们知道.APP在设计上习惯性的把返回button放在屏幕的左上角,那么,在非常多时候(尤其是大屏幕手机),操作改返回button,就会有诸多不便了.为了更加方便实现"返回"功能. ...

  2. 利用NIO的Selector处理服务器-客户端模型

    package NIOTEST; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocket ...

  3. SQL之经典语句

    一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...

  4. PowerBuilder--Aes128加解密

    通过C#开发Com控件,注册到系统,然后由pb通过OLEObject进行调用 原文:https://www.cnblogs.com/eric_ibm/archive/2012/07/06/dll.ht ...

  5. Redis加锁与解锁

    Redis加锁 customerM = BaseMemCached.setMLock(customerId); /** * 个人账户表加锁 **/ public static CustomerM se ...

  6. 安装android Studio和运行react native项目(跳坑篇)

    1.需配环境变量,值为sdk的地址. ANDROID_HOME  值:E:\Users\HP\AppData\Local\Android\sdk 2.下载gradle-2.14.1-all.zip 包 ...

  7. Java线程面试题:子线程先运行 2 次,然后主线程运行 4 次,如此反复运行 3 次

    package thread; /** * 需求:线程编程:子线程先运行 2 次,然后主线程运行 4 次,如此反复运行 3 次. * @author zhongfg * @date 2015-06-1 ...

  8. linux关机前同步数据(sync)

    sync,将内存中未更新的数据写入硬盘中.

  9. Mark指针的指针(**)和链表使用(*&)

    利用二级指针删除单向链表 彻底理解链表中为何使用指针的指针或者指针的引用 详解C++指针的指针和指针的引用

  10. 九度OJ 1252:回文子串 (字符串处理、DP)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:387 解决:224 题目描述: 输入一个字符串,输出该字符串中对称的子字符串的最大长度. 比如输入字符串"google" ...