Win32 程序开发:创建一个应用程序窗口

本文详细介绍了一个简单的Windows应用程序的创建步骤,包括设计窗口类、注册窗口类、创建窗口、更新显示窗口及应用程序消息循环等关键环节,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1)简单介绍创建应用程序的步骤

1.设计一个窗口类

2.注册这个窗口类

3.创建应用程序窗口

4.更新显示窗口

5.应用程序消息循环

2)下面根据这个步骤进行创建一个应用程序窗口吧(编译器为VS2017)


/* 头文件 */
#include <windows.h>

/* 全局变量 */
WCHAR g_lpszClassName[] = L"CLASSNAME";
WCHAR g_lpszWindowName[] = L"哈喽,新的征程";

/* 函数声明 */
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

/* 应用程序主函数 */
INT APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, INT nCmdShow)
{
    /* 1.设计一个窗口类 */
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;        
    wcex.cbClsExtra = 0;    
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = WndProc;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = g_lpszClassName;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    /* 2.注册这个窗口类 */
    if (RegisterClassEx(&wcex) == ((ATOM)0))
    {
        MessageBox(NULL, L"注册窗口类失败!", L"错误", MB_YESNO | MB_ICONERROR);
        exit(-1);
    }

    /* 3.创建窗口 */
    HWND hWnd = CreateWindowEx(NULL, g_lpszClassName, g_lpszWindowName, WS_OVERLAPPEDWINDOW, 10, 10, 800, 800, NULL, NULL, hInstance, NULL);
    if (hWnd == NULL)
    {
        MessageBox(NULL, L"创建窗口失败!", L"错误", MB_YESNO | MB_ICONERROR);
        exit(-1);
    }

    /* 4.更新显示窗口 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* 5.应用程序消息循环 */
    MSG msg = { 0 };
    BOOL bRet;
    /* GetMessage 发生错误会返回-1 */
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet != -1)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}

/* 应用程序消息处理回调函数 */
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

下面进行讲解上面的程序:

1.首先设计一个窗口类(不懂可以看我的这篇文章,此处就不多赘述了)

2.注册这个设计的窗口类调用RegisterClassEx函数进行注册

3.进行创建窗口,调用CreateWindowEx函数进行创建

4.更新显示此窗口,分别调用ShowWindow(显示窗口)和UpdateWindow(更新窗口)

5.应用程序消息循环,通过while循环

6.窗口消息处理过程

1.设计一个窗口类(...)

2.注册这个设计的窗口类

