Lesson20 Hook与数据编程

通过安装Hook过程,可以用来屏蔽消息队列中某些消息

The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.

 

Syntax

 

HHOOK SetWindowsHookEx(

int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId   //为零表示和所有安装的线程相关

);

 

一、下面我们来创建一个屏蔽鼠标过程的hook:

1.创建基于MFC的一个InnerHook工程项目

2.BOOL CInnerHookerDlg::OnInitDialog()中添加hook

SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());

要获得当前线程句柄,使用函数DWORD GetCurrentThreadId(void);

3.实现鼠标过程MouseProc为:

 

LRESULT CALLBACK MouseProc(

 

 

int nCode,

WPARAM wParam,

LPARAM lParam

)

{

return 1; //返回值为一表示屏蔽鼠标过程

}

 

二、如果要屏蔽键盘消息,可以添加如下代码

1.CPP文件中添加一个变量:HHOOK g_hKeyBoard;

2.CInnerHookerDlg::OnInitDialog()中添加hook

g_hKeyBoard=SetWindowsHookEx(WH_KEYBOARD,KeyBoardProc,NULL,GetCurrentThreadId());

 

3.实现键盘过程keybroadProc为(只屏蔽空格键):

 

4.下边添加代码使程序在F2键按下后退出。

要关闭窗口,首先要获得窗口的句柄,先声明一个全局变量Hwnd g_hWnd

OnInitDialog()中把窗口句柄传给它:

g_hWnd=m_hWnd;

接下来为键盘钩子过程添加代码:

 

这时我们只能屏蔽主线程的键盘消息,如果要屏蔽所有消息,就得把代码放到动态链接库中实现。

三、屏蔽所有线程的消息

首先要创建一个动态链接库

1.新建一个Win32 Dynamic-Link Library项目工程Hook

 得到动态链接库模块的句柄有两种方式:

     方法1DllMain函数方式:

 

HMODULEHINSTANCE可以通用

     方法2GetModuleHandle函数方式

SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("Hook"),0);

 

这样我们所安装的钩子过程就和运行在同一个桌面上的所有进程相关了

2.编写MouseProc()

 

3.之后新建一个模块文件Hook.def,添加代码:

LIBRARY Hook

EXPORTS

SetHook @2   //@2用来指定序号。

4.编译生成dll文件

 

接下来新建一个工程,用来测试刚才的DLL

    首先安装一个鼠标Hook屏蔽所有的鼠标消息。

1.新建一个基于MFC对话框的项目工程HookTest

2.BOOL CHookTestDlg::OnInitDialog()前声明SetHook函数

_declspec(dllimport) void SetHook();

3.Setting对话框的Link选项卡的,添加库文件:..\Hook\Debug\Hook.lib

4.OnInitDialog()中调用 SetHook();

5.将生成好的动态链接库拷贝到测试程序项目工程目录下面。

 

调试运行,你会发现你的鼠标坏了,所有的鼠标操作都被屏蔽了。

 

 

    然后安装一个键盘Hook,我们可以按照刚才所做键盘Hook的过程在动态链接库中也做一个Hook

    这是需要给SetHook带上参数HWND hwnd.

    在测试程序中要把函数也带上参数,并给SetHook传入窗口句柄 SetHook(m_hWnd)

 

 

    接着,让程序窗口始终在其他窗口之前,而且将它最大化,从而使用户不能切换到窗口。

可以使用SetWindowPos函数

BOOL SetWindowPos(

HWND hWndInsertAfter,

int X,

int Y,

int cx,

int cy,

UINT uFlags

);

A window can be moved to the top of the Z-order either by setting the pWndInsertAfter parameter to &wndTopMost and ensuring that the SWP_NOZORDER flag is not set or by setting a window’s Z-order so that it is above any existing topmost windows. When a nontopmost window is made topmost, its owned windows are also made topmost. Its owners are not changed.

 

得到窗口的大小,可以使用函数GetSystemMetrics

int GetSystemMetrics(int nIndex);

代码:

int cxScreen,cyScreen;

cxScreen=GetSystemMetrics(SM_CXSCREEN);

cyScreen=GetSystemMetrics(SM_CYSCREEN);

SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);

 

SetHook(m_hWnd);

