|
原文链接:网络安全编程:Windows消息机制
Windows是一个庞大而复杂的操作系统,它提供了丰富而强大的功能,不但操作灵活方便,而且有众多的应用软件对其进行支持。Windows因有众多软件的支持,从而长期雄霸于PC系统。之所以有众多软件的支持,是因为Windows提供了良好的应用程序开发平台(接口)、完整的开发文档和各种优秀的开发环境。对于一个程序员来说,除了要掌握基本的开发语言以外,还要掌握具体的开发环境和系统平台的相关知识;在掌握编程语言和开发环境等知识后,还要掌握调试技术以及各种调试分析工具。同样,Windows操作系统提供了良好的调试接口,并且有非常多的调试工具。
大部分Windows应用程序都是基于消息机制的(命令行下的程序并不基于消息机制),熟悉Windows操作系统的消息机制是掌握Windows操作系统下编程的基础。
01 对消息的演示测试
在真正学习和认识消息之前,先来完成一个简单的任务,看看消息能完成什么样的工作。首先写一个简单的程序,通过编写的程序发送消息来关闭记事本的进程、获取窗口的标题和设置窗口的标题。
程序的具体代码如下:
- void CMsgTestDlg::OnClose()
- {
- // 在此处添加处理程序代码
- HWND hWnd = ::FindWindow("Notepad", NULL);
- if ( hWnd == NULL )
- {
- AfxMessageBox("没有找到记事本");
- return ;
- }
- ::SendMessage(hWnd, WM_CLOSE, NULL, NULL);
- }
- void CMsgTestDlg::OnExec()
- {
- // 在此处添加处理程序代码
- WinExec("notepad.exe", SW_SHOW);
- }
- void CMsgTestDlg::OnEditWnd()
- {
- // 在此处添加处理程序代码
- HWND hWnd = ::FindWindow(NULL, "无标题 - 记事本");
- if ( hWnd == NULL )
- {
- AfxMessageBox("没有找到记事本");
- return ;
- }
- char *pCaptionText = "消息测试";
- ::SendMessage(hWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)pCaptionText);
- }
- void CMsgTestDlg::OnGetWnd()
- {
- // 在此处添加处理程序代码
- HWND hWnd = ::FindWindow("Notepad", NULL);
- if ( hWnd == NULL )
- {
- AfxMessageBox("没有找到记事本");
- return ;
- }
- char pCaptionText[MAXBYTE] = { 0 };
- ::SendMessage(hWnd, WM_GETTEXT, (WPARAM)MAXBYTE, (LPARAM)pCaptionText);
- AfxMessageBox(pCaptionText);
- }
复制代码
编写的代码中有4个函数:第1个函数OnClose()是用来关闭记事本程序的;第2个函数OnExec()是用来打开记事本程序的,主要是测试其他3个函数时可以方便地打开记事本程序;第3个函数OnEditWnd()是用来修改记事本标题的;第4个函数OnGetWnd()是用来获取当前记事本标题的。程序的界面如图1所示。
图1 消息测试窗口
简单测试一下这个程序。首先单击“打开记事本程序”按钮,出现记事本的窗口(表示记事本程序被打开了);接着单击“修改记事本标题”按钮,可以发现记事本程序的窗口标题改变了;再单击“获取记事本标题”按钮,弹出记事本程序窗口标题的一个对话框;最后单击“关闭记事本程序”按钮,记事本程序被关闭。
02 对“消息测试”程序代码的解释
上面的代码中要学习的API函数有两个,分别是FindWindow()和SendMessage()。下面看一下它们在MSDN中的定义。
FindWindow()函数的定义如下:
- HWND FindWindow(
- LPCTSTR lpClassName,
- LPCTSTR lpWindowName
- );
复制代码
FindWindow()函数的功能是,通过指定的窗口类名(lpClassName)或窗口标题(lpWindowName)查找匹配的窗口并返回最上层的窗口句柄。简单理解就是,通过指定的窗口名(窗口名相对于窗口类来说要直观些,因此往往使用的是窗口名)返回窗口句柄。FindWindow()函数有两个参数,分别是 lpClassName 和 lpWindowName。通过前面的描述,该函数通常使用的是第2个参数 lpWindowName,该参数是指定窗口的名称。在例子代码中,为程序指定的窗口名是“无标题—记事本”。“无标题—记事本”是记事本程序打开后的默认窗口标题,当 FindWindow()找到该窗口时,会返回它的窗口句柄。例子代码中也使用了lpClassName(窗口类名),在窗口的名称会改变的情况下,只能通过窗口类名来获取窗口的句柄了。
当使用 FindWindow()函数获取窗口句柄时,指定窗口名是比较直观和容易的。但是,如果窗口名经常发生变化时,那么就不得不使用窗口类名了。
使用FindWindow()函数返回的窗口句柄是为了给SendMessage()函数来使用的。
SendMessage()函数的定义如下:
- LRESULT SendMessage(
- HWND hWnd,
- UINT Msg,
- WPARAM wParam,
- LPARAM lParam
- );
复制代码
该函数的作用是根据指定窗口句柄将消息发送给指定的窗口。该函数有4个参数,第1个参数hWnd是要接收消息的窗口的窗口句柄,第2个参数Msg是要发送消息的消息类型,第3个参数wParam和第4个参数lParam是消息的两个附加参数。第1个参数hWnd在前面已经介绍过了,该参数通过FindWindow()函数获取。
在程序的代码中,SendMessage()函数的第2个参数分别使用的了WM_CLOSE消息、WM_SETTEXT消息和WM_GETTEXT消息。下面来看这3个消息的具体含义。
WM_CLOSE:将 WM_CLOSE 消息发送后,接收到该消息的窗口或应用程序将要关闭。WM_CLOSE 消息没有需要的附加参数,因此 wParam 和 lParam 两个参数都为 NULL。
WM_SETTEXT:应用程序发送 WM_SETTEXT 消息对窗口的文本进行设置。该消息需要附加参数,wParam 参数未被使用,必须指定为 0 值,lParam 参数是一个指向以 NULL 为结尾的字符串的指针。
WM_GETTEXT:应用程序发送 WM_GETTEXT 消息,将对应窗口的文本复制到调用者的缓冲区中。该消息也需要附加参数,wParam 参数指定要复制的字符数数量,lParam 是接收文本的缓冲区。
例子代码在VC6下进行编译连接,生成可执行文件后,可以通过按钮的提示进行测试,以便大家感性认识消息的作用。
03 如何获取窗口的类名称
编写程序调用FindWindow()函数的时候,通常会使用其第2个参数,也就是窗口的标题。但是有些软件的窗口标题会根据不同的情况进行改变,那么程序中就不能在FindWindow()函数中直接通过窗口的标题来获得窗口的句柄了。而窗口的类名通常是不会变的,因此编程时可以指定窗口类名来调用FindWindow()函数以便获取窗口句柄。那么,如何能获取到窗口的类名称呢?这就是下面要介绍的第1个开发辅助工具——Spy++。
Spy++是微软Visual Studio中提供的一个非常实用的小工具,它可以显示系统的进程、窗口等之间的关系,可以提供窗口的各种信息,可以对系统指定的窗口进行消息的监控等。它的功能非常多,这里演示如何用它来获取窗口的类名称。
打开“开始”菜单,在Visual Studio的菜单路径下找到Spy++,打开Spy++窗口,如图2所示。
图2 “Microsoft Spy++”窗口
选择工具栏中的“Find Window”按钮,如图3所示。
图3 “Find Window”按钮
单击“Find Window”按钮,出现如图4所示的窗口。
图4 Find Window窗口
在图4中,用鼠标左键单击“Finder Tool”后面的图标,然后拖曳到指定的窗口上,会显示出“Handle”(窗口句柄)“Caption”(窗口标题)和“Class”(窗口类名),其中“Class”是编程时要使用的“窗口类”名称。
“Hide Spy++”是一个比较实用的功能,它用来隐藏 Spy++主窗口界面。选中该复选框后,拖曳“Finder Tool”后的图标时,图2所示为窗口将被隐藏。这个功能的实用之处在于,有些应用软件有反Spy++的功能,隐藏 Spy++ 主窗口有助于避免被反Spy++的软件检测到。为什么隐藏 Spy++的“Find Window”窗口会有反检测的功能,反检测的原理是什么?原理很简单,目标程序也是通过调用 FindWindow()函数来查找 Spy++窗口的,如果有该窗口,就进行一些相应的处理。
通过Spy++找到的窗口句柄是不能在编程中使用的,每次打开窗口时,窗口的句柄都会改变。
将“Finder Tool”后的图标拖曳到记事本的标题处,Spy++的Find Window窗口显示的内容如图5所示。
图5 获取到信息的Find Window窗口
从图5中可以得到记事本程序的标题和类名称。当编写程序调用FindWindow()函数,不能通过程序的标题文本得到窗口的句柄时,可以通过窗口类名称得到窗口的句柄。
微信公众号:计算机与网络安全
|
|