①RegisterClassEx函数讲解(注册窗口类函数,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

ATOM WINAPI RegisterClassExW(_In_ CONST WNDCLASSEXW *);

_In_ CONST WNDCLASSEXW *: 为我们设计的窗口类

返回值(ATOM也就是原子的意思):成功返回注册的原子,失败返回0

②示例中的使用

所以我们只需要通过返回值判断是否为注册失败,注册失败返回0,0也就是false

if (RegisterClassEx(&wcex) == ((ATOM)0)) { 提示注册失败信息 }

3.进行创建窗口

①CreateWindowEx函数讲解(注册窗口类函数,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)(用点多,希望认真看完)

HWND WINAPI CreateWindowExW(
    _In_ DWORD dwExStyle,
    _In_opt_ LPCWSTR lpClassName,
    _In_opt_ LPCWSTR lpWindowName,
    _In_ DWORD dwStyle,
    _In_ int X,
    _In_ int Y,
    _In_ int nWidth,
    _In_ int nHeight,
    _In_opt_ HWND hWndParent,
    _In_opt_ HMENU hMenu,
    _In_opt_ HINSTANCE hInstance,
    _In_opt_ LPVOID lpParam);

dwExStyle:                窗口扩展风格

lpClassName:           我们注册的窗口类的名称

lpWindowName:       窗口的名称(说白了就是窗口的标题)

dwStyle:                    窗口创建的风格(跟dwExStyle不同哦)

X:                               窗口左上角的点位于屏幕的横坐标

Y:                               窗口左上角的点位于屏幕的纵坐标

nWidth:                     窗口的宽度

nHeight:                    窗口的高度

hWndParent:            窗口所有者的句柄

hMenu:                      菜单句柄

hInstance:                 所在模块的实例句柄

lpParam:                    在WM_CREATE中进行传递的参数

返回值:                       创建成功返回创建的窗口句柄,失败返回NULL

②示例中的使用 

HWND hWnd = CreateWindowEx(NULL, g_lpszClassName, g_lpszWindowName, WS_OVERLAPPEDWINDOW, 
                            10, 10, 800, 800, NULL, NULL, hInstance, NULL);

窗口的扩展风格为NULL,也就是没有扩展风格

窗口类的名称是g_lpszClassName("CLASSNAME")

窗口名称是g_lpszClassName("哈喽,新的征程")

窗口的风格为WS_OVERLAPPEDWINDOW

窗口的位于屏幕的横纵坐标为(10, 10)

窗口的宽和高为(800, 800)

窗口的所有者为NULL,也就是没有所有者

窗口的菜单句柄为NULL,也就是没有菜单

窗口的所在模块的实例句柄,为操作系统在WinMain中传递过来的示例句柄

在WM_CREATE中传递的参数为NULL,也就是传递一个NULL的参数

最后通过返回值进行判断是否创建成功:

if (hWnd == NULL) { 创建窗口失败的提示信息 }

4.更新显示此窗口

①ShowWindow函数讲解(显示窗口,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

BOOL WINAPI ShowWindow(
    _In_ HWND hWnd,
    _In_ int nCmdShow);

hWnd:显示的窗口句柄

nCmdShow:窗口显示的方式(全屏,最大化窗口,最小化窗口,······)

返回值:成功返回非0,失败返回0

②UpdateWindow函数讲解(显示窗口,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

BOOL WINAPI UpdateWindow(
    _In_ HWND hWnd);

hWnd:更新的窗口

返回值:成功返回非0,失败返回0

③示例中的使用

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

hWnd为CreateWindowEx中创建的窗口

nCmdShow为窗口的显示的方式(在WinMain由操作系统传入)

创建窗口后,还没有显示,所以需要调用ShowWindow进行显示窗口。

可是显示过后为何还要更新?

这是因为显示后还要通过更新窗口进行显示窗口中的东西(比如:图片,文字,······等)

5.应用程序消息循环

①GetMessage函数讲解(从调用线程的消息队列中取得一个消息,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

BOOL WINAPI GetMessageW(
    _Out_ LPMSG lpMsg,
    _In_opt_ HWND hWnd,
    _In_ UINT wMsgFilterMin,
    _In_ UINT wMsgFilterMax);

lpMsg:接收从消息队列中获取的消息

hWnd:接收消息的窗口句柄

wMsgFilterMin:指定被检测的最小消息值

wMsgFilterMax:指定被检测的最大消息值

返回值:获取错误返回-1,消息为WM_QUIT返回0,其他为非0

②TranslateMessage函数讲解(将虚拟键消息转换为字符消息,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

BOOL WINAPI TranslateMessage(
    _In_ CONST MSG *lpMsg);

lpMsg:接收的消息

返回值:成功返回非0,失败返回0

③DispatchMessage函数讲解(将消息分发到窗口处理,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

LRESULT WINAPI DispatchMessageW(
    _In_ CONST MSG *lpMsg);

lpMsg:分发的消息

返回值:窗口处理过程的返回值

④示例中的使用

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet != -1)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

while循环的结束,当GetMessage获取到的消息为WM_QUIT窗口退出,则返回0(false)
当GetMessage的返回值不为-1(获取消息失败)的时候,进行虚拟键转换和分发消息

6.窗口消息处理过程

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

参数:

hWnd:      窗口处理过程的窗口句柄

message:消息ID

wParam:  附加消息

lParam:    附加消息

①PostQuitMessage(线程终止请求,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

VOID WINAPI PostQuitMessage(
    _In_ int nExitCode);

nExitCode:程序退出代码

②DefWindowProc(缺省的窗口处理过程,winuser.h中提供的函数)

函数原型:(代码来源于VS2017)

LRESULT CALLBACK DefWindowProcW(
    _In_ HWND hWnd,
    _In_ UINT Msg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam);

参数和返回值跟我们的窗口处理过程一样

这个函数的意义就是我们不想处理的消息就交给他吧

小插曲:

return msg.wParam

其实这个跟main的return是一样的,但对于初学者会有疑问,为什么不是return 0

解释部分:

当GetMessage获取到的消息为WM_DESTROY(销毁窗口)的时候调用我们的处理:

case WM_DESTROY:
    PostQuitMessage(0);
    break;

当我们调用PostQuitMessage传入的nExitCode(程序退出代码,0为正常退出,非0为异常)

调用了PostQuitMessage后会向消息队列中添加一个WM_QUIT(窗口退出消息)

然后GetMessage接收到WM_QUIT消息的MSG

MSG的wParam为PostQuitMessage中的nExitCode

所以最后return的值也就是PostQuitMessage的nExitCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值