测试工具,首先也是一个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. Flask--上下文源码流程

  2. [转]Ubuntu Server命令行更换软件源

    sucd /etc/aptwget http://mirrors.163.com/.help/sources.list.lucidmv sources.list sources.list.backup ...

  3. 《Lucene in Action》(第二版) 第一章节的学习总结 ---- 用最少的代码创建索引和搜索

    第一章节是介绍性质,但是通过这一章节的学习,我理解到如下概念: 1.Lucene由两部分组成:索引和搜索.索引是通过对原始数据的解析,形成索引的过程:而搜索则是针对用户输入的查找要求,从索引中找到匹配 ...

  4. EJB是什么?(节选)

    近期的项目中使用了EJB.当时就仅仅知道怎么用,没有深入的去理解.当完毕这个项目之后.再回想项目中使用的技术.框架以及工具的时候,突然感觉对EJB这个概念非常是模糊,于是上网搜一些资料.可是,非常多的 ...

  5. DAO调用存储过程问题

    相关文章:1.使用 Spring 框架调用 DB2 存储过程   2.Spring如何使用JdbcTemplate调用存储过程的三种情况   3.spring中调用存储过程,函数

  6. 【Mac系统】之fiddler下载和安装

    使用教程参考:http://www.cnblogs.com/TankXiao/archive/2012/02/06/2337728.html#request 一.首先,在Mac下安装fiddler时, ...

  7. shiro自定义logout filter

    虽然shiro有自己默认的logout过滤器,但是,有些时候,我们需要自己定义一下操作,比如说loutgout后,进入指定页面,或者logout后写入日志操作,这个时候,我们可以通过自定义logout ...

  8. rebound是facebook的开源动画库

    网址:http://www.jcodecraeer.com/a/opensource/2015/0121/2338.html 介绍: rebound是facebook的开源动画库.可以认为这个动画库是 ...

  9. iphone怎么检测屏幕是否被点亮 (用UIApplication的Delegate)

    本文转载至  http://gaohaijun.blog.163.com/blog/static/176698271201161524857373/     问题:那位能说一下怎么能检测到iphone ...

  10. spring BeanFactory加载xml配置文件示例

    项目目录结构如下: HelloWorld.java package com.thief.demo; public class HelloWorld { public void sayHello() { ...