因为第一个参数设置为&wndTopMost,这时程序始终处于顶层窗口,

不管怎样切换窗口,我们的窗口显示在最前面。

 

 

 

四、如何实现在切换到其他线程时,也能响应F2退出程序

在程序中,我们屏蔽了鼠标和键盘,但是我们留下了一个退出程序的后门(F2)。

前面讲过动态链接库共享性的原理,多个进程可以共享同一份代码与数据页,

按道理切换到其它线程之后,按下F2应该也可以退出程序才对,

但是发现当切换到其他程序后,再按F2 程序不会退出,

这是因为系统的页面拷贝机制,如果系统发现被某线程要修改某个数据页面,

它就会先拷贝一份页面数据,再对新的页面数据进行修改,

其它没有更新数据的线程继续使用旧的页面数据。

比如:SetHook(HWND hwnd)中将形参传递给了一个全局变量g_hWnd

,那么调用SetHook的线程将使用新的存放了hwnd的数据页面,

而其它的线程继续使用旧的数据页面,所以在其它线程成为活动窗口的时候,

按下F2时,因为没有g_hWnd没有传递到hwnd窗口,所以按下F2没有反应。

 

我们可以通过创建一个新的节,将全局变量放到这个节当中,然后将这个节设置为一个共享的节,

这样全局变量就可以在多个线程间共享,从而使切换到其他线程时也能按下F2退出程序。

要显示动态链接库的节,可以使用命令行: dumpbin -headers Hook.dll

 

如何创建一个新的节?

如果确实想在其他程序窗口下关闭我们的程序窗口,可以把共享窗口句柄,使系统不再进行页面拷贝,方法是使用下面语句把窗口句柄设为共享:

 

#pragma data_seg("MySec")//MySec是新创建的节的名字(不能超过8个字节)

HWND g_hWnd=NULL; //新变量必须初始化,否则没有新建节的信息

#pragma data_seg()   //以上为新建节

 

新创建的节共享以后才有效,共享节有两种方法:

1.#pragma comment(linker,"/section:MySec,RWS") //设置节的属性,读,写,共享

2.也可以把#pragma comment(linker,"/section:MySec,RWS")省略。

Hook.def中添加如下代码:

SEGMENTS

MySec read write shared

也能对节的属性进行设置

 

SetWindowsHookEx函数的第一个参数设为WH_GETMESSAGE,能够破解密码。

使用Hook时要小心。

 

1.Hook简介:作用是拦截某些消息,关键函数是SetWindowsHookEX()

2.示例程序:

1.新建一基于对话框工程,InnerHook,此过程的钩子是只拦截本进程的。

2.OnInitDialog()中添加代码:

   g_hWnd=m_hWnd;

g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());设置了鼠标钩子

g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId());设置了键盘钩子

3.完成钩子函数的编写:

HHOOK g_hKeyboard=NULL;

HHOOK g_hMouse;

HWND g_hWnd=NULL;LRESULT CALLBACK MouseProc(

   int nCode,      // hook code

   WPARAM wParam, // message identifier

   LPARAM lParam   // mouse coordinates

)

{

return 1;

}LRESULT CALLBACK KeyboardProc(

   int code,       // hook code

   WPARAM wParam, // virtual-key code

   LPARAM lParam   // keystroke-message information

)

{

//if(VK_SPACE==wParam || VK_RETURN==wParam)如果是空格键

/*if(VK_F4==wParam && (1==(lParam>>29 & 1)))拦截ALT+F4按键!

   return 1;

else

   return CallNextHookEx(g_hKeyboard,code,wParam,lParam);*/

if(VK_F2==wParam)F2时程序可以退出,这是留的后门。否则程序无法关闭,只能用任务管理器来关闭它了。

{

   ::SendMessage(g_hWnd,WM_CLOSE,0,0);

   UnhookWindowsHookEx(g_hKeyboard);当程序退出时最好将钩子移除。

   UnhookWindowsHookEx(g_hMouse);

}

return 1;

}

3.编写一个屏屏蔽所有进程和所有线程的钩子程序。耸闭飧龉匙颖匦氚沧霸贒LL中,然后被某个程序调用才行。

1.新建一个DLL工程名为Hook

