Win32入门笔记(一)

image-20201215100931578

创建窗口

首先win32窗口的创建主要分为两个步骤,注册一个窗口类(注意窗口类并不是C++类,只是一个Window的数据结构),实现一个窗口实例。

image-20201214144740309

WinMain entry point入口函数

CALLBACK 修饰符告诉编译器如何调用这个函数 ,stdcall

hInstance 实例的句柄

hPrevInstance 没有任何意义?

pCmdLine 命令行参数

nCmdShow 用于设置窗口被创建之后应该如何展示

INT CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR pCmdLine, INT nCmdShow)
{
    return 0;
}

在注册窗口类之前,我们需要填充WNDCLASSEX结构

cbsize:wc数据接口的大小

style:窗口风格,CS_OWNDC意思为每个窗口都可以独立渲染,多用于同时绘制多个窗口

lpfnWndProc:指向WINPROC函数的指针,这个函数处理所有有关这个窗口的信息,这个函数非常重要,因为窗口信息决定了窗口的行为,同时决定了窗口的样式,毕竟里面有Draw功能绘制窗口

cbClsExtra:可用来存储数据

cbWndExtra:用于为每个窗口实例分配字节

hIcon:定义应用程序图标

hIconsm:定义窗口标题栏图标

hCursor:定义鼠标

hbrBackground:定义背景,由于我们绘制由DirectX来执行,所以这里不需要背景

lpszMenuName:菜单名称

lpszClassName:窗口类名称,我定义为 Opda Window Class

设置好属性之后就可以进行注册窗口类了。

    // 注册窗口类

    WNDCLASSEX wc = {};

    // 窗口类名
    const wchar_t CLASS_NAME[] = L"Opda Window Class";

    // 设置属性
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    wc.lpfnWndProc = DefWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = nullptr;
    wc.hIconSm = nullptr;
    wc.hCursor = nullptr;
    wc.hbrBackground = nullptr;
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = CLASS_NAME;

    RegisterClassEx(&wc);

接下来我们需要创建窗口实例

    //创建窗口实例
    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, L"Hello World",
        WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
        200,200,640,480,
        nullptr,nullptr,hInstance,nullptr
        );
HWND CreateWindowExA(
  DWORD     dwExStyle,    窗口扩展样式
  LPCSTR    lpClassName,    实例窗口类的名称,与wc的lpszClassName相对应
  LPCSTR    lpWindowName,    窗口名称
  DWORD     dwStyle,    窗口样式
  int       X,    窗口位置X,Y
  int       Y,
  int       nWidth,        窗口宽度
  int       nHeight,    窗口高度
  HWND      hWndParent,    窗口的父窗口
  HMENU     hMenu,    窗口菜单
  HINSTANCE hInstance,    实例
  LPVOID    lpParam    可以用来存储传递数据
);

最后我们需要将窗口显示出来

    ShowWindow(hwnd,SW_SHOW);
    while (true);
    return 0;

得到效果

image-20201214162127020

窗口消息

事件驱动编程,窗口消息本质上来说就是事件Event

窗口会处理比如鼠标移动,鼠标点击消息事件,把消息按顺序放进队列里,我们利用GetMessage获取队列的消息,利用TranslateMsg进行消息的转换(比如将键盘点击转换为字符),DispatchMessage将消息返回到WndProc进行消息处理

image-20201214210636986

首先我们需要一个消息结构体,用于保存消息数据,然后把这个结构体指针传进GetMessage

如果hwnd为null,则所有窗口的消息都会被接受

BOOL GetMessage(
  LPMSG lpMsg,    消息指针
  HWND  hWnd,    处理消息的窗口指针
  UINT  wMsgFilterMin,    过滤选项,用于过滤得到想要的消息
  UINT  wMsgFilterMax
);

消息结构体

typedef struct tagMSG {
  HWND   hwnd;    窗口指针
  UINT   message;    消息类型
  WPARAM wParam;    保存数据    
  LPARAM lParam;
  DWORD  time;    消息发布的时间
  POINT  pt;    鼠标的位置
  DWORD  lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;

只要GetMessage返回数值 > 0则表明信息接收没有问题,继续循环,当返回0时表示窗口关闭退出循环

//消息循环
    MSG message;
    while (GetMessage(&message,nullptr,0,0) > 0)
    {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

这时候会出现一个问题,当你关掉窗口之后,窗口只是被最小化还会在后台运行,所以我们要自定义winProc解决问题,同时需要将窗口结构体的lpfnWndProc设置为我们自定义的windowProc

wParam和lParam包含与消息相关的数据,主要取决与消息的类型

比如WM_QUIT message的wParam是PostQuitMessage给出的数字69,lParam则没有是用到的

image-20201214220730551

// 自定义WindowProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:    //接受到关闭窗口消息就结束进程
        PostQuitMessage(69);
    default:
        break;
    }

    //处理完特定的消息之后,进行默认消息处理
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

.....

wc.lpfnWndProc = WindowProc;

.....


    //出现错误
    if (gResult == -1)
    {
        return -1;
    }
    else
    {
        return message.wParam;    //返回69
    }

得到可以拖动,正常关闭结束进程的窗口

全部代码

#include "Windows.h"

// 自定义WindowProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:    //接受到关闭窗口消息就结束进程
        PostQuitMessage(69);
    default:
        break;
    }

    //处理完特定的消息之后,进行默认消息处理
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


// 创建WinMain entry point
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR pCmdLine, int nCmdShow)
{
    // 注册窗口类

    WNDCLASSEX wc = {};

    // 窗口类名
    const wchar_t CLASS_NAME[] = L"Opda Window Class";

    // 设置属性
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = nullptr;
    wc.hIconSm = nullptr;
    wc.hCursor = nullptr;
    wc.hbrBackground = nullptr;
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = CLASS_NAME;

    RegisterClassEx(&wc);

    //创建窗口实例
    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, L"Hello World",
        WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
        200,200,640,480,
        nullptr,nullptr,hInstance,nullptr
        );

    ShowWindow(hwnd,SW_SHOW);

    //消息循环
    MSG message;
    //用于保存GetMessage返回的结果
    BOOL gResult;
    while (gResult = GetMessage(&message,nullptr,0,0) > 0)
    {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    //出现错误
    if (gResult == -1)
    {
        return -1;
    }
    else
    {
        return message.wParam;
    }
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2021 Opda
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信