孙鑫VC++学习笔记(转载至程序员之家--虎非龙)[16--20]

第16课 1.事件对象:来实现线程的同步。与互斥对象一样均属于内核对象。 当人工重置有信号时,所有线程均得到信号,所以不能设为人工重置。代码就不贴了。 通过创建匿名的事件对象,也可以让一个程序只能运行一个实例。 2.关键代码段实现线程的同步:类似公用电话亭,只有当电话亭里面没人了,其它人才可以再进去打电话。用了4个函数,这种方法比较简单!但缺点是如果使用了多少关键代码码,容易赞成线程的死锁 3.线程死锁,用关键代码示例,用了两个临界区对象,实战中要注意避免这种错误! 4.使用异步套接字编写网络聊天室 1)加载套接字库,进行版本协商,包含头文件,链接库文件,这次请示的是2.2版本! 2)在类CChatDlg中增加一个成员变量m_socket,在析构函数中释放这个变量 3)利用WSASocket()创建套接字(数据报类型的UDP型的) 4)然后调用WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)为网络事件定义消息!此时如果发生FD_READ消息,系统会发送UM_SOCK消息给应用程序!程序并不会阻塞在这儿了! 以上是在BOOL CChatDlg::OnInitDialog()完成 5)然后完成消息响应! 头文件中:#define UM_SOCK WM_USER+1 afx_msg void OnSock(WPARAM,LPARAM); 源文件中: ON_MESSAGE(UM_SOCK,OnSock) 实现消息响应函数:void CChatDlg::OnSock(WPARAM wParam,LPARAM lParam) { switch(LOWORD(lParam)) { case FD_READ: WSABUF wsabuf; wsabuf.buf=new char[200]; wsabuf.len=200; DWORD dwRead; DWORD dwFlag=0; SOCKADDR_IN addrFrom; int len=sizeof(SOCKADDR); CString str; CString strTemp; HOSTENT *pHost; if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag, (SOCKADDR*)&addrFrom,&len,NULL,NULL)) { MessageBox("接收数据失败!"); return; } pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET); //str.Format("%s说 :%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf); str.Format("%s说 :%s",pHost->h_name,wsabuf.buf); str+="/r/n"; GetDlgItemText(IDC_EDIT_RECV,strTemp); str+=strTemp; SetDlgItemText(IDC_EDIT_RECV,str); break; } } OK! 6)完成数据发送的功能! void CChatDlg::OnBtnSend() { // TOD Add your control notification handler code here DWORD dwIP; CString strSend; WSABUF wsabuf; DWORD dwSend; int len; CString strHostName; SOCKADDR_IN addrTo; HOSTENT* pHost; if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="") { ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); addrTo.sin_addr.S_un.S_addr=htonl(dwIP); } else { pHost=gethostbyname(strHostName); addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]); } addrTo.sin_family=AF_INET; addrTo.sin_port=htons(6000);

GetDlgItemText(IDC_EDIT_SEND,strSend); len=strSend.GetLength(); wsabuf.buf=strSend.GetBuffer(len); wsabuf.len=len+1;

SetDlgItemText(IDC_EDIT_SEND,"");

if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0, (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL)) { MessageBox("发送数据失败!"); return; } }

7)完成将主机名转换为IP地址的功能,以前将IP地址转换为主机名的功能 嘿嘿,单线程的聊天室创建完毕!性能并且非常出色!

 

 

第17课 进程间通信
有四种方法
1.剪贴板
  a.创建个ClipBoard的对话框应用程序,加两EditBox和两个Button发送接收。
  b.具体代码:
    发送端代码:
if(OpenClipboard())
{
  CString str;
  HANDLE hClip;
  char *pBuf;
  EmptyClipboard();
  GetDlgItemText(IDC_EDIT_SEND,str);
  hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
  pBuf=(char*)GlobalLock(hClip);将句柄转换为指针!
  strcpy(pBuf,str);
  GlobalUnlock(hClip);
  SetClipboardData(CF_TEXT,hClip);
  CloseClipboard();
}
     接收端代码:
if(OpenClipboard())
{
  if(IsClipboardFormatAvailable(CF_TEXT))
  {
   HANDLE hClip;
   char *pBuf;
   hClip=GetClipboardData(CF_TEXT);
   pBuf=(char*)GlobalLock(hClip);
   GlobalUnlock(hClip);
   SetDlgItemText(IDC_EDIT_RECV,pBuf);
   CloseClipboard();
  }
}
2.匿名管道:只能在父子进程之间进行通信
  a.先建一个Parent的单文档应用程序,增加“创建管道”“读取数据”“写入数据”三个菜单
  b.增加成员变量HANDLE类型的hRead,hWrite,初始化变量,并在析构函数中释放句柄
  c.响应菜单代码:
void CParentView::OnPipeCreate() 菜单“创建管道”代码
{
// TOD Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
  MessageBox("创建匿名管道失败!");
  return;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(STARTUPINFO));将数据清0!
sui.cb=sizeof(STARTUPINFO);
sui.dwFlags=STARTF_USESTDHANDLES;
sui.hStdInput=hRead;
sui.hStdOutput=hWrite;
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);

if(!CreateProcess("..//Child//Debug//Child.exe",NULL,NULL,NULL,
   TRUE,0,NULL,NULL,&sui,&pi))创建子进程
{
  CloseHandle(hRead);
  CloseHandle(hWrite);关闭句柄,将内核对象的使用计数减少1,这样当操作系统发现内核对象的使用计数为0时,将清除内核对象。
  hRead=NULL;
  hWrite=NULL;
  MessageBox("创建子进程失败!");
  return;
}
else
{
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
}
}

void CParentView::OnPipeRead() 菜单“读取数据”代码
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
  MessageBox("读取数据失败!");
  return;
}
MessageBox(buf);
}

void CParentView::OnPipeWrite() 菜单“写入数据”代码
{
// TOD Add your command handler code here
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
  MessageBox("写入数据失败!");
  return;
}
}
     d.再建一个Child的单文档,在View中增加两个成员hRead和hWrite.在OnInitialUpdate()中得到句柄的值。
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();

// TOD Add your specialized code here and/or call the base class
hRead=GetStdHandle(STD_INPUT_HANDLE);注意这句代码!
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}

     e.加菜单“读取数据”“写入数据”其代码如下:
void CChildView::OnPipeRead()
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
  MessageBox("读取数据失败!");
  return;
}
MessageBox(buf);
}

void CChildView::OnPipeWrite()
{
// TOD Add your command handler code here
char buf[]="匿名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
  MessageBox("写入数据失败!");
  return;
}
}

3.命名管道:还可以跨网络通信,服务器只能在win2000和NT下运行!而客户端可以在95下运行。关键函数CreateNamedPipe
  a.先建一个NamedPipeSRV单文档应用程序,加菜单“创建管道”“读取数据”“写入数据”
  b.在View中增加Handle变量hPipe,注意在析构函数中释放它!
  c.响应菜单,创建命名管道
void CNamedPipeSrvView::OnPipeCreate()
{
// TOD Add your command handler code here
hPipe=CreateNamedPipe(".//pipe//MyPipe",
  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  0,1,1024,1024,0,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
  MessageBox("创建命名管道失败!");
  hPipe=NULL;
  return;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
  MessageBox("创建事件对象失败!");
  CloseHandle(hPipe);
  hPipe=NULL;
  return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
if(!ConnectNamedPipe(hPipe,&ovlap))
{
  if(ERROR_IO_PENDING!=GetLastError())
  {
   MessageBox("等待客户端连接失败!");
   CloseHandle(hPipe);
   CloseHandle(hEvent);
   hPipe=NULL;
   return;
  }
}
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
{
  MessageBox("等待对象失败!");
  CloseHandle(hPipe);
  CloseHandle(hEvent);
  hPipe=NULL;
  return;
}
CloseHandle(hEvent);
}

void CNamedPipeSrvView::OnPipeRead()
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
  MessageBox("读取数据失败!");
  return;
}
MessageBox(buf);
}