2.增加Hook.cpp

3.代码如下:

#include <windows.h>包含头文件HHOOK g_hMouse=NULL;

HHOOK g_hKeyboard=NULL;#pragma data_seg("MySec")新建了一个节,用于将下 面的这个变量设为全局共享。

HWND g_hWnd=NULL;这个变量是全局共享的。

#pragma data_seg()//#pragma comment(linker,"/section:MySec,RWS")

/*HINSTANCE g_hInst;BOOL WINAPI DllMain(

   HINSTANCE hinstDLL, // handle to the DLL module

   DWORD fdwReason,     // reason for calling function

   LPVOID lpvReserved   // reserved

)

{

g_hInst=hinstDLL;

}*/LRESULT CALLBACK MouseProc(

   int nCode,      // hook code

   WPARAM wParam, // message identifier

   LPARAM lParam   // mouse coordinates

)

{

return 1;拦截了鼠标消息。

}LRESULT CALLBACK KeyboardProc(

   int code,       // hook code

   WPARAM wParam, // virtual-key code

   LPARAM lParam   // keystroke-message information

)

{

if(VK_F2==wParam)如果是F2键,则退出。

{

   SendMessage(g_hWnd,WM_CLOSE,0,0);

   UnhookWindowsHookEx(g_hMouse);当退出时将钩子卸掉。

   UnhookWindowsHookEx(g_hKeyboard);

}

return 1;

}void SetHook(HWND hwnd)此函数设置了钩子。

{

g_hWnd=hwnd;注意这种传递调用它的进程的句柄的方法,比较巧妙!

g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("Hook"),0);

g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,GetModuleHandle("Hook"),0);

}Hook.DEF的代码如下:

LIBRARY Hook

EXPORTS

SetHook @2

SEGMENTS

MySec READ WRITE SHARED 也可以设置节的属性。

    4.新建一个工程调用此钩子函数。工程名为HookTest,基于对话框的。在OnInitDialog()中调用SetHook(),要事先声明_declspec(dllimport) void SetHook(HWND hwnd);

      然后在Project->Setting->Link->加入..\Hook\Debug\Hook.lib,并将Hook.Dll拷贝到当前目录。

int cxScreen,cyScreen;

cxScreen=GetSystemMetrics(SM_CXSCREEN);

cyScreen=GetSystemMetrics(SM_CYSCREEN);

SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);将窗口保持在最前面。

SetHook(m_hWnd);

    5.DLL的调试方法,设置断点,然后运行时断点时,step into即可。

4.数据库编程

1.ODBCADO简介:ADO可以认为是建立在ODBC上的。

   ADO的三个核心对象

Connection对象

   Connection对象表示了到数据库的连接,它管理应用程序和数据库之间的通信。 RecordsetCommand对象都有一个ActiveConnection属性,该属性用来引用Connection对象。

Command对象

   Command对象被用来处理重复执行的查询,或处理需要检查在存储过程调用中的输出或返回参数的值的查询。

Recordset对象

   Recordset对象被用来获取数据。 Recordset对象存放查询的结果,这些结果由数据的行(称为记录)和列(称为字段)组成。每一列都存放在RecordsetFields集合中的一个Field对象中。

2.演示在VB中使用ADO的方法,方法比较简单,使用方便。另外在VB中演示了ConnectionCommandRecordset的方法,用这三种方法都可以执行SQL语句。

3.VC中利用ADO访问数据库。

    1.新建一个基于对话框的工程,名为ADO

    2.在对话框中放一ListBox和一个Button控件。

    3.在使用时须导入MSADO15.dll,方法是在StdAfx.h#import "D:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename("EOF","rsEOF")

    至少于将EOF改名为rsEOF,是为了避免与文件中的EOF重名。然后编译程序,将产生的debug目录下的两个文件MSADO15.tlhMSADO15.tli加到工程中,其目的只是方便我们查看而已。并不是编译需要它。

    ADO也是COM组件,须初始化COM库方法是CoInitialize(NULL);使用完后须CoUninitialize();

    代码如下:

    void CAdoDlg::OnBtnQuery()

