GetWindowLong说明及应用 <2>

“窗口类的封装,从全局窗口消息处理到窗口对象消息处理的映射方法:
     对界面进行封装,一般都是一个窗口一个类,比如实现一个最基本的窗口类CMyWnd,你一定会把窗口过程作为这个类的成员函数,但是使用WINAPI创建窗口时必须注册类WNDCLASS,里面有个成员数据lpfnWndProc需要WNDPROC的函数指针,一般想法就是把窗口类的消息处理函数指针传过去,但是类成员函数除非是静态的,否则无法转换到WNDPROC,而全局的消息处理函数又无法得到窗口类对象的指针。这里有几种解决办法:
    一种解决方法是用窗口列表,开一个结构数组,窗口类对象创建窗口的时候把窗口HWND和this指针放入数组,全局消息处理函数遍历数组,利用HWND找出this指针,然后定位到对象内部的消息处理函数。这种方法查找对象的时间会随着窗口个数的增多而增长。
    另一种方法比较聪明一点,WNDCLASS里面有个成员数据cbWndExtra一般是不用的,利用这点,注册类时给该成员数据赋值,这样窗口创建时系统会根据该值开辟一块内存与窗口绑定,这时把创建的窗口类的指针放到该块内存,那么在静态的窗口消息循环函数就能利用GetWindowLong(hWnd,GWL_USERDATA)取出该指针,return (CMyWnd*)->WindowProc(...),这样就不用遍历窗口了。但是这样一来就有个致命弱点,对窗口不能调用SetWindowLong(hWnd,GWL_USERDATA,数据),否则就会导致程序崩溃。幸好这个函数(特定这几个参数)是调用几率极低的,对于窗口,由于创建窗口都是调用窗口类的Create函数,不用手工注册WNDCLASS类,也就不会调用SetWindowLong函数。但是毕竟缺乏安全性,而且当一秒钟内处理的窗口消息很多时,这种查找速度也可能不够快。
 
创建窗口时:   SetWindowLong( m_hWnd, GWL_USERDATA, (LONG) this );
函数功能描述:  用这个函数能够获得指定窗口的信息 
函数原型:     LONG GetWindowLong( HWND hWnd,int nIndex )
参数:         hWnd:指定窗口的句柄; nIndex:需要获得的信息的类型
  nIndex取值如下:
  GWL_EXSTYLE    得到扩展的窗口风格
  GWL_STYLE      得到窗口风格
  GWL_WNDPROC    得到窗口回调函数的地址,或者句柄。得到后必须使用CallWindowProc函数来调用 
  GWL_HINSTANCE  得到应用程序运行实例的句柄
  GWL_HWNDPARENT 得到父窗口的句柄
  GWL_ID         得到窗口的标识符
  GWL_USERDATA   得到和窗口相关联的32位的值(每一个窗口都有一个有意留给创建窗口的应用程序是用的32位的值) 

  当hWnd标识一个对话框时可以使用下面的值:
  Value Action 
  DWL_DLGPROC   得到对话框回调函数的地址,或者句柄。得到后必须使用CallWindowProc函数来调用 
  DWL_MSGRESULT 得到对话框回调函数中消息处理过程的返回值
  DWL_USER      得到额外的应用程序私有信息,如一些句柄和指针等 
返回值:成功时,返回一个请求的32位的值;失败时,返回0,可以使用GetLastError来取得错误信息
示例:
long nStyle = ::GetWindowLong(hWnd, GWL_STYLE);   // hWnd是一个编辑框的句柄
if(nStyle & ES_PASSWORD)
{
    AfxMessageBox("这是一个密码域");
}

下面是一个具体的应用:
 

    星号密码查看工具大家都用过吧?现在我们自己来写一个超级简单的。密码框其实就是Windows的一个子窗口,显示星号是因为密码框设置了EM_SETPASSWORDCHAR属性,只要我们把密码框的EM_SETPASSWORDCHAR属性去掉,那么密码就会以明文显示了。我们可以给程序发送消息去掉EM_SETPASSWORDCHAR属性,通过安装鼠标钩子监视鼠标动作,如果用户单击的是密码框,那么就发送一个去除密码属性的消息。
    本文使用的编程工具为VC6.0,具体实现步骤和代码如下。

1)生成一个基于对话框的程序pass。打开passDlg.cpp,加入下面的全局变量和鼠标钩子函数。
HHOOK g_hHook = NULL;//全局钩子函数句柄
//鼠标钩子函数
LRESULT CALLBACK HookProc( int code, WPARAM wParam,LPARAM lParam )
{
HWND hwnd;
POINT point;
GetCursorPos(&point);
//得到鼠标位置
hwnd=::WindowFromPoint(point);
//得到包含鼠标的窗口句柄
long nStyle=::GetWindowLong(hwnd,GWL_STYLE);
//得到窗口风格

EVENTMSG *event=(EVENTMSG *)lParam;
if(event->message==WM_LBUTTONDOWN)
//是否为鼠标左键
{
if(nStyle & ES_PASSWORD)
//是否为密码框
{
::PostMessage(hwnd, EM_SETPASSWORDCHAR,0,0);
//去掉密码属性
}
}
return CallNextHookEx(g_hHook,code,wParam,lParam);
}

这里需要注意的是,程序中的::PostMessage(hwnd, EM_SETPASSWORDCHAR,0,0);只能是PostMessage,而不能用SendMessage代替。

2)添加“开始探测”按钮及响应函数OnOK(),并在函数中安装钩子。
void CPassDlg::OnOK() 
{
g_hHook=SetWindowsHookEx(WH_JOURNALRECORD,HookProc,GetModuleHandle(NULL),0);
//安装钩子
}

钩子的第三部分使用GetModuleHandle(NULL)函数,意为把自己作为保存钩子的DLL。
3)添加“取消退出”按钮及响应函数OnExit(),并在函数中卸载钩子。
void CPassDlg::OnExit() 
{

if(g_hHook)
UnhookWindowsHookEx(g_hHook);
//卸载钩子
exit(0);
}

    至此,整个程序就编写完成了。打开我们的程序,按下“开始探测”按钮,再打开需要输入密码的程序试试,是不是以明文显示了?用这个小程序可以搞定系统的密码设置、Outlook或防范不严的程序密码,但对有专门防范的程序就不行了,比如新版QQ。
 

posted @ 2013-11-06 11:31  陳さん様  阅读(270)  评论(0编辑  收藏  举报