1、前言
本文主要介绍了在C#编程中,使用Windows系统提供的API取操作Windows的窗口应用程序的一些方法。使用到的工具:spy++(基于VS),插件FlaUI.
2、思路简述
Windows系统为我们提供了可供调用的一些API,存放于诸如user32.dll等类似的文件中,我们可以使用这些dll文件中开放的接口从而去操作Windows的一些应用程序窗口。操作窗口的前提就是需要获取到这些窗口的信息,此处我使用工具spy++来直观的获取一些窗口的信息从而便于程序调用。关于FlaUI插件,在本文中仅作为实现模拟键鼠操作的工具,并没有做深入探讨。
3、工具介绍
Spy++ (SPYXX.EXE) 是一个基于 Win32 的实用工具,它提供系统的进程、线程、窗口和窗口消息的图形视图。使用 Spy++ 可以执行下列操作: 显示系统对象(包括进程、线程和窗口)之间关系的图形树。 搜索指定的窗口、线程、进程或消息。 查看选定的窗口、线程、进程或消息的属性。(来自百度百科)
如图,可以看到spy++可以将所有系统窗口一一展示出来,我们也可以对其进行一些查找等操作:
我们可以使用工具栏中第六个,望远镜标识的“查找窗口”操作(默认快捷键ALT+F3),来找到我们想要寻找的窗口:
通过手动拖动查找标识(箭头所指框中原有的圆形标识)到想要寻找的窗口,例如我这边是将其放置在浏览器窗口上,然后我们可以看到下面的介绍中给出了该窗口的一些基本信息,然后点击确定,就会在主页面定位到该窗口的位置,右键->属性,我们就可以看到这个窗口的详细信息.
这里面包含一些窗口的基本信息,诸如窗口标题、窗口句柄、窗口大小等,窗口栏还会显示子窗口以及父窗口信息,类一栏会展示类名等。
每一个应用程序在启动的时候会占用一个唯一的句柄,我们可以通过句柄去找到并操作这个窗口。窗口的类名是程序本身特有的,也可以根据类名锁定窗口。
窗口信息右键消息,可以看到这个窗口与系统之间的一些交互信息
点击记录选项,可以对想要查看的消息进行筛选:
在消息栏可以选中自己想要查看的消息类型进行展示,从而提取到想要的信息。
4、API介绍
在编程过程中,我们需要引入Windows的一些API才能够实现与窗口的交互行为,这些API封装在诸如user32.dll等文件中,我们在C#中引入这些文件就可以使用。
引入方式:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
这里以其中一个API为例表明了引入的方法,下面列举一些会用到的API的功能介绍:
1、IntPtr FindWindow(string lpClassName, string lpWindowName);根据类名或窗口标题获取窗口句柄,其中一个可以为null
2、bool GetWindowRect(IntPtr hWnd, out RECT lpRect);获取窗口的大小,输入窗口句柄,会将该窗口的大小输出到RECT对象中去
3、bool IsWindowVisible(IntPtr hWnd);判断一个句柄是否属于某个窗口
4、IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);根据窗口大小以及窗口句柄创建一个该窗口的图像,可以使用Image中的方法将该图像的一个句柄转换为可以操作的BitMap格式
5、IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);向hWnd窗口发送一条消息,消息内容一般都是操作数,例如关闭窗口WM_CLOSE = 0x0010,后续参数根据操作需求传入,可以为Zero
6、int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);根据窗口句柄获取窗口类名,输出到 lpCalssName
7、bool ShowWindow(IntPtr hWnd, int nCmdShow);将窗口设置为焦点
8、bool SetCursorPos(int x, int y);将鼠标移动到屏幕中指定的坐标
9、int ShellExecute(IntPtr hwnd, StringBuilder lpszOp, StringBuilder lpszFile, StringBuilder lpszParams, StringBuilder lpszDir, int FsShowCmd);对处在路径lpszDir的程序名lpszFile执行lpszOp操作
10、IntPtr CreateCompatibleDC(IntPtr hdc);创建一个绘图用的设备内存空间,通常与CreateCompatibleBitmap()一起使用
11、IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);将hgdiobj图像选入到hdc内存空间中
12、bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, int nFlags);将创建的图像打印到对应的设备内存
13、int DeleteDC(IntPtr hdc);删除开辟的内存空间句柄,释放资源
-------------
IntPtr:指的是句柄指针,在系统进程中,每个进程都会在开始运行的时候占用一个唯一的整数串,这个整数串称之为句柄,在C#中使用IntPtr这个结构体类型去表示句柄,它拥有指针的属性。
-------------
5、实现窗口截屏
下面以获取窗口截图为例,来统合上述操作。在截图的过程中,坐标我们都可以根据窗口拿到,如果说想要对这个截屏进行裁剪,我们可以预先在窗口中找到指定坐标(spy++展示鼠标消息然后移动到指定位置记录下坐标即可,spy++展示的位置是相对于整个窗口而言的,并非全屏幕),然后去进行对应的操作即可。
//获取窗口截屏
public static Bitmap GetShotCutImage(IntPtr hWnd)
{
var hscrdc = GetWindowDC(hWnd);
var windowRect = new Rectangle();
RECT rect;
//获取窗口坐标
GetWindowRect(hWnd, out rect);
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
//创建指定大小的窗口图像
var hbitmap = CreateCompatibleBitmap(hscrdc, width, height);
var hmemdc = CreateCompatibleDC(hscrdc);
//加载位图并绘制窗口
SelectObject(hmemdc, hbitmap);
PrintWindow(hWnd, hmemdc, 0);
var bmp = Image.FromHbitmap(hbitmap);
//释放资源
DeleteDC(hscrdc);
DeleteDC(hmemdc);
return bmp;
}
//对窗口进行裁剪
public static Bitmap PictureCut(Bitmap orginImage, int X, int Y, int width, int heigth)
{
string pictureName = "测试" + ProgramHelper.GenerateRandomNumber(6) + ".jpg";
//裁剪
Bitmap imageClone = (Bitmap)orginImage.Clone();
// 定义裁剪区域
Rectangle sourceRect = new Rectangle(X, Y, width, heigth); // 源图像的矩形区域
// 创建一个与裁剪区域大小相同的 Bitmap 对象
Bitmap croppedBitmap = new Bitmap(sourceRect.Width, sourceRect.Height);
// 创建一个与 croppedBitmap 相关联的 Graphics 对象
using (Graphics graphics = Graphics.FromImage(croppedBitmap))
{
// 将源图像的指定区域绘制到 croppedBitmap 上
graphics.DrawImage(imageClone, new Rectangle(0, 0, croppedBitmap.Width, croppedBitmap.Height), sourceRect, GraphicsUnit.Pixel);
}
return croppedBitmap;
}
//矩形结构体
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
public override string ToString()
{
return $"Left:{Left}, Top:{Top}, Right:{Right}, Bottom:{Bottom}";
}
}