首先来谈谈句柄,初学习WinSDK的朋友刚看到这个词头大了吧?其实我也是了,我们来看看programming windows里面是怎么说的,一个句柄仅仅是用来识别某些事情的数字。它唯一的标识这当前的一个实例。这样说确实不容易懂。那么我们这么看,比如你打开windows自带的计算器。你多打开几次是不是桌面上出现了很多个计算器呢?你使用其中一个计算器的时候当你按下等于按钮的时候运算结果是否会出现在其他的计算机结果栏里?不会,那windows怎么知道让结果出现在哪里呢?这就是句柄的作用了,句柄唯一的标识着一个程序,你打开的每一个窗口(计算器)都有一个不同的句柄你你每一步操作都是指定了在某个句柄下的,所以,他不会出错。而且你打开的每一个计算机都共享着同样的代码和内存。通过句柄系统会把所需的资源充分的调用到当前的某个程序自己的数据区。
不仅是窗口,各种菜单,GDI对象都有自己的句柄,获取句柄的手段也是多重多样,不过当然是通过调用API函数实现了,如:
MFC中的hHandle = GetSafeHandle();
API编程中的hBrush = GetStorkObject(BLACK_BRUSH);
很多操作都需要将句柄添加到参数列表中,当你没有直接定义句柄变量的时候可能要记忆很多API的返回类型来间接获取。如:
hPen = SelectObject(hdc,GetStockObject(&logicpen)); // SelectObject()这个函数在设置本设备描述表下的GDI对象时会返回设置前的GDI对象句柄 MoveToEx(hdc, pt1.x, pt1.y, &apt); LineTo(hdc, pt2.x,pt2.y); SelectObject(hdc,hPen); |
完成选择自定义的GDI对象的操作。句柄的种类很多,掌握一种的使用方法所有的不学自通,WinAPI编程永远伴随的元素中句柄是其中之一。非常重要。由于是浅谈,所以就说到这里了.
接下来是windows下的消息映射机制了,呵呵,窗口过程,刚学的朋友难理解吧?WinSDK编程基于C,但是和C的理念有着完全的不同,这中间的不同,在我看来最多的也就是来自于这个消息映射,后面什么吹的很炫的Hook技术,木马技术,键盘截获,都是来自于特殊消息的捕捉,映射自定义的特殊消息来实现的(当然和我接下来谈的稍微有点不同)。
首先我们应该先明白消息和事件的区别,Windows是消息驱动的操作系统,这里的消息的产生来自于某个实例化的对象上用户的操作,来自控件,菜单,或者是系统本身产生的,而事件是靠消息触发的,但这也不是绝对的。可以用一个简单的例子去解释,我这里越写越觉得自己难表达清楚,就比如这么一个例子:“某男杀人这条消息导致被枪毙这个事件”不过最重要的区别是在消息产生后并不会被直接处理,而是先插入windows系统的消息队列,然后系统判断此消息产生于哪个程序,送入此程序的消息循环,由LRSULT CALLBACK winprc(hwnd , uint,wParam,lParam)处理。而事件是操作系统处理消息的过程中反馈的结果。
用户操作-> 产生消息->发送系统->系统判断来源->发给相应的窗口过程或者其他Callback函数->消息处理->等待下一条消息的产生
以上为消息循环整个过程。
LRSULT CALLBACK winprc(hwnd , uint,wParam,lParam); int WINAPI WinMain(…) { MSG msg; RegisterClass(…); // 注册窗口类 CreateWindow(…); // 创建窗口 ShowWindow(…); // 显示窗口 UpdateWindow(…); While(GetMessage(&msg,…)){ // 消息循环 TranslateMessage(…); DispatchMessage(…); } LRSULT CALLBACK winprc(hwnd , uint,wParam,lParam); //窗口过程函数,用于映射switch语句中各个需要被处理的消息 { While((UINT)message) { Switch(message) Case… Case… ……… Default………. } } |
以上是最基本的WinAPi编程的代码结构。其实这里面最重要的结构莫过于while(GetMessage(&msg))和Winproc这个函数,这也是传统的C面向过程编程的区别,win编程总等着特定事件触发对应的消息映射函数来完成代码功能,并不是一条代码从头走到尾。关于特殊消息的映射,这里不谈,这里仅是个入门指引。
最后谈一点就是重绘问题。其实在我看来这个东西更多是属于GDI编程里面的东西,说起来其实难度不大,但是处理起来确实是个难点。先拿刚才的代码来说吧。先添加一条关于WM_LBUTTONDOWN的消息映射:
Static int apt[2]; case WM_LBUTTONDOWN: hdc = GetDC(hwnd); apt[1].x = LOWORD (lParam); apt[1].y = HIWORD (lParam); hPen = CreatePen(BLACK_PEN,3,RGB(125,125,125)); SelectObject(hdc,hPen); MoveToEx(hdc,apt[0].x,apt[0].y,NULL); LineTo(hdc,apt[1].x,apt[1].y); apt[0].x = apt[1].x; apt[0].y = apt[1].y; DeleteObject(hPen); ReleaseDC(hwnd,hdc); return 0; |
这段代码实现一个简单的画线功能,当你在你的客户区胡点一通鼠标后试着拖动一下窗口大小,或者将其最小化或者被其他窗口覆盖一下你都会发现你原来画的线没了,可是其他窗口为什么被覆盖了以后再弹出窗口还会有原来的东西呢?那就是重绘,要重新绘制整个客户区(准确的说是失效的矩形),以上说的操作都会导致你的客户区失效,这时会产生重绘消息WM_PAINT,我们要想保存这些线那么我们就必须保存这些你用鼠标左键点过的点。当然这是重绘技术中最简单的,当你的客户区上是一个复杂的画面的话,就不仅仅需要保存点,还有各种形状的图形,颜色等等……这里给大家一段我自己写的代码来实现以上的WM_LBUTTONDOWN消息映射来产生的点。通过单链表来动态添加点来实现重绘。
case WM_PAINT: hdc = BeginPaint(hwnd,&ps); TextOut(hdc,cxClient/6,cyClient/6,TEXT("图形重绘"),strlen("图形重绘")); ReDrawLines(&MyList,hdc); EndPaint(hwnd,&ps); return 0; case WM_LBUTTONDOWN: hdc = GetDC(hwnd); apt[1].x = LOWORD (lParam); apt[1].y = HIWORD (lParam); hPen = CreatePen(BLACK_PEN,2,RGB(125,0,0)); SelectObject(hdc,hPen); MoveToEx(hdc,apt[0].x,apt[0].y,NULL); LineTo(hdc,apt[1].x,apt[1].y); MyList.pCurrent->x = apt[0].x; MyList.pCurrent->y = apt[0].y; MyList.pCurrent->pNext->x = apt[1].x; MyList.pCurrent->pNext->y = apt[1].y; MyList.m_iCounter = MyList.m_iCounter+2; MyList.pCurrent = MyList.pCurrent->pNext->pNext; apt[0].x = apt[1].x; apt[0].y = apt[1].y; DeleteObject(hPen); ReleaseDC(hwnd,hdc); return 0; |
其中的重绘函数代码如下:
void ReDrawLines(LinkList* pLinkList,HDC hdc) { pMyPoint p = pLinkList->pHead; int iSaver =pLinkList->m_iCounter; while(iSaver!=0) { MoveToEx(hdc,p->x,p->y,NULL); LineTo(hdc,p->pNext->x,p->pNext->y); p=p->pNext->pNext; iSaver=iSaver-2; } } |
添加了以上的代码你会发现再次拖动窗口大小等等你原来画的线就都能重现出来了。呵呵是不是觉得一个看似简单的东西其实里面需要很多代码实现呢?也许,这就是windows.
好了,WinSDK入门的东西就谈这么多,希望能给初学者一定的帮助,这么多的字,都是我一个一个打出来没有任何借鉴和摘抄的。相信做为一个过来人能更多的理解大家学习中的困难。
2008.4.8.15:18从http://windowssecurity.ctocio.com.cn/tips/179/7595179.shtml转载