void CNamedPipeSrvView::OnPipeWrite()
{
// TOD Add your command handler code here
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
  MessageBox("写入数据失败!");
  return;
}
}

      d.再建一个NamedPipeCLT单文档工程,加菜单“连接管道”“读取数据”“写入数据”,当然别忘记成员变量hPipe的定义和初始化
      e.响应菜单代码
void CNamedPipeCltView::OnPipeConnect() 连接管道
{
// TOD Add your command handler code here
if(!WaitNamedPipe(".//pipe//MyPipe",NMPWAIT_WAIT_FOREVER))
{
  MessageBox("当前没有可利用的命名管道实例!");
  return;
}
hPipe=CreateFile(".//pipe//MyPipe",GENERIC_READ | GENERIC_WRITE,
  0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
  MessageBox("打开命名管道失败!");
  hPipe=NULL;
  return;
}
}

void CNamedPipeCltView::OnPipeRead() 读取数据
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
  MessageBox("读取数据失败!");
  return;
}
MessageBox(buf);
}

void CNamedPipeCltView::OnPipeWrite() 写入数据
{
// TOD Add your command handler code here
char buf[]="命名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
  MessageBox("写入数据失败!");
  return;
}
}

4.邮槽,使用时应将消息长度限制在424字节以下,关键函数CreateMailSlot()
  a.先建一个MailSlotSRV工程,加菜单“接收数据”
  b.消息响应代码:
void CMailslotSrvView::OnMailslotRecv() 菜单“接收数据”的代码
{
// TOD Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateMailslot(".//mailslot//MyMailslot",0,
  MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
  MessageBox("创建油槽失败!");
  return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
  MessageBox("读取数据失败!");
  CloseHandle(hMailslot);
  return;
}
MessageBox(buf);
CloseHandle(hMailslot);
}
    c.加工程MailSlotCLT,加菜单“发送数据”
    d.加消息响应,添加代码,客户端也比较简单。
void CMailslotCltView::OnMailslotSend() 菜单“发送数据”的代码
{
// TOD Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateFile(".//mailslot//MyMailslot",GENERIC_WRITE,
  FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
  MessageBox("打开油槽失败!");
  return;
}
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
  MessageBox("写入数据失败!");
  CloseHandle(hMailslot);
  return;
}
CloseHandle(hMailslot);
}

5.以上4种方法各有优缺点:剪贴板比较简单。邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。
命名管道和邮槽可以进行网络通信。

 

 

 

