理解什么是句柄?
对于“句柄”,之前一直停留在一知半解的认识层面,也说不清具体概念,只知道它是一个标识符,用来标记对象或者说某个东西的。只知其名不知其意。目前学习windows编程,对“句柄”做一个完整的认。
现在先看一张图(这张图是从其他博主博文扒下来的):
图1是程序运行到某时刻时的内存快照,图2是程序往后运行到另一时刻时的内存快照。红色部分标出了两次的变化。
【解释一下:】
windows是一个以虚拟内存为基础的操作系统,很多时候,进程的代码和数据并不是全部装入内存,进程某一段装入内存之后,可能会被换出到外存。当再次需要时会重新装入内存。两次装入内存,那么很多时候装入的地址是不一样的。也就是说同一个对象在内存中的地址会变化。那么程序怎么才能准确的访问到对象呢?这时候就引入了句柄。
系统为每个进程在内存中分配一定的区域,用来存放各个句柄,即一个个32位无符号整型值(32位操作系统中)。每个32位无符号整型值相当于一个指针,指向内存中的另一个区域(我们不妨称之为区域A)。而区域A中存放的正是对象在内存中的地址。当对象在内存中的位置发生变化时,区域A的值被更新,变为当前时刻对象在内存中的地址,而在这个过程中,区域A的位置以及对应句柄的值是不发生变化的。这种机制,用一种形象的说法可以表述为:有一个固定的地址(句柄),指向一个固定的位置(区域A),而区域A中的值可以动态地变化,它时刻记录着当前时刻对象在内存中的地址。这样,无论对象的位置在内存中如何变化,只要我们掌握了句柄的值,就可以找到区域A,进而找到该对象。而句柄的值在程序本次运行期间是绝对不变的,我们(即系统)当然可以掌握它。这就是以不变应万变,按图索骥,顺藤摸瓜。
所以,我们可以这样理解句柄:
- 数值上:是一个32位的unsigned int;
- 逻辑上:相当于一个二级指针。指向对象在内存中的地址。(理解上:这个指针的值不可变的,普通指针的值可以变得。)
- 作用上:是windwos使用句柄来标识诸多资源或者对象。比如窗口,画笔,进程,线程等等。
【回到开头,windows下我们如何拿到一个窗口句柄】:
使用FindWindow函数
可以根据窗口的类名和窗口名拿到句柄
HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName);
//IpClassName :指向一个指定了类名的空结束字符串
//pWindowName:指向一个指定了窗口名(窗口标题)的空结束字符串。如果该参数为空,则为所有窗口全匹配。
//返回值:如果函数成功,返回值为具有指定类名和窗口名的窗口句柄;如果函数失败,返回值为NULL。
[那么我们如何获取到窗口的 类名 和 窗口名呢?]
我们可以使用vs自带的工具spy++.exe;
通过搜索–>查找窗口,拖到要查询的窗口就行。
现在,我们写一个简单的例子:用记事本打开一个文件。我这里打开的是cfg.ini文件。然后找到记事本窗口,然后通过查找到的记事本窗口句柄将记事本窗口进行移动位置并且改变大小。
//根据类名和窗口名查找窗口句柄
HWND GetWindowHandle(std::string className, std::string windowName)
{
HWND hwnd = FindWindowA(className.c_str(), windowName.c_str());
return hwnd;
}
int main()
{
HWND notePadWHND = GetWindowHandle("Notepad", "cfg.ini - 记事本");
if (notePadWHND) {
RECT rect;
GetWindowRect(notePadWHND, &rect); //获取记事本窗口的位置
MoveWindow(notePadWHND, rect.left + 500, rect.top + 300, 300, 300, TRUE);//移动记事本窗口
}
return 0;
}