zoukankan      html  css  js  c++  java
  • 第1章 Windows程序内部运行机制

    参考: https://blog.csdn.net/u014162133/article/details/46573873

    1、Windows API与Win32 SDK

    操作系统提供了各种方便开发Windows应用程序的编程接口,所有的函数都在Windows.h头文件中声明。Win32 SDK(Software Development Kit): 即Windows 32位平台下的软件开发包,包括API函数,帮助文档,微软提供的一些辅助开发工具。

    2、窗口与句柄

    窗口是屏幕上一块矩形区域,是Windows应用程序与用户进行交互的接口。窗口分为客户区和非客户区。在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的,要对某个窗口进行操作,首先就要得到这个窗口的句柄。其它各种资源(窗口,图标,光标等),系统在创建这些资源时会为它们分配内在,并返回标识这些资源的标识号,即句柄。比如:图标句柄(HICON)、光标句柄(HCURSOR)、画刷句柄(HBRUSH)

    3、消息与消息队列

    消息:Windows中由结构体MSG来表示,

    typedef struct tagMSG{
        HWND hwnd; //消息所属的窗口的句柄
        UINT message; //消息本身标识符,由一数值表示,系统对消息定义为WM_XXX宏(WM为Windows Message缩写)
        WPARAM wParam; //随消息的不同附加信息也不同
        LPARAM lParam; //消息的附加参数
        DWORD time; //消息投递的时间
        POINT pt; //鼠标当前位置
    }MSG;

    消息队列:每当一个Windows应用程序创建后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序的窗口的消息,消息产生后被投递到消息队列中,应用程序通过一个消息循环不断的从消息队列中取出消息进行响应。响应过程由系统向应用程序发送消息,实际就是调用应用程序的消息处理函数。

    4、创建一个完整的Win32程序,该程序实现创建一个窗口,其中主要步骤为

    (1) WinMain函数的定义

    (2) 创建一个窗口(创建一个完整的窗口需要四个步骤:设计窗口类、注册窗口类、创建窗口、显示和更新窗口)

    (3) 进行消息循环

    (4) 编写窗口过程函数

    5、

    int WINAPI WinMain(
        HINSTANCE hInstance, //当前运行的应用程序的实例句柄
        HINSTANCE hPrevInstance, //Win32环境下,这个参数总是NULL
        LPSTR lpCmdLine, //传递给应用程序的命令行参数
        int nCmdShow //程序的窗口显示状态,例如最大化、最小化、隐藏等
        );
    typedef struct _WNDCLASS {
        UINT style; //这一类型窗口的样式(CS_HREDRAW、CS_VREDRAW、CS_NOCLOSE、CS_DBLCLKS)
        WNDPROC lpfnWndProc; //是一个函数指针,指向窗口过程函数,窗口过程函数是一个回调函数
        int cbClsExtra; //一定字节数的附加内存空间(类附加内存),一般我们将这个参数设置为0
        int cbWndExtra; //一定字节数的附加内存空间(窗口附加内存),一般我们将这个参数设置为0
        HINSTANCE hInstance; //包含窗口过程的程序的实例句柄
        HICON hIcon; //窗口类的图标句柄,如果这个值为NULL,那么系统将提供一个默认的图标
        HCURSOR hCursor; //窗口类的光标句柄
        HBRUSH hbrBackground; //窗口类的背景画刷句柄,可以是一个画刷的句柄,也可以是一个标准的颜色值
        LPCTSTR lpszMenuName; //菜单资源的名字。如果使用菜单资源的ID,需要用MAKEINTRESOURCE进行转换。如果该值为NULL,那么创建的窗口没有默认的菜单。注意,菜单并不是一个窗口
        LPCTSTR lpszClassName; //窗口类的名字
    } WNDCLASS, *PWNDCLASS;
    //lpWndClass为窗口类对象的指针
    ATOM WINAPI RegisterClass(const WNDCLASS *lpWndClass);
    HWND WINAPI CreateWindow(
        LPCTSTR lpClassName, //窗口类名称
        LPCTSTR lpWindowName, //窗口名称,如果指定了标题栏,则显示在标题栏上
        DWORD dwStyle, //窗口风格,或称窗口格式,窗口类型
        int x, //窗口左上角 x 坐标
        int y, //窗口左上角y 坐标
        int nWidth, //窗口宽度
        int nHeight, //窗口高度
        HWND hWndParent, //父窗口句柄
        HMENU hMenu, //窗口菜单句柄
        HINSTANCE hInstance, //窗口所属的应用程序实例句柄
        LPVOID lpParam //作为WM_CREATE消息的附加参数传入的数据指针。在创建多文档界面的窗口时,lpParam必须指向CLIENTCREATESTRUCT结构体。多数窗口将这个参数设置为NULL
        );

    6、窗口类型WS_OVERLAPPEDWINDOW:

    7、

    8、

    //该函数通过发送一个WM_PAINT消息来刷新窗口,该消息直接发送给窗口过程函数处理,不会进消息队列。
    BOOL UpdateWindow(HWND hWnd);

    9、消息循环

    TranslateMessage函数用于将虚拟键消息转换为字符消息,比如,将WM_KEYDOWNWM_KEYUP消息的组合转换为一条WM_CHAR消息,并将转换后的新消息投递到消息队列中。

    从消息队列中获取消息,也可以用PeekMessage

    BOOL PeekMessage(
        LPMSG lpMsg,
        HWND hWnd,
        UINT wMsgFilterMin,
        UINT wMsgFilterMax,
        UINT wRemoveMsg
        );

    4个参数和GetMessage4个参数的作用相同。最后一个参数指定消息的获取方式,如果设为PM_NOREMOVE,那么消息不会从消息队列中移除;如果设为PM_REMOVE,那么消息会从消息队列中移除(与GetMessage函数的行为一致)。

    10、回调函数

    LRESULT CALLBACK WindowProc(
        HWND hwnd, //窗口句柄
        UINT uMsg, //消息标识码
        WPARAM wParam, //附加参数
        LPARAM lParam //附加参数
        );

    回调函数的实现机制:

    (1) 定义一个回调函数

    (2) 提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者

    (3) 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理

    针对Windows的消息处理机制,窗口过程函数被调用的过程如下:

    (1) 在设计窗口类的时候,将窗口过程函数的地址赋值给lpfnWndProc成员变量

    (2) 调用RegisterClass(&wndclass)注册窗口类,那么系统就有了我们所编写的窗口过程函数的地址

    (3) 当应用程序接收到某一窗口的消息,调用DispatchMessage(&msg)将消息回传给系统。系统则利用先前注册窗口类时得到函数指针,调用窗口过程函数对消息进行处理。

    11、

    (10)

    //加载图标资源,返回系统分配给该图标的句柄,如果加载的是系统的标准图标,第一个参数必须为NULL,
    //LPCTSTR被定义为CONST CHAR*(指向常量的字符指针),图标的ID是一个常数,
    //要使用MAKEINTRESOUCE宏把资源ID标识转换为需要的LPCTSTR类型
    HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
    
    //使用方法参考LoadIcon
    HCURSOR LoadCursor(HINSTANCE hlnstance,LPCTSTR lpCursorName);

    (11) sprintf格式化字符,其头文件为stdio.h,在MFC中格式化字符用CString.Format
    (12) GetDC()与ReleaseDC()要成对使用,否则会内存泄漏。同样,BeginPaint()与EndPaint(),这两个Parint只能在WM_PAINT消息中调用。

    (13) HGDIOBJ GetStockObject(int fnObject); 获取画笔、画刷、字体、调色板的句柄,由于该函数可以返回多种资源对象的句柄,使用时需要对返回值进行类型转换,

    如:hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH ) //创建空画刷

    (14) 什么时候用NULL,什么时候用0。答,对指针赋值时用NULL,对变量赋值时用0。 

    (15) 什么是野指针?答:将指针指向的内存释放后,此指针即变成野指针!

    如何避免野指针?答:将此指针指向NULL即可,p=NULL;

    12、

    #include <stdio.h>
    #include <windows.h>
    #include <stdexcept>
    using namespace std;
    
    //回调函数原型声明,返回长整形的结果,CALLBACK表示stdcall调用
    LRESULT CALLBACK WinProc(
                                HWND hwnd,      // handle to window
                                UINT uMsg,      // message identifier
                                WPARAM wParam,  // first message parameter
                                LPARAM lParam   // second message parameter
    );
    
    //(1) WinMain函数,程序入口点函数
    int WINAPI WinMain(
                       HINSTANCE hInstance,      // handle to current instance
                       HINSTANCE hPrevInstance,  // handle to previous instance
                       LPSTR lpCmdLine,          // command line
                       int nCmdShow              // show state
                       ){
        //(2)
        //一.设计一个窗口类,类似填空题,使用窗口结构体
        WNDCLASS wnd;
        wnd.cbClsExtra = 0; //类的额外内存
        wnd.cbWndExtra = 0;    //窗口的额外内存
        wnd.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);//创建一个空画刷填充背景
        //加载游标,如果是加载标准游标,则第一个实例标识设置为空
        wnd.hCursor = LoadCursor(NULL, IDC_CROSS);
        wnd.hIcon = LoadIcon(NULL, IDI_ERROR);
        wnd.hInstance = hInstance;//实例句柄赋值为程序启动系统分配的句柄值
        wnd.lpfnWndProc = WinProc;//消息响应函数
        wnd.lpszClassName = "gaojun";//窗口类的名子,在注册时会使用到
        wnd.lpszMenuName = NULL;//默认为NULL没有标题栏
        wnd.style = CS_HREDRAW | CS_VREDRAW;//定义为水平和垂直重画
        //二.注册窗口类
        RegisterClass(&wnd);
        //三.根据定制的窗口类创建窗口
        HWND hwnd;//保存创建窗口后的生成窗口句柄用于显示
        //如果是多文档程序,则最后一个参数lParam必须指向一个CLIENTCREATESTRUCT结构体
        hwnd = CreateWindow("gaojun", "WIN32应用程序", WS_OVERLAPPEDWINDOW,
     CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL);
        //四.显示窗口
        ShowWindow(hwnd, SW_SHOWDEFAULT);
        //五.更新窗口
        UpdateWindow(hwnd);
        
        //(3).消息循环
        MSG msg;//消息结构体
        //如果消息出错,返回值是-1,当GetMessage从消息队列中取到是WM_QUIT消息时,返回值是0
        //也可以使用PeekMessage函数从消息队列中取出消息
        BOOL bSet;
        while((bSet = GetMessage(&msg, NULL, 0, 0)) != 0){
            if (-1 ==  bSet)
            {
                return -1;
            }
            else{
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        return 0;//程序结束,返回0
    }
    
    //消息循环中对不同的消息各类进行不同的响应
    LRESULT CALLBACK WinProc(
                             HWND hwnd,      // handle to window
                             UINT uMsg,      // message identifier
                             WPARAM wParam,  // first message parameter
                             LPARAM lParam   // second message parameter
                             ){
        switch (uMsg)
        {
        case WM_CHAR://字符按键消息
            char szChar[20];
            sprintf(szChar, "char is %d;", wParam);//格式化操作,stdio.h
            MessageBox(hwnd, szChar, "gaojun", 0);//输出操作windows.h中
            break;
        case WM_LBUTTONDOWN://鼠标左键按下消息
            MessageBox(hwnd, "this is click event!", "点击", 0);
            HDC hdc;
            hdc = GetDC(hwnd);//获取设备上下文句柄,用来输出文字
            //在x=0,y=50(像素)的地方输出文字
            TextOut(hdc, 0, 50, "响应WM_LBUTTONDONW消息!", 
    strlen("响应WM_LBUTTONDONW消息!"));
            ReleaseDC(hwnd, hdc);//在使用完DC后一定要注意释放
            break;
        case WM_PAINT://窗口重给时报消息响应
            HDC hDc;
            PAINTSTRUCT ps;
            hDc = BeginPaint(hwnd, &ps);
            TextOut(hDc, 0, 0, "这是一个Paint事件!", strlen("这是一个Paint事件!"));
            EndPaint(hwnd, &ps);
            break;
        case WM_CLOSE://关闭消息
            if (IDYES == MessageBox(hwnd, "确定要关闭当前窗口?", "提示", MB_YESNO))
            {
                DestroyWindow(hwnd);//销毁窗口
            }        
            break;
        case WM_DESTROY:
            PostQuitMessage(0);//在响应消息后,投递一个退出的消息使用程序安全退出
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);//调用缺省的消息处理过程函数
        }
        return 0;
    }
  • 相关阅读:
    leetcode 347. Top K Frequent Elements
    581. Shortest Unsorted Continuous Subarray
    leetcode 3. Longest Substring Without Repeating Characters
    leetcode 217. Contains Duplicate、219. Contains Duplicate II、220. Contains Duplicate、287. Find the Duplicate Number 、442. Find All Duplicates in an Array 、448. Find All Numbers Disappeared in an Array
    leetcode 461. Hamming Distance
    leetcode 19. Remove Nth Node From End of List
    leetcode 100. Same Tree、101. Symmetric Tree
    leetcode 171. Excel Sheet Column Number
    leetcode 242. Valid Anagram
    leetcode 326. Power of Three
  • 原文地址:https://www.cnblogs.com/happykoukou/p/8779149.html
Copyright © 2011-2022 走看看