第18课 ActiveX编程(下面X均为ActiveX简称)
1.在VB中调用X控件,添加方法 project->Add components。另外可以用Object Browser来查看控件
2.在VC中创建X控件
  1.新建一个X工程名为Clock,注意一个文件中可以包含多个控件。
  2.保持缺省设置,完成。注意它生成的三个类,以及相关的接口。
  3.运行它。选择TSTCON32.exe作为容器。
  4.选择Insert Control,此时我们可以看到,它画了一个椭圆。也可以在VB中测试。
  5.删除注册信息。用regsvr32 /u +文件名。也可以在菜单选择反注册命令。
  6.重写代码。在CClockCtrl::OnDraw()中画了一个椭圆,此时我们在其中得到系统时间,并显示它。为此我们在OnCreate()设置了一个定时器,每隔一定时间发出一个Invalidate()消息,使窗口重绘。
  7.如何改变控件的背景色和前景色?ClassWizard->AutoMation->Add Property->BackColor,还需要在OnDraw()中加上相应的代码
   CBrush brush(TranslateColor(GetBackColor()));
 pdc->FillRect(rcBounds, &brush);
 pdc->SetBkMode(TRANSPARENT);
 pdc->SetTextColor(TranslateColor(GetForeColor()));
  8.增加属性页。在
  BEGIN_PROPPAGEIDS(CClockCtrl, 2)此时数目也得改成相应的数目
 PROPPAGEID(CClockPropPage::guid)
 PROPPAGEID(CLSID_CColorPropPage)
  END_PROPPAGEIDS(CClockCtrl)  OK~
  9.增加自定义属性:ClassWizard->AutoMation->Add Property加上一个变量m_interval,类型为short,对应外部变量为Interval。在CClockCtrl中增加OnIntervalChanged方法。添加如下代码:
   if(m_interval<0 || m_interval>6000)
 {
  m_interval=1000;
 }
 else
 {
  m_interval=m_interval/1000*1000;
  KillTimer(1);
  SetTimer(1,m_interval,NULL);
  BoundPropertyChanged(0x1);
 }
   10.测试:Control->Invoke Methods
   11.将时间间隔加到属性页中,在资源视图中加入一文本框和编辑框。为EditBox关联成员变量,加入属性interval。
   12.增加方法:ClassWizard->AutoMation->Add Method->Hello加入代码 OK!在VB中可以调用此方法!
   void CClockCtrl::Hello() 
{
 // TOD Add your dispatch handler code here
 MessageBox("Hello world!");
}
   13.增加事件:ClassWizard->AutoMation->Add Events->Click
   14.增加自定义事件:ClassWizard->AutoMation->Add Events->NewMinute
      在新的一分钟到达时发出这个通知,在OnDraw()中写代码:
       CTime time=CTime::GetCurrentTime();
 if(0==time.GetSecond())
 {
  FireNewMinute();
 }
   15.让Interval属性具有持久性。在CClockCtrl::DoPropExchange()中调用PX_short()方法,OK!
    PX_Short(pPX,"Interval",m_interval,1000);
   16.让Property Page和Property属性中的interval保持一致的方法:在OnIntervalChanged()中调用BoundPropertyChanged(0x1);
   17.希望控件在设计时间内不走动的方法:在OnTimer()中, if(AmbientUserMode())InvalidateControl();巧妙!
3.在VC中调用X控件
  1.新建ClockTest对话框应用程序
  2.点击右键->插入X控件->时钟控件
  3.Project->Add Component会生成CClock类。
  4.在CCLockTestDlg中增加CClock类的成员变量m_clock,然后可以动态创建一个这样的东东!
  5.试验Click(),NewMinute(),SetBkColor(),SetForeColor()方法和属性
  6.如何为动态创建的控件做事件响应呢?首先你得知道它的ID号,然后参考非动态的控件事件代码,呵。

第19课 DLL编程 1.DLL简介,动态库,静态库。动态库节约磁盘空间,静态库体积大。可以用多种语言编写DLL文件。动态库有两种加载方式:隐式调用和动态加裁! 2.新建一个DLL1的dll工程,加入一源文件名为dll1.cpp,加入add和subtract两个函数,注意此时须在函数名前加_declspec(dllexport),并且编译。用dumpbi -exports dll1.dll查看其导出的函数,发现函数名字已经被改成了 ?add@@YAHHH@Z,这种现象叫做名字粉碎,是为了支持函数重载而做的。 3.编写一个程序测试DLL,工程名为DllTest,基于对话框的,放置两个按纽add和subtract,响应按纽消息,调用这个Dll的add和subtract函数。使用这两个函数前要先声明函数,//extern int add(int a,int b); //extern int subtract(int a,int b); 还需要将Dll1.lib和Dll1.dll拷贝到当前目录下!另外还需要在Project->Setting->Link->Object/Library中加入Dll1.lib,此种方式为隐式调用!OK!用Dumpbin -imports DllTest.exe查看它的输入信息,可以看到它加载了dll1.dll。同时也可以用depends程序查看程序需要哪些dll文件!除了用extern外,还可以用//_declspec(dllimport) int add(int a,int b); //_declspec(dllimport) int subtract(int a,int b); 告诉编译器,此函数是动态链接库中的函数,这样可以提高效率。 4.通常写Dll时在dll1.h中声明函数,然后在DllTest.h中包含这个头文件,另外会用一组宏来取代_declspec(dllimport) Dll1.h #ifdef DLL1_API #else #define DLL1_API extern "C" _declspec(dllimport) #endif

