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

原文地址: https://blog.csdn.net/ling_xiao007/article/details/51720652

1.1 API与SDK

操作系统把它所能够完成的功能以函数的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是<u style="box-sizing: border-box; outline: 0px; word-break: break-all;">Windows应用程序编程接口</u>(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程序运行的机制。

1.png

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

1.4 WinMain函数

完整的Win32程序步骤如下:

1)入口函数

2)创建窗口

3)消息循环与发送消息

4)窗口过程与消息响应

1)入口函数:

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 把一个最小的需要重绘的正方形区域叫做<u style="box-sizing: border-box; outline: 0px; word-break: break-all;">“无效区域”</u>。发现了一个“无效区域“后,它就会向该应用程序发送一个 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")
2.png

程序运行界面


3.png
#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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容