{

// TOD Add your control notification handler code here

CoInitialize(NULL);初始化

_ConnectionPtr pConn(__uuidof(Connection));产生connection智能指针

_RecordsetPtr pRst(__uuidof(Recordset));产生recordset智能指针

_CommandPtr pCmd(__uuidof(Command));产生command智能指针pConn->ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=pubs";数据库信息

pConn->Open("","","",adConnectUnspecified);打开数据库//pRst=pConn->Execute("select * from authors",NULL,adCmdText);用记录集查询数据

//pRst->Open("select * from authors",_variant_t((IDispatch*)pConn),

// adOpenDynamic,adLockOptimistic,adCmdText);

pCmd->put_ActiveConnection(_variant_t((IDispatch*)pConn));

pCmd->CommandText="select * from authors";用这种方法也可以查询数据

pRst=pCmd->Execute(NULL,NULL,adCmdText);

while(!pRst->rsEOF)将查询到的数据加到列表框咯。

{

((CListBox*)GetDlgItem(IDC_LIST1))->AddString(

   (_bstr_t)pRst->GetCollect("au_lname"));

pRst->MoveNext();

}pRst->Close();

pConn->Close();

pCmd.Release();

pRst.Release();

pConn.Release();

CoUninitialize();

}

 

 

本文来自CSDN博客http://blog.csdn.net/huahuamoon/archive/2007/12/29/2002855.aspx

数据库编程:

 

COM

[] 小型可执行程序的扩展名, 串行通讯端口

[] Commercial organizations,商业组织,公司

The Component Object Model组件对象模型

ADO的三个核心对象

Connection对象

Connection对象表示了到数据库的连接,它管理应用程序和数据库之间的通信。RecordestCommand对象都有一个ActiveConnection属性,该属性用来饮用Connection对象。

Command对象

Command对象被用来处理重复执行的查询,或处理需要检查在存储过程调用中的输出或返回参数的值的查询。

Recordset对象

Recordset对象被用来获取数据。Recordset对象存放查询的结果,这些结果又数据的行(称为记录)和列(成为字段)组成。每一列都存放在RecordsetFields集合中的一个Filed对象中

 

 

关于ADO数据库连接方面知识的总结

 

1、导入库文件

 

使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:

#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","EndOfFile") rename("BOF","FirstOfFile")

 

ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。

类型库描述了自治接口,以及C++使用的COM vtable接口。

当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,

并以此创建一组C++头文件。这些头文件具有.tli .tlh扩展名,#import引入ADO库文件的代码编译后,在项目的目录下生成了这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。

程序的第三行指示ADO对象不使用名称空间,在有些应用程序中,

由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。

如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")

第四行代码将ADO中的EOF(文件结束)更名为adoEOF,因为文件的结尾也是以EOF结尾的,是为了避免与定义了自己的EOF的其他库冲突。 至于改为什么名字,可以根据自己的命名习惯自己确定。

 

 

2、初始化COM环境

OLE DB 是基于COM技术编写的,ADOOLE DB基础之上的用户程序,

OLE DB是一个COM组件,在访问COM组件的时候需要初始化COM库,方法如下:

1   ::CoInitialize(NULL); //初始化OLE/COM库环境

//对数据库的访问在上下代码之间写,下面第三步就应该写在这里

::CoUninitialize();//既然初始化了环境,当然一定要记得释放他了

2)也可以调用MFC全局函数

AfxOleInit();

 

3、三大指针对象的定义和创建实例

 

(1)

_ConnectionPtr pConnection("ADODB.Connection");

_RecordsetPtr pRecordset("ADODB.Recordset");

_CommandPtr pCommand("ADODN.Command");

 

(2)

 _ConnectionPtr pConnection;

_RecordsetPtr pRecordset;

_CommandPtr pCommand;

 

pConnection.CreateInstance(__uuidof(Connection));

pRecordset.CreateInstance(__uuidof(Recordset));

pCommand.CreateInstance(__uuidof(Command));

 

要产生一个智能指针对象,其实在定义的同时也可以初始化,如:

_ConnectionPtr pConnection(__uuidof(Connection));

_ConnectionPtr 是智能指针

__uuidof() 用来获取Connection全局唯一标识符

 

(3)

_ConnectionPtr pConnection;

_RecordsetPtr pRecordset;

