GetWindowText背后不为人知的故事

GetWindowText背后不为人知的故事

作者:BlogUpdater |  时间:2020-01-09 |  浏览:687 |  评论已关闭 条评论

蝎子
GetWindowText可能比你想象的要更加复杂一些。MSDN上对它的描述尽量使用比较容易理解的文字来简化这一复杂性,如果你不想去阅读复杂的API文档的话,这是一种很好的做法。但是也有不好的地方:这意味着你无法知晓隐藏在它背后的故事。
今天就来给大家讲一讲。

Windows如何管理窗口文本
对于窗口类(Window Class)来说,有两种方法来管理窗口的文字。一种是手动进行管理,一种是交由系统代为管理。默认情况下,窗口类会使用系统代管的方式。
如果一个窗口类决定让系统帮助管理文字,则系统会帮助做如下的事情:
> WM_NCCREATE消息的默认处理例程会将传入到CreateWindow(Ex)中的lpWindowName保存到一个特殊的位置(一个独立的存储空间)。
> WM_GETTEXT消息的默认处理例程会从这一特殊位置获取它。
> WM_SETTEXT消息的默认处理例程会拷贝传入的字符串到这一特殊位置。

另一方面,如果窗口类决定自行手动管理文字,则系统将不会做任何特殊处理。在这种情况下,窗口类将需要自行处理WM_GETTEXT和WM_SETTEXT消息并返回/保存文字字符串。
框架窗口(Frame Window)一般会让系统管理它们的标题文字,而一些自定义控件通常会自行处理其控件文字。

GetWindowText背后的故事
GetWindowText这个函数有个问题:窗口的文字必须能不被阻塞的轻易被其他函数/组件读取。例如,FindWindow需要获取一个窗口的标题文字来进行窗口的查找。任务切换应用程序需要获取窗口的文字来标识被切换的窗口。从系统整体运行体验来说,一个被阻塞的应用程序不应该再阻塞系统中的其他应用,这个需求在任务切换场景下尤为必要。

通过发送WM_GETTEXT消息是不可行的,因为目标窗口可能处于挂起状态无法响应此消息。作为替代方案,GetWindowText需要尝试读取上文提到的特殊位置来确保调用不会受到挂起窗口的影响。
另一方面,GetWindowText被用来从控件或者对话框中获取文字,而有些控件大量使用到了自绘来手动管理窗口文字。

因此,GetWindowText做了一个妥协:

> 如果你尝试在自身进程的窗口中调用GetWindowText,则GetWindowText会发送WM_GETTEXT消息给目标窗口(你的窗口)。
> 如果你尝试对另外一个进程中的窗口调用GetWindowText,则GetWindowText将会使用特殊位置的字符串,而不是发送消息。

根据以上规则的第一条,如果你在自身进程中获取窗口文本,此时窗口挂起,那么GetWindowText同样也会阻塞。但由于这个是你自己的进程,调用阻塞说明你的应用程序有不合理的设计,所以这是你需要解决的问题。发送WM_GETTEXT消息可以确保从那些自行管理文字的窗口(自定义控件)中获取到窗口文字。

根据以上规则的第二条,如果你尝试获取另一个进程中的窗口文字,则GetWindowText不会发送WM_GETTEXT消息,它只会从特殊位置来获取文字字符串。因为通常获取窗口文字主要是用来获取框架窗口的标题,而框架窗口一般不会自绘标题栏,所以这个从特殊位置得到的字符串一般都是正确的。
MSDN中文档将这一底层工作机制简化为这样一句话:GetWindowText不能获取另一个应用程序中的窗口的文字。

如果我不想遵守这些规则呢?
如果你希望获取一个在另一个进程中的自绘窗口的文字,一个可行的方法是想目标窗口发送WM_GETTEXT消息。因为此时你并不是使用GetWindowText,所以你不会受到上面规则的限制。
但是请注意,如果目标窗口处于挂起状态,则你的调用也会阻塞,因为SendMessage会一直等到目标窗口返回响应为止。

另外请注意,因为WM_GETTEXT是一个系统消息(其值位于0到WM_USER-1之间),你不需要做任何形式的列集(Marshalling),实际上你也不应该这样做,因为USER模块会帮助自动完成这一过程。

能举个栗子吗?
考察下面的控件代码:

如果应用程序A执行以下函数创建窗口:
hwnd = CreateWindow(“Sample”, “Frappy”, …);

然后应用程序B使用某种方式获取到了A创建窗口的句柄,然后执行如下调用:
TCHAR szBuf[80];
GetWindowText(hwnd, szBuf, 80);

szBuf会得到”Frappy”的返回,因为它是借助系统的帮助从特殊位置获取到了这个字符串。
但是,如果使用下面的方法:
SendMessage(hwnd, WM_GETTEXT, 80, (LPARAM)szBuf);
szBuf将会返回: “Booga”。
亲爱的朋友,看到这其中的区别了吗?

总结
为了各种设计时目标,Windows开发团队在实现时做出很多Tradeoff,来确保用户和开发者得到最佳的使用体验。
一个字:真是太难了。

评论已关闭。