Win32程序HelloWorld 第一个Win32窗体程序

⌚Time: 2023-01-07 21:11:08

👨‍💻Author: Jack Ge

WinMain函数

WinMain 是用于应用程序入口点的传统名称。当windows系统启动程序时,调用的就是WinMain程序


int __clrcall WinMain(

  [in] HINSTANCE hInstance,//应用程序当前实例句柄

  [in] HINSTANCE hPrevInstance,//应用程序上一个实例句柄

  [in] LPSTR     lpCmdLine,//应用程序的命令行,不包括程序名称。 

  [in] int       nShowCmd//控制窗口的显示方式。

);


创建窗口

1.设计一个窗口类

一个完整的窗体具有图标、背景、光标等特征。对于win32程序,可以通过WNDCLASS结构体中预先定义好的属性去设计一个窗口。


typedef struct tagWNDCLASSA {

  UINT      style;

  WNDPROC   lpfnWndProc;

  int       cbClsExtra;

  int       cbWndExtra;

  HINSTANCE hInstance;

  HICON     hIcon;

  HCURSOR   hCursor;

  HBRUSH    hbrBackground;

  LPCSTR    lpszMenuName;

  LPCSTR    lpszClassName;

} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;


style

窗体类风格

lpfnWndProc

指向窗口过程的指针。必须使用CallWindowProc函数来调用窗口过程。在应用程序中定义的回调函数,用于处理发送到窗口的消息。

hInstance

包含类的窗口过程的实例的句柄。

hIcon

类图标的句柄。此成员必须是图标资源的句柄。如果此成员为NULL,系统将提供默认图标。

hCursor

类游标的句柄。此成员必须是游标资源的句柄。如果此成员为NULL,则每当鼠标移动到应用程序的窗口中时,应用程序必须显式设置光标形状。

lpszMenuName

类菜单的资源名称,如果此成员为NULL,则属于此类的窗口没有默认菜单。

lpszClassName

窗体类名称

窗口设计完成后,需要使用RegisterClass对其进行注册,注册成功后,才可以进行创建。就像汽车生产,需要有关部门注册审批后才可以进行生产


ATOM RegisterClassA(

  [in] const WNDCLASSA *lpWndClass

);

2.创建窗口

设计好窗口并且注册成功后,就需要用CreateWindow函数创建窗口


HWND CreateWindowExA(

  [in]           DWORD     dwExStyle,

  [in, optional] LPCSTR    lpClassName,//注册的窗体类名称

  [in, optional] LPCSTR    lpWindowName,//窗体名称

  [in]           DWORD     dwStyle,//窗体样式

  [in]           int       X,//窗体水平位置

  [in]           int       Y,//窗体垂直位置

  [in]           int       nWidth,//窗体宽度

  [in]           int       nHeight,//窗体高度

  [in, optional] HWND      hWndParent,//父窗体句柄

  [in, optional] HMENU     hMenu,//菜单句柄

  [in, optional] HINSTANCE hInstance,//实例句柄

  [in, optional] LPVOID    lpParam//窗体创建数据指针

);

如果lpClassName是字符串,则指定窗口类名。类名可以是用RegisterClass或RegisterClassEx注册的任何名称,前提是注册该类的模块也是创建窗口的模块。类名也可以是任何预定义的系统类名。

hMenu

菜单的句柄,或指定子窗口标识符,具体取决于窗口样式。对于重叠或弹出窗口,hMenu标识要与窗口一起使用的菜单;如果要使用类菜单,则可以为NULL。对于子窗口,hMenu指定子窗口标识符,一个由对话框控件用于通知其父窗口事件的整数值。应用程序确定子窗口标识符;对于具有相同父窗口的所有子窗口,它必须是唯一的。

3.显示窗口

窗口创建后,需要进行显示


BOOL ShowWindow(

  [in] HWND hWnd,//窗体句柄

  [in] int  nCmdShow//显示状态

);

消息循环

程序的执行机制可以分为两类,一类是过程驱动,按照实现编辑好的流程按部就班的执行。一类是事件驱动,每个事件的发生都是随机的,针对不同的事件执行不同的处理流程。

win32程序是基于事件驱动的程序。系统事件和用户事件会产生消息,,创建窗口会触发WM_CREATE消息,绘制窗口会触发WM_PAINT消息,我们点击鼠标、键盘、都会触发相应的消息。


