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

Posted ling_xiao007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一. Windows程序内部运行机制--Windows编程课程学习笔记相关的知识,希望对你有一定的参考价值。

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;

以上是关于一. Windows程序内部运行机制--Windows编程课程学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

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

VC++深入详解 孙鑫 第一章 Windows程序内部运行机制

api函数是啥意思,有啥分类?

运行cmd后,arp-a显示:不是内部或外部命令,也不是可运行的程序或批处理程序。怎么办?

Delphi体系内部的4种消息传递办法(Send,Post,Perform,Dispatch)

如何从 Windows 任务调度程序在(ubuntu bash windows 10)上运行程序