windows SDK程序的框架分析

一个典型的SDK程序的框架如下:

 

1、RegeditClass 注册窗口类。

2、CreateWindow 根据窗口类,创建对应的窗口

3、窗口过程回调函数(WndProc)

4、ShowWindow 显示窗口

5、UpdateWindow 更新窗口

6、消息循环(GetMessage DispatchMessage等)


流程简述:

        Windows程序是消息驱动或者说是消息响应机制。即应用程序需要事先准备好一个函数,我们称之为回调函数(WndProc),在某些条件下,由系统从内核中直接调用该函数,而应用程序通常不会主动调用该函数。显然在什么条件下调用窗口过程回调函数值得深究。实际上,这是windows对于用户与UI交互的一种设计,即将用户操作等动作抽象并封装成一个特殊的结构体MSG(如下所示),每种操作对应了唯一的标识值,我们称之为消息码message(例如后面经常遇到的WM_CREATE、WM_PAINT、WM_KEYDOWN、WM_MOUSEMOVE)等。当由于一些原因(例如用户通过键盘键入某一个按键),系统会产生键盘消息(结构体MSG对应的实例msg),然后通知应用程序来处理。而应用程序在回调函数中就可以接收到了这个消息,根据特定的消息码message来为该动作做出对应的响应。

typedefstruct tagMSG {
	HWND hwnd;
	UINT message; //消息码
	WPARAM wParam;
	LPARAM lParam;
	DWORD time;
	POINT pt;
} MSG,*LPMSG,*PMSG;


 显然,一台正在“跑”的计算机中,有很多进程在操作系统上运行(我们可以把进程理解成磁盘上应用程序的实例,即磁盘上的计算器文件是一个应用程序,而一个计算器双击运行一次就产生一个用户看得见、“摸得着”的进程)。显然,每一个有UI界面的进程都可以并且必须能够响应用户的操作,即能响应消息。在一个时间段中,每个进程通常都不止一个消息待处理。一旦消息多了会累积,而每个消息都应有被处理的机会,那么自然要让消息排个队,并在适当的时机被取出处理。因此在windows的设计中,每个进程至少应有一个消息队列。当有需要进程处理的消息时,系统就将其投入到该进程的消息队列中。进程要做的事情就是“乖乖”在自己的程序中等待消息的到来,然后在上面所说的窗口过程回调函数中处理就好了。因此进程总是“被动的”,等待消息、处理消息。
上面为什么说一个进程至少有一个消息队列呢?实际上,消息队列应称之为线程消息队列,即线程才可以但不一定必须拥有一个消息队列。一个进程可以有多个线程,也可以有多个窗口,但通常只有主线程(即UI线程,负责创建窗口),才会被分配一个消息队列(实际是一种数据结构,用链表来存储消息)。而操作系统内部怎么知道该不该为一个线程分配一个消息队列呢,实际上,一开始线程是不会被分配消息队列,只要该线程调用了与UI相关的API(windows 提供的函数调用接口)时,windows内核就会为其分配消息队列了。
                                                                           
 
所以一个正常的应用程序需要响应各种消息,那么最重要的事就是写一个循环和对应的回调函数。
这个循环我们又叫消息循环:
while(GetMessage(&msg, NULL,0,0)) //从消息队列中取出消息
{
     DispatchMessage(&msg);//将消息"派发"给窗口过程回调函数
}


这个回调函数我们称之为窗口回调函数(因为它与窗口是一一对应关系)。
由于窗口过程函数需要根据不同的消息码 message来做出不同的处理,因此窗口过程函数的主体通常是一个大大的switch-case:
switch(message)
{
    case WM_COMMAND:
        break;
    case WM_NCPAINT:
        break;
    case WM_PAINT:
        break;
    case WM_DESTROY:
        break;
    case WM_CREATE:
        break;
    default:
        returnDefWindowProc(hWnd, message, wParam, lParam); //其余消息交给DefWindowProc,由操作系统默认处理
}