BOOL GetMessage(

  [out]          LPMSG lpMsg,//消息结构体地址

  [in, optional] HWND  hWnd,//窗体句柄

  [in]           UINT  wMsgFilterMin,//最先的消息

  [in]           UINT  wMsgFilterMax//最后的消息

);

BOOL TranslateMessage(

  [in] const MSG *lpMsg

);

LRESULT DispatchMessage(

  [in] const MSG *lpMsg

);

编写一个消息循环,不断的从消息队列中取出消息并且进行响应


    MSG msg = { };

    while (GetMessage(&msg, NULL, 0, 0) > 0)

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

GetMessage在收到WM_QUIT消息后返回0,此时退出循环,程序才可以结束,否则程序就通过这个while循环来保证程序始终处于运行状态!

窗口过程函数

DispatchMessage 函数调用消息目标窗口的窗口过程。窗口过程函数的形式:


LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

参数分别是窗体句柄,消息代码和消息代码的两个附加参数

典型的窗口过程只是一个在消息代码上切换的大型 switch 语句。 添加要处理的每个消息的事例。

例子:


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

 {

     switch (message)

     {

     case WM_CREATE:

         //窗体创建完成消息处理

         ...

         break;

     case WM_TIMER:

         //定时器消息处理

         ...

         break;

     case WM_PAINT:

         //窗体重绘消息

         ...

         break;

     case WM_DESTROY:

         //销毁

         PostQuitMessage(0);

         break;

     default:

         //如果不在窗口过程中处理特定消息,请将消息参数直接传递给 DefWindowProc 函数。 此函数对消息执行默认操作,该操作因消息类型而异

         return DefWindowProc(hWnd, message, wParam, lParam);

     }

     return 0;

 }

使用VS建立第一个Win32程序

建立项目

新建Win32项目

选择windows应用程序,空项目,点击完成

右键项目,添加,新建项,选择C++文件,建立一个源程序文件

编写ANSI字符集程序

项目,属性,配置属性,常规,字符集,选择多字节字符集

编辑代码


#include <windows.h>



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)

{

    // Register the window class.

    const char CLASS_NAME[]  = "Sample Window Class";



    WNDCLASSA wc = { };



    wc.lpfnWndProc   = WindowProc;

    wc.hInstance     = hInstance;

    wc.lpszClassName = CLASS_NAME;



    RegisterClassA(&wc);



    // Create the window.



    HWND hwnd = CreateWindowExA(

        0,                              // Optional window styles.

        CLASS_NAME,                     // Window class

        "My first window",    // Window text

        WS_OVERLAPPEDWINDOW,            // Window style



        // Size and position

        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,



        NULL,       // Parent window    

        NULL,       // Menu

        hInstance,  // Instance handle

        NULL        // Additional application data

        );



    if (hwnd == NULL)

    {

        return 0;

    }



    ShowWindow(hwnd, nCmdShow);



    // Run the message loop.



    MSG msg = { };

    while (GetMessage(&msg, NULL, 0, 0) > 0)

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }



    return 0;

}



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;



    case WM_PAINT:

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);



            // All painting occurs here, between BeginPaint and EndPaint.



            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));



            EndPaint(hwnd, &ps);

        }

        return 0;



    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

编译运行,效果

Unicode字符集程序

项目,属性,配置属性,常规,字符集,选择Unicode字符集

代码


#ifndef UNICODE

#define UNICODE

#endif 



#include <windows.h>



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)

{

    // Register the window class.

    const wchar_t CLASS_NAME[]  = L"Sample Window Class";



    WNDCLASSW wc = { };



    wc.lpfnWndProc   = WindowProc;

    wc.hInstance     = hInstance;

    wc.lpszClassName = CLASS_NAME;



    RegisterClassW(&wc);



    // Create the window.



    HWND hwnd = CreateWindowExW(

        0,                              // Optional window styles.

        CLASS_NAME,                     // Window class

        L"My first window",    // Window text

        WS_OVERLAPPEDWINDOW,            // Window style



        // Size and position

        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,



        NULL,       // Parent window    

        NULL,       // Menu

        hInstance,  // Instance handle

        NULL        // Additional application data

        );



    if (hwnd == NULL)

    {

        return 0;

    }



    ShowWindow(hwnd, nCmdShow);



    // Run the message loop.



    MSG msg = { };

    while (GetMessage(&msg, NULL, 0, 0) > 0)

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }



    return 0;

}



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;



    case WM_PAINT:

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);



            // All painting occurs here, between BeginPaint and EndPaint.



            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));



            EndPaint(hwnd, &ps);

        }

        return 0;



    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}




编译运行即可