一. Windows程序内部运行机制--Windows编程课程学习笔记

来源:互联网 发布:flothermal软件下载 编辑:程序博客网 时间:2024/05/22 17:41

1.1 API与SDK

操作系统把它所能够完成的功能以函数的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows应用程序编程接口(Application ProgrammingInterface),简称Windows API。API大多是意义的单词的组合,其精确的拼写及调用语法可以在MSDN中查询。

SDK全称是Software Development Kit,软件开发包。实际是开发所需资源。

1.2窗口与句柄

利用窗口可以接受用户输入以及显示输入。通常包括标题栏、菜单栏、系统菜单、最小化框、最大化框、滚动条。可以分为客户区与非客户区。客户区用于显示与绘制。窗口可以有父窗口、子窗口。同时对话框、消息框也是窗口。

Windows应用程序中,句柄(HWND)来标识窗口。各种各样的资源系统创建时都会分配内存,并返回一个标识这些资源的标识号即句柄。

1.3消息与消息队列

1)消息

Windows程序设计是一种事件驱动方式的模式,主要基于消息。消息其实由一个结构体MSG表示。

MSG结构定义如下:

typedefstruct tagMSG {                 HWND   hwnd;     // 消息所属窗口           UINT   message;         // 消息的标识符           WPARAMwParam;      // 附加消息           LPARAMlParam;         // 附加消息           DWORD  time;           // 投递到消息队列的时间           POINT  pt;                  // 鼠标当前坐标} MSG;

2)消息队列

应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息(消息通常与窗口相关)。消息队列等待应用程序响应,应用程序对事件作出反应的过程就是消息响应,各种消息响应放在一起组成了窗口过程。这种消息机制就是Windows程序运行的机制


3)进队消息和不进队消息

进队消息由操作系统放入消息队列,应用程序取出并发送。不进队消息在系统调用窗口过程是直接发送给窗口。最终都由系统调用窗口过程函数对消息进行处理。

1.4 WinMain函数

完整的Win32程序步骤如下:

1)入口函数WinMain

2)创建窗口

3)消息循环与发送消息

4)窗口过程与消息响应

1)入口函数WinMain

intWINAPI WinMain(  HINSTANCE hInstance,          // 当前应用程序实例句炳  HINSTANCE hPrevInstance,          //永远为NULL(当前句柄的前一个句柄)  LPSTR lpCmdLine,              // 命令行参数  int nCmdShow                  // 窗口显示时的状态);

2)创建窗口

Step1:  设计一个窗口类;

Step2: 注册窗口类;

Step3: 创建窗口;

Step4:  显示及更新窗口。

Step1. 设计窗口类