而通常,一个带UI界面的进程至少得有一个窗口,所以需要调用CreateWindow这个API来创建窗口。创建一个窗口自然需要指定这个窗口需要什么风格啊,窗口有多大等等信息,其实更为重要的是要通过该函数告诉操作系统,你的这个窗口所对应的窗口过程函数是什么。因为从用户角度来讲,实际响应消息的"实体"应该是对应的窗口,而不该是线程。线程只是提供了一个数据结构来存储发送过来的所有消息,实际消息取出后,就应该根据消息中所指定的窗口(hwnd),找到该窗口所对应的窗口过程回调函数,然后就交由该函数来处理消息了。
    
    
  1. CreateWindowExA(DWORD dwExStyle,
  2. LPCSTR lpClassName, //需要指定“类名”
  3. LPCSTR lpWindowName,
  4. DWORD dwStyle,
  5. int x,
  6. int y,
  7. int nWidth,
  8. int nHeight,
  9. HWND hWndParent,
  10. HMENU hMenu,
  11. HINSTANCE hInstance,
  12. LPVOID lpParam)
从CreateWindowEx中没有找到需要指明的窗口过程回调函数,但是却有一个"类名"的概念,这个“类”实际也是一种设计。我们知道对于界面而言,很多窗口的外观和作用是固定的,这相当于是工厂中已经生产好了的组件。因此对于这些窗口而言,其窗口回调、样式都是固定好了的,不用每次创建窗口时重复指定。因此,微软创建了一个“窗口类”的概念,类里面封装了一个窗口的窗口回调函数,样式啊等等,然后那些拥有固定外观和作用的窗口有着微软定制好了的“窗口类”,创建窗口时,直接指定这个“类名”,系统就知道了你要什么样的窗口了。
    
    
  1. typedefstruct _WNDCLASSW {
  2. UINT style;
  3. WNDPROC lpfnWndProc; //在窗口类中指定窗口过程回调函数
  4. int cbClsExtra;
  5. int cbWndExtra;
  6. HINSTANCE hInstance;
  7. HICON hIcon;
  8. HCURSOR hCursor;
  9. HBRUSH hbrBackground;
  10. LPCWSTR lpszMenuName;
  11. LPCWSTR lpszClassName;
  12. } WNDCLASSW,*LPWNDCLASSW,*PWNDCLASSW;
当然,我们也可以自己定制自己的窗口类,这也就是框架第一步注册窗口类的由来:
   
   
  1. ATOM WINAPI
  2. RegisterClassW(CONST WNDCLASSW *lpWndClass)
  3. {
  4. WNDCLASSEXW Class;
  5. if(lpWndClass == NULL)
  6. return0;
  7. RtlCopyMemory(&Class.style, lpWndClass,sizeof(WNDCLASSW));
  8. Class.cbSize =sizeof(WNDCLASSEXW);
  9. Class.hIconSm = NULL;
  10. return RegisterClassExW(&Class);
  11. }
     最后,框架中还有两个API:ShowWindow和UpdateWindow需要解释。其实CreateWindow只是在内存中创建了一个窗口,即在内存中分配了各种数据结构,但是窗口的显示却需要ShowWindow和UpdateWindow来进一步处理,这两个API实际上在内部还是通过消息机制,向本进程的主窗口发送WM_PAINT等消息。因此,最终的显示仍是依赖窗口过程回调函数来完成。如果窗口过程回调中没有显式处理,那么就会在 DefWindowProc内部,由默认的系统内部来处理。
   
   
  1. LRESULT WINAPI
  2. DefWindowProcA(HWND hWnd,
  3. UINT Msg,
  4. WPARAM wParam,
  5. LPARAM lParam)
  6. {
  7. BOOL Hook, msgOverride = FALSE;
  8. LRESULT Result=0;
  9. LoadUserApiHook();
  10. Hook=BeginIfHookedUserApiHook();
  11. if(Hook)
  12. {
  13. msgOverride =IsMsgOverride(Msg,&guah.DefWndProcArray);
  14. if(msgOverride == FALSE)
  15. {
  16. EndUserApiHook();
  17. }
  18. }
  19. /* Bypass SEH and go direct. */
  20. if(!Hook||!msgOverride)
  21. return RealDefWindowProcA(hWnd,Msg, wParam, lParam);
  22. _SEH2_TRY
  23. {
  24. Result= guah.DefWindowProcA(hWnd,Msg, wParam, lParam);
  25. }
  26. _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
  27. {
  28. }
  29. _SEH2_END;
  30. EndUserApiHook();
  31. returnResult;
  32. }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值