C#调用win32 api 操作其它窗口
实现以下功能:
- 找到窗体
- 找到控件(也叫子窗体)
- 获取内容
- 获取位置
- 设置
- 位置
- 内容
- 鼠标点击
示范
1. 找窗体
以操作系统自带的计算器为例
string clWindow = "CalcFrame"; //整个窗口的类名
string tlWindow = "计算器"; //窗口标题
IntPtr ParenthWnd = FindWindow(clWindow, tlWindow);
这样就得到了窗口的句柄 ParenthWnd ,如果 ParenthWnd==IntPtr.Zero 说明没有找到窗体;
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
参数 lpClassName,lpWindowName 可以只填写一个,但另一个需要填写: null 而不是 :"" (即 string.Empty)。
如果同时开了几个同样的窗口,比如启动了2个 计算器, 那返回的句柄是哪个的呢 ? 答案是:最顶层的那个。
1、如果计算器2界面,在计算器1界面的前面,那么会返回计算器2的句柄。 2、如果两个计算器界面是并排的,即不存在重叠(包括部分重叠), 先切换计算器1,然后切换计算器
2,然后切换别的程序,会返回计算器2的句柄,反之返回计算器1的句柄。
这个问题说的可能是Windows系统界面里某个名词的概念,如顶层窗口,Z次序,活动窗口。但我不是特别明白,姑且举例说明实际情况了。
2. 找控件,获取内容,获取位置
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string lpszClass, string lpszWindow);
这个函数使用起来比较麻烦,hwndParent 指的是目标控件的直接上级的句柄,有时候你知道窗口的句柄还不行,还需要知道之间的层级关系,逐层查找才行。这里举例说明一下怎么用 :
FindWindowEx(ParenthWnd, IntPtr.Zero, "TPanel", "打印预览");
我想说的是:参数 hwndChildAfter 可以写 IntPtr.Zero,lpszClass和lpszWindow 可以为null 。
我一般是用枚举的方法:枚举得到这个窗口的所有句柄列表,然后筛选这个列表,从而得到想要的句柄。 上代码:
[DllImport("user32.dll", ExactSpelling = true)]
public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, int lParam);
[DllImport("user32.dll")]
public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
public static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
public struct WindowInfo
{
public IntPtr hWnd;//句柄
public string szWindowName;//窗口名
public string szClassName;//类名
public System.Drawing.Rectangle Rect;//位置大小信息
}
List<WindowInfo> EnumChildWindowsCallback(IntPtr handle)
{
//用于保存句柄列表
List<WindowInfo> wndList = new List<WindowInfo>();
win32Api.EnumChildWindows(handle, delegate (IntPtr hWnd, int lParam)
{
WindowInfo wnd = new WindowInfo();
StringBuilder sb = new StringBuilder(256);
//get hwnd
wnd.hWnd = hWnd;
//get window name
GetWindowTextW(hWnd, sb, sb.Capacity);
wnd.szWindowName = sb.ToString();
//get window class
GetClassNameW(hWnd, sb, sb.Capacity);
wnd.szClassName = sb.ToString();
RECT rect = new RECT();
if (GetWindowRect(hWnd, ref rect) == true)
wnd.Rect = rect.ToRectangle();
wndList.Add(wnd);
return true;
}, 0);
//这里已经得到句柄列表 “wndList” ,对wndList进行筛选即可。
return wndList;
}
3. 设置内容,位置
设置窗口标题,文本框内容:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
例子 const int WMSETTEXT = 0x000C; SendMessage(ParenthWnd, WMSETTEXT, IntPtr.Zero, "哥要你改标题");
设置窗口位置,大小:
[DllImport("User32.dll", EntryPoint = "MoveWindow")]
public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
例子: MoveWindow(hWnd, x, y, nWidth, nHeight, true);
4. 鼠标点击
方法1 :给控件发送消息
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
找到控件句柄后,这样就可以了
const int WM_CLICK = 0x00F5;
SendMessage(hWnd, WM_CLICK, IntPtr.Zero, IntPtr.Zero);
方法2:移动鼠标位置,让鼠标点击
[DllImport("user32.dll")]
public static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
[Flags]
public enum MouseEventFlags : uint
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
Absolute = 0x8000
}
得到窗口的位置后,根据要点击的目标点相对窗口的位置,设置点击位置,如
mouse_event((int)MouseEventFlags.LeftDown + (int)MouseEventFlags.LeftUp, windowRect.Left + 455, windowRect.Top + 40, 0, 0);
当然前提是,窗口是顶层的,鼠标位置在你想点的位置。可以通过这样的方法实现
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
实际例子: win32Api.SetForegroundWindow(ParenthWnd); win32Api.SetCursorPos(windowRect.Left + 455, windowRect.Top + 40); 然后再调用 mouse_event
这两种点击方法的对比:
方法2使用面更广,如对于找不到句柄的按钮(原理基本上是窗体启动后,动态画上去的,另外wpf控件用spy++也是 找不到句柄的) 也可以采用这种方式。
另外也不需要知道控件的句柄,知道相对窗口或别的控件的位置即可。
但是方法2的缺点也是很明显的:
需要把目标窗口设置到顶层
设置鼠标位置后,用户的鼠标移动操作,会造成干扰。如果执行 mouse_event 可能点到错误的位置。
(by xrcdev http://www.cnblogs.com/xrcdev/)
参考:
http://www.cnblogs.com/zhuiyi/archive/2012/07/09/2583024.html
C#调用win32 api 操作其它窗口的更多相关文章
- MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API(转)
转自:http://www.cnblogs.com/Yahong111/archive/2007/08/16/857574.html 续上文[翻译]MSIL 教程(一) ,本文继续讲解数组.分支.循环 ...
- MSComm控件与Win32 API操作串口有何区别?
MSComm控件与Win32 API操作串口有何区别? [问题点数:50分,结帖人shell_shell] 收藏帖子 回复 我是一个小兵,在战场上拼命! 结帖率 83.33% 我以前用MSCo ...
- 用C#调用Windows API向指定窗口发送按键消息 z
用C#调用Windows API向指定窗口发送 一.调用Windows API. C#下调用Windows API方法如下: 1.引入命名空间:using System.Runtime.Interop ...
- Python调用win32 API绘制正弦波
Python调用win32 API新建窗口与直接创建窗口的流程相同 流程:注册窗口→创建窗口→显示窗口→更新窗口→消息循环 代码: # -*- coding: utf-8 -*- import win ...
- C#调用Win32 api时的内存操作
一般情况下,C#与Win 32 Api的互操作都表现的很一致:值类型传递结构体,一维.二维指针传递IntPtr.在Win32 分配内存时,可以通过IntPtr以类似移动指针的方式读取内存.通过IntP ...
- 【转】用C#调用Windows API向指定窗口发送
一.调用Windows API. C#下调用Windows API方法如下: 1.引入命名空间:using System.Runtime.InteropServices; 2.引用需要使用的方法,格式 ...
- 用C#调用Windows API向指定窗口发送按键消息
一.调用Windows API. C#下调用Windows API方法如下: 1.引入命名空间:using System.Runtime.InteropServices; 2.引用需要使用的方法,格式 ...
- C#调用Win32 api学习总结
从.NET平台调用Win32 API Win32 API可以直接控制Microsoft Windows的核心,因为API(Application Programming Interface)本来就是微 ...
- nodejs 调用win32 api
video 教程文件 win32 api >node -v v12.16.1 >npm install -g node-gyp >npm i @saleae/ffi >node ...
随机推荐
- SQLServer------将表和内容导入到另一个数据库
转载: http://jingyan.baidu.com/article/d5c4b52bc5c102da570dc547.html
- Linux学习(CentOS-7)---磁盘分区(概念、分区方法、分区方案)
1磁盘分区相关的概念 1.1什么是磁盘 磁盘就是计算机的外部存储器设备,即将圆形的磁性盘片装在一个方的密封盒子里,这样做的目的是为了防止磁盘表面划伤,导致数据丢失.简单地讲,就是一种计算机信息载体,也 ...
- 搭建NFS服务器
1:yum install -y nfs-utils-* portmap-* 2:NFS安装完毕,需要创建共享目录,共享目录在vi /etc/exports文件里面配置,可配置参数如下: /data/ ...
- Spring MVC学习笔记——SiteMesh的使用(转)
转自 SiteMesh的使用 SiteMesh的介绍就不多说了,主要是用来统一页面风格,减少重复编码的. 它定义了一个过滤器,然后把页面都加上统一的头部和底部. 需要先在WEB-INF/lib下引入s ...
- npm 发布到远程资源库
一.npm 发布到远程资源库 1.创建package.json Package.json 属性说明 name - 包名. version - 包的版本号. description - 包的描述. ho ...
- NOSDK--一键打包的实现(五)
1.5 mac下的脚本环境配置及脚本的使用 脚本环境主要依赖sdk,ndk和ant,前两个可以在android官方网站下载(被墙了的话只能FQ了). android-sdk-macosx androi ...
- 关于Block的简单使用
Block在整个iOS开发中无所不见,很重要,很重要,文本在这里block的简单使用介绍.我们可以简单地定义.使用block. 1. Block和C的指针函数很像,但比C的函数灵活多了.废话了.... ...
- Java中Runnable和Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
- 使用Impersonation仿冒用户运行WCF 服务方法
默认情况下,当我们把wcf服务器部署在IIS上时, 我们访问wcf时使用的是IIS内建的内建角色,对于IIS8.0,它被命名为ApplicationPoolIdentity, 一般来说,系统为每个应用 ...
- nuget的搭建及多源冲突
为什么使用nuget来管理类库引用就不再阐述,好处真的一抓一把.在使用nuget的时候,我们如果总去访问别人的nuget源,受限于网络情况的好坏,速度真的没法保证,更别说访问国外的源了.那好,我们来自 ...