typedef struct _WNDCLASS {  UINT           style;                //窗口类样式(CS_XXX)  WNDPROC     lpfnWndProc;     //窗口过程函数指针(*)  int            cbClsExtra;         //窗口类附加内存字节数,通常为0   int            cbWndExtra;     //窗口附加内存字节数,通常为0  HANDLE       hInstance;          //应用程序实例句柄  HICON         hIcon;              //标题栏图标(IDI_XXX, NULL)  HCURSOR     hCursor;            //光标  HBRUSH       hbrBackground;   //窗口背景颜色  LPCTSTR       lpszMenuName;   //菜单资源名称  LPCTSTR       lpszClassName;   //窗口类名称} WNDCLASS;

About lpfnWndProc.指向窗口过程函数(回调函数:特定事件或条件发生受另一方调用)。窗口过程函数被调用过程如下:(1)设计窗口类时,地址复制给lpfnWndProc;(2)调用RegisterClass(&wndclass),系统有了窗口过程函数地址;(3)应用程序接收一个消息时,调用DispatchMessage(&msg)将消息回传给系统。系统利用指针,调用窗口过程函数处理。

About hIcon. 可以调用LoadIcon加载一个图标资源。HICON LOADICON( HINSTANCE hInstance, LPCTSTR lpIconName),加载标准图标第一个参数NULL,也可以加载自定义图标。

About hCursor. 可以调用LoadCursor加载一个光标。同LoadIcon

About hbrBackground. 调用GetStockObject得到系统标准画刷。

详见MSDN。

Step2. 注册      

ATOMRegisterClass(CONST WNDCLASS *lpWndClass)

Step3. 创建

HWND CreateWindow( LPCTSTRlpClassName,       // 已注册的窗口类名称 LPCTSTRlpWindowName,    // 窗口标题栏中显示的文本 DWORDdwStyle,              //窗口样式(WS_XXX | WS_XXX) intx,                           // 水平坐标 inty,                        // 垂直坐标 intnWidth,                     // 宽度 intnHeight,                  //高度 HWNDhWndParent,            //父窗口句柄 HMENUhMenu,              //菜单句柄 HINSTANCEhInstance,      // 应用程序实例句柄 LPVOIDlpParam           // 用于多文档程序的附加参数,单文档为NULL );

Step4. 显示与更新

BOOL ShowWindow(HWNDhWnd, int nCmdShow) // nCmdShow(SW_XXX)BOOL UpdataWindow(HWND hWnd);

3)消息循环与发送消息

MSG msg;       while (GetMessage(&msg,NULL,0,0))        // 从消息队列获取消息       {                     TranslateMessage(&msg);         //消息解释                     DispatchMessage(&msg);         //将消息发送到“窗口过程”       }

TranslateMessage函数用于将虚拟键消息转换为字符消息。将WM_KEYDOWN和WM_KEYUP消息的组合转换为一条WM_CHAR消息,该消息的WParam附加参数包含了字符的ASCII码。并将转换后的新消息投递到调用线程的消息队列中。注意,Translate函数并不会修改原有的信息,他只是产生新的消息并投递到消息队列中。

DispatchMessage分派一个消息到窗口过程。由窗口过程函数对消息进行处理。DispatchMessage实际上是将消息回传给操作系统,再由操作系统调用窗口过程函数对消息进行处理。

BOOL GetMessage(               LPMSGlpMsg,                // 消息结构体指针,返回消息信息            HWND hWnd,                 //窗口句柄 ,通常设为NULL             UINT wMsgFilterMin,            //消息过滤最小值             UINT wMsgFilterMax            //消息过滤最大值     );

       WM_QUIT返回0,错误返回-1。

4)窗口过程与消息响应

       

LRESULT CALLBACK MyWndProc(      HWNDhwnd,               // handle to window      UINTuMsg,             // message identifier      WPARAMwParam,            //first message parameter      LPARAMlParam             // second message parameter){          switch(uMsg)                 {                case WM_PAINT:                 //响应消息             caseWM_KEYDOWN:           //响应消息             caseWM_LBUTTONDOWN:   //响应消息            …                default:                   returnDefWindowProc(hwnd,uMsg,wParam,lParam);          }           return0;}

About WM_PAINT.  Windows 把一个最小的需要重绘的正方形区域叫做“无效区域”。发现了一个“无效区域“后,它就会向该应用程序发送一个 WM_PAINT 消息,通知应用程序重新绘制窗口。当窗口从无到有、改变尺寸、最小化后再恢复、被其他窗口遮盖后再显示时,窗口的客户区都将变为无效。

About DefWindowProc. 不感兴趣的、不需要我们处理的消息,交还给Windows操作系统由DefWindowProc函数来实现。在销毁窗口后,发送WM_DESTROY(响应方式就是调用PostQuitMessage函数,该函数会在消息队列中添加一个WM_QUIT消息,准备让由消息循环中的GetMessage取得)

1.5 测试程序

VS2015下面测试一下程序。新建Win32 项目,编写代码后调试,发现一堆error2664,解决方法是项目属性,字符集更改为使用多字节字符型。之后出现链接错误,解决方法是添加附加依赖项

#pragma comment (lib,"User32.lib")#pragma comment(lib, "gdi32.lib")


程序运行界面


#pragma warning (disable:4996)#pragma comment (lib,"User32.lib")#pragma comment(lib, "gdi32.lib")#include <Windows.h>#include <stdio.h> LRESULT CALLBACK MyWinProc(    HWND hwnd,            //handle to window    UINT uMsg,                //message identifier    WPARAM wParam,       //first message parameter    LPARAM lParam         //second message parameter    ); int WINAPI WinMain(    HINSTANCE hInstance,         // 当前应用程序实例句炳    HINSTANCE hPrevInstance,      // 永远为NULL(当前句柄的前一个句柄)    LPSTR lpCmdLine,             // 命令行参数    int nCmdShow                 // 窗口显示时的状态    ){    //Step1. 设计窗口类    WNDCLASS wndcls;     wndcls.cbClsExtra = 0;    wndcls.cbWndExtra = 0;    wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);    wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);    wndcls.hIcon = LoadIcon(NULL, IDI_QUESTION);    wndcls.hInstance = hInstance;    wndcls.lpfnWndProc = MyWinProc;    wndcls.lpszClassName = ("WindowsTest");    wndcls.lpszMenuName = NULL;    wndcls.style = CS_HREDRAW | CS_VREDRAW;     //Step2. 注册窗口类    RegisterClass(&wndcls);     //Step3. 创建窗口类    HWND hwnd;    hwnd = CreateWindow("WindowsTest", "MyFirst Windows App", WS_OVERLAPPEDWINDOW,        0, 0, 600, 400, NULL, NULL, hInstance, NULL);     //Step4. 显示及刷新窗口    ShowWindow(hwnd, SW_SHOWNORMAL);    UpdateWindow(hwnd);     // 定义结构体,开始消息循环    MSG msg;    while (GetMessage(&msg, NULL, 0, 0))    {        TranslateMessage(&msg);        DispatchMessage(&msg);    }     return msg.wParam;} LRESULT CALLBACK MyWinProc(    HWND hwnd,            //handle to window    UINT uMsg,          // message identifier    WPARAM wParam,       // first message parameter    LPARAM lParam         //second message parameter    ){    switch (uMsg)    {    case WM_CHAR:        charszChar[20];        sprintf(szChar, "char code is %d", wParam);        MessageBox(hwnd,szChar, "char", 0);        break;    case WM_PAINT:        HDChDC;        PAINTSTRUCT ps;        hDC = BeginPaint(hwnd, &ps);        TextOut(hDC,0, 0, "HelloWorld!", strlen("Hello World!"));        EndPaint(hwnd,&ps);        break;    case WM_LBUTTONDOWN:        MessageBox(hwnd, "mouse clicked", "message", 0);        HDChdc;        hdc = GetDC(hwnd);        TextOut(hdc,0, 50, "HelloWin32!", strlen("Hello Win32!"));        ReleaseDC(hwnd,hdc);        break;    case WM_DESTROY:        PostQuitMessage(0);        break;    case WM_CLOSE:        if (IDYES == MessageBox(hwnd, "是否真的结束?", "message", MB_YESNO))        {            DestroyWindow(hwnd);        }        break;    default:        return DefWindowProc(hwnd, uMsg, wParam, lParam);    }    return 0;}

0 0
原创粉丝点击