_CommandPtr pCommand;

 

pConnection.CreateInstance("ADODB.Connection");

pRecordset.CreateInstance("ADODB.Recordset");

pCommand.CreateInstance("ADODB.Command");

 

4、打开一个连接

pConnection->ConnectionString = "这里的字符串有下面四种写法";//对连接字符串赋值

pConnection->Open(ConnectionString,"","",adModeUnknown); //连接数据库

第二三个参数分别为用户的ID与密码,

因为在连接字符串ConnectionCstring中已经设置好了,这里可以为空。

第四个参数可以取下面两个参数:

adAsyncConnect

异步打开数据库,在ASP中直接用16

adConnectUnspecified

同步打开数据库,在ASP中直接用-1

 

ConnectionString根据不同的数据源,分别对应不同的写法

(要记下来很困难,可以在VB中利用ADO控件先连接好,再将其拷贝在VC中,这样不容易出错)

    1

访问Access 2000

"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=databaseName;User ID=userName;Password=userPassWord"

 

    2)

访问ODBC数据

"Provider=MADASQL;DSN=dsnName;UID=userName;PWD=userPassword;"

 

   3)

访问Oracle数据库

Provider=MSDAORA;Data Sourse=serverName;User ID=userName;Password=userPassword;"

 

   4)

访问MS SQL数据库

"Provider=SQLOLEDB,Data Source=serverName;Initial Catalog=databaseName;User ID=userName;Password=userPassword;"

 

5、执行SQL命令,得到数据

方法1

pRecordset = pConnection->Execute("Select * from authors",NULL,adCmdText);

方法2

pRecordset ->Open("Select * from authors",_

variant_t((Idispacth*) pConnection), //设置活动连接

adOpenDynamtic,

//游标类型

adLockOptimistic,

//锁的类型

adCmdText);

方法3

pCommand->put_ActiveConnection(_variant_t((Idispatch *) pConn);

pCommand->CommandText = "Select * from authors";

pRecordset = pCmd->Execute(NULL,NULL,adCmdText);

 

 

得到数据之后,做一个循环取得数据:

While (!pRecordset ->adoEOF)

{

Str = pRecordset->GetCollect("au_lname"));

pRst->MoveNext();

}

SQL命令比较多,但是不去考虑细节,这里只说出通用的方法

CString strSQL;//定义SQL命令串,用来保存SQL语句

 

strSQL.Format("SQL statement");

 

然后在每个要用到SQL命令串的方法中,使用strSQL.AllocSysString()的方法进行类型转换

 

6com的专用数据类型

 

variant ,bstr ,SafeArray

 

variant变量的范围包括很多,它是一种变体类型,主要用于支持自动化的语言访问,

从而在VB中非常方便地使用,但是VC中比较复杂,它使用_variant_t 进行管理

bstr是一种字符串变量,使用_bstr_t进行管理,这个类重载了char *操作符

 

7、关闭连接

 

if(pConnection->State);     //不能多次关闭,否则会出现错误

pConnection->Close();

pRecordset->Close();

pCommand.Release();

pConnection.Release();

//释放引用计数

pRecordset.Release();

 

 

    注意:调用Close()时用"->",调用Release()时要用".",为什么?

因为智能指针,_ConnectionPtr是一个重载了->运算符的类

 

_ConnectionPtr:它是一个接口指针模板。'.'是模板_com_ptr的函数。->'接口函数'调用。

//forexample:

_ConnectionPtr m_Conn;

m_Conn.CreateInstance(....);//Createinterfaceinstance.

m_Conn->Open(...);//Openaconnectiontodatabase.

 

'->'_com_ptr重载了的运算符.目的就是为了让你调用模板参数的函数.

 

8、结构化异常处理

ADO封装了COM接口,所以需要进行错误处理

如下例:

HRESULT hr;

try

{

hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象

if(SUCCEEDED(hr))

{

hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库

///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51; }

}

catch(_com_error e)///捕捉异常

{

CString errormessage;

errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());

AfxMessageBox(errormessage);///显示错误信息

}

 

这里介绍了三种对象通过ADO访问数据库,它们都可以执行SQL语句获取数据,但不是管那种方法获取数据,最终都将数据放置到记录集对象当中。