DLL1_API int _stdcall add(int a,int b); DLL1_API int _stdcall subtract(int a,int b); Dll1.cpp的代码: #define DLL1_API extern "C" _declspec(dllexport) #include "Dll1.h" #include <Windows.h> #include <stdio.h>

int _stdcall add(int a,int b) { return a+b; }

int _stdcall subtract(int a,int b) { return a-b; } 5.在Dll1中加入类Point它有一个函数output(int a,intb),它的功能是在屏幕上输出x,y值。须包含头文件windows.h和stdio.h.然后在DllTest中加入一个按纽来测试这个函数!此时我们可以dumpbin来查看dll1.dll和dllTest.exe的导出导入情况。注意,也可以只导出类的某个函数。 6.我们希望导出的函数名不被改变,加extern "C"大写的C!即可,#define DLL1_API extern "C" _declspec(dllexport),但它只能导出全局函数,不能导出类的成员函数,并且如果调用约定被改成了别的方式,此时函数名也被改变。所以这种方式不太好。 7.解决之道是用模块定义文件。 1.新建dll2.dll工程; 2.加dll2.cpp中写两个函数add和subtract 3.在目录中新建dll2.def文件,增加到工程。 4.在dll2.def中加入如下代码: LIBRARY Dll2

EXPORTS add subtract 5.编译后用dumpbin查看函数名是否被改变? 6.测试,我们这次用动态加载的方法来调用dll文件。以前是用隐式链接的方法,嘿嘿。动态加载的好处是需要时再加载,可以提高执行的效率。代码如下: HINSTANCE hInst; hInst=LoadLibrary("Dll3.dll"); typedef int (/*_stdcall*/ *ADDPROC)(int a,int b); //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"?add@@YAHHH@Z"); ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1)); if(!Add) { MessageBox("获取函数地址失败!"); return; } CString str; str.Format("5+3=%d",Add(5,3)); MessageBox(str); FreeLibrary(hInst); 7.此时你改变调用约定,函数名不会被改变,但如果你加上_stdcall定义函数,调用时也需要加入_stdcall,否则会出错! 8.DllMain()是Dll的入口点,不过不是必须的。但在DllMain中不要做复杂的调用。为什么?因为DllMain加载时,某些核心Dll文件不一定已经被加载。 9.创建一个基于MFC的DLL工程,简介。 10.当不使用DLL时,调用FreeLibrary减少DLL的使用计数,释放DLL资源,减少系统负担。明白? 11.上面总结:1.*.def使函数名不改变; 2.定义时为_stdcall,调用时也必须用_stdcall.

 

 

第20课 钩子与数据库编程 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.编写一个屏屏蔽所有进程和所有线程的钩子程序。此时这个钩子必须安装在DLL中,然后被某个程序调用才行。 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.ODBC,ADO简介:ADO可以认为是建立在ODBC上的。 ADO的三个核心对象 Connection对象 Connection对象表示了到数据库的连接,它管理应用程序和数据库之间的通信。 Recordset和Command对象都有一个ActiveConnection属性,该属性用来引用Connection对象。 Command对象 Command对象被用来处理重复执行的查询,或处理需要检查在存储过程调用中的输出或返回参数的值的查询。 Recordset对象 Recordset对象被用来获取数据。 Recordset对象存放查询的结果,这些结果由数据的行(称为记录)和列(称为字段)组成。每一列都存放在Recordset的Fields集合中的一个Field对象中。 2.演示在VB中使用ADO的方法,方法比较简单,使用方便。另外在VB中演示了Connection和Command和Recordset的方法,用这三种方法都可以执行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.tlh和MSADO15.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(); } 至此20课笔记全部记完,关于数据库的比较简单些。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值