C++使用代码创建一个Windows桌面应用程序

WinMain函数

Windows应用程序的唯一程序入口。

函数原型

1 int WINAPI WinMain
2 {
3     HINSTANCE hInstancem
4     HINSTANCE hPreInstance,
5     LPSTR lpCmdLine,
6     int nCmdShow 
7 }

WINAPI定义如下

#define WINAPI _stdcall

_stdcall是一个函数调用约定,除此之外,还有__cdeclfastcallthiscallnaked call等函数调用约定。

_stdcall调用约定又称Pascal调用约定,也是Pascal语言的调用约定。它使用的方式为:

1 int __stdcall sum(int a,int b);

__stdcall:函数的多个参数由调用者按从右到左的顺序压入堆栈,被调用函数获得参数的序列是从左到右的的;清理堆栈的工作由被调用函数负责。
在Visual C++中,常用宏WINAPICALLBACK来表示__stdcall调用约定。

更详细的说明可以查看

https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2019

__cdecl(也可写成_cdecl)调用约定又称C调用约定,是C函数默认的调用约定,也是C++全局函数的默认调用约定,通常省略。

1 int sum(int a,int b);
2 int __cdecl sum(int a,int b);

__cdecl:函数的多个参数由调用者按从右向左的顺序压入堆栈,被调函数获得参数的序列是从左到右的;清理堆栈的工作由调用者负责

更详细的说明可以查看
https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019

WinMain函数的各参数说明

hInstance

应用程序当前运行的实例的句柄,该句柄由Windows系统生成。

hPrevInstance

当前实例的前一个实例的句柄,在Win32环境下,该参数总是NULL,不再起作用

lpCmdLine

一个以空终止的字符串,代表传递给程序的命令行参数。

nCmdShow

指定窗口的显示状态

常用值如下

nCmdShow = 0;不显示

nCmdShow = 1;正常显示(默认)

nCmdShow = 2;最小化显示

nCmdShow = 3;最大化显示

使用代码创建Windows程序的步骤

1、设计一个Windows类

2、在Windows系统中注册Windows类

3、用该Windows类创建一个窗口

4、显示窗口

5、创建一个消息循环

6、创建一个窗口过程函数WndProc

一、设计Windows类

在创建一个窗口前,必须对窗口进行设计,指定窗口的属性。系统已经定义了WNDCLASS结构用于描述待创建窗口的参数。

WNDCLASS声明如下

 1 typedef struct tagWNDCLASSA {
 2   UINT      style;
 3   WNDPROC   lpfnWndProc;
 4   int       cbClsExtra;
 5   int       cbWndExtra;
 6   HINSTANCE hInstance;
 7   HICON     hIcon;
 8   HCURSOR   hCursor;
 9   HBRUSH    hbrBackground;
10   LPCSTR    lpszMenuName;
11   LPCSTR    lpszClassName;
12 } WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

下面介绍各参数

style

窗口样式,可用值如下

CS_VREDRAW:垂直重绘,当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,在垂直方向上调整窗口高度时,将不会重绘窗口。

CS_HREDRAW:水平重绘,当窗口水平方向上的宽度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,在水平方向上调整窗口高度时,将不会重绘窗口。

CS_OWNDC:独占设备描述表,为该类中的每个窗口分配一个单值的设备描述表。

CS_SAVEBITS:在一个窗口中保存用户图像,以便于在该窗口被遮住、移动时不必每次刷新屏幕。但是,这样会占用更多的内存,并且比人工进行同样操作时要慢得多。

CS_DBLCLKS:使窗口可以检测到鼠标双击事件,当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息

CS_BYTEALLGNCLIENT:鼠标用户区域按字节对齐显示。

CS_BYTEALLGNWINDOW:鼠标用户窗口按字节对齐显示。

CS_PARENTDC:在父窗口中设定一个子窗口的剪切区,以便于子窗口能够画在父窗口中。

CS_NOCLOSE:系统菜单中没有CLOSE菜单项,窗口没有关闭按钮。

lpfnWndProc

指向窗口过程函数的函数指针。窗口过程函数是一个回调函数,针对Windows的消息处理机制,窗口过程函数被调用的过程如下:

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

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

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

cbClsExtra

Windows系统为窗口类结构分配追加的额外字节数。一般为0

cbWndExtra

Windows系统为窗口实例分配或追加的额外字节数,一般为0。如果应用程序使用资源文件里的CLASS指令创建对话框,并用WNDCLASS结构注册对话框框时,cbWndExtra必须设置成DLGWINDOWEXTRA

hInstance

包含窗口过程程序的实例句柄。一般直接赋WinMain()的hInstance即可

hIcon

窗口类的图标资源。这个成员变量必须是一个图标资源的句柄。可以使用LoadIcon()函数加载图标,如果hIcon为NULL,窗口将使用系统提供的默认图标

hCursor

窗口类的光标句柄。这个成员变量必须是一个光标资源的句柄。可以使用LoadCursor()函数加载光标。如果hCursor为NULL,应用程序必须在鼠标进入应用程序窗口时,明确设置光标的形状

hbrBackground

窗口类的背景画刷句柄。当窗口发生重绘时,系统使用这里指定的画刷来填充窗口的背景。该成员可以指定为用于绘制背景的物理画刷的句柄,也可以指定为标准的系统颜色值。如下:

BLACK_BRUSH 黑色

DKGRAY_BRUSH 深灰

GRAY_BRUSH 灰色

HOLLOW_BRUSH 空

LTGRAY_BRUSH 浅灰

NULL_BRUSH 等同于HOLLOW_BRUSH

WHITE_BRUSH 白色

BLACK_BRUSH 黑色

lpszMenuName

指向一个以空终止的字符串,该字符串描述菜单的资源名。若使用整数来标识菜单,需要用MAKEINTRESOURCE宏来进行转换。如果lpszMenuName设置为NULL,那么基于窗口类创建的窗口将没有默认菜单

lpszClassName

指向一个以空终止的字符串,该字符串描述窗口类的名字。这个类名可以是由RegisterClass或者RegisterClassEx注册的名字,或者是任何预定义的控件类名

WNDCLASS使用实例如下

 1 WNDCLASS wc;
 2 
 3     wc.style          = CS_HREDRAW | CS_VREDRAW;
 4     wc.lpfnWndProc    = WndProc;
 5     wc.cbClsExtra     = 0;
 6     wc.cbWndExtra     = 0;
 7     wc.hInstance      = hInstance;
 8     wc.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
 9     wc.hCursor        = LoadCursor(nullptr, IDC_ARROW);
10     wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
11     wc.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
12     wc.lpszClassName  = szWindowClass;

二、注册Windows类

Windows类设计完成时,需要调用RegisterClass()函数去注册这个类,才可以创建该类型的窗口

1 ATOM RegisterClass(
2   const WNDCLASSA *lpWndClass
3 );

注册代码如下

if(!RegisterClass(&wc))
{
    return 0;
}

三、创建窗口

 使用CreateWindow函数创建窗口,如果函数调用成功,返回值为新窗口的句柄;如果调用失败,返回值为NULL。可以使用GetLastError()函数获取错误信息

 1 HWND CreateWindow( 
 2   LPCTSTR lpClassName, 
 3   LPCTSTR lpWindowName, 
 4   DWORD dwStyle, 
 5   int x, 
 6   int y, 
 7   int nWidth, 
 8   int nHeight, 
 9   HWND hWndParent, 
10   HMENU hMenu, 
11   HANDLE hInstance, 
12   PVOID lpParam 
13 ); 

lpClassName

指定窗口类的名称,这个名称就是WNDCLASSA中的lpszClassName。如果在调用CreateWindow函数之前,没有调用RegisterClass函数注册这个类,系统无法得知窗口的相关信息,窗口创建就会失败。

lpWindowName

指定窗口名称,如果指定了标题栏,那么这里指向的字符串就会显示在标题栏上。

dwStyle

指定创建窗口的样式,可以组合不同的窗口样式

常量 说明
WS_CAPTION(0x00C00000L) 创建一个有标题栏的窗口
WS_SYSMENU(0x00080000L) 创建一个在标题栏上带有系统菜单的窗口(需要和WS_CAPTION一起使用)
WS_MINIMIZEBOX(0x00020000L) 创建一个具有最小化按钮的窗口(需要和WS_SYSMENU一起使用)
WS_MAXIMIZEBOX(0x00010000L) 创建一个具有最大化按钮的窗口(需要和WS_SYSMENU一起使用)
WS_TILED(0x00000000L) 创建一个层叠的窗口,层叠的窗口有一个标题栏和一个边框
WS_TILEDWINDOW 创建一个使用(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)样式的层叠的窗口
WS_CHILD(0x40000000L) 创建窗口为子窗口,不能应用于弹出式窗口样式
WS_OVERLAPPED 与WS_TILED样式相同
WS_OVERLAPPEDWINDOW 创建一个使用(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)样式的层叠的窗口
WS_EX_TOPMOST 创建一个始终置顶的窗口(不管窗口是否已经激活)  
WS_POPUP(0x80000000L) 创建一个弹出式窗口(不能与WS_CHILD一起使用)
WS_VISIBLE(0x10000000L) 创建一个初始状态为可见的窗口(可以使用ShowWindow函数来控制显示或隐藏窗口)
完整窗口样式可以访问:https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles

x

指定窗口左上角的x坐标

y

指定窗口左上角的y坐标

nWidth

以设备单元指定窗口的宽度 

nHeight

以设备单元指定窗口的高度

hWndParent

指定被创建窗口的父窗口的句柄。如果要创建一个子窗口,这里就需要提供父窗口的句柄。

hMenu

菜单句柄,指向附属于该窗口的菜单

hInstance

WinMain函数中传入的应用程序实例的句柄

lpParam

作为WM_CREATE消息的附加参数lParam传入的数据指针。在创建多文档界面的客户窗口时,lpParam必须指向CLIENTCREATESTRUCT结构体。多数窗口将这个参数设置为NULL

CreateWindow示例代码如下

HWND hwnd; 
 
    hwnd = CreateWindow( 
        "MainWClass",        
        "Test Window",           
        WS_OVERLAPPEDWINDOW, 
        0,      
        0,    
        CW_USEDEFAULT,       // 默认宽度
        CW_USEDEFAULT,       // 默认高度
        NULL,         // 没有父窗体
        NULL,        // 没有菜单
        hinstance,           
        NULL);      //没有附加数据

四、显示窗口

执行CreateWindow函数,窗体创建成功之后,需要调用ShowWindow函数把窗口显示在桌面上

BOOL ShowWindow(HWND hWnd,int nCmdShow);

hWnd

CreateWindow创建窗口成功后返回的窗口句柄

nCmdShow

指示窗口显示的状态

常用的窗口显示状态如下

SW_HIDE: 隐藏窗口并激活其它窗口

SW_SHOW: 在窗口原来的位置以原来的尺寸激活并显示窗口

SW_SHOWMAXIMIZED: 激活并以最大化显示窗口

SW_SHOWMINIMIZED: 激活并最小化显示窗口

SW_SHOWNORMAL 激活并显示窗口。如果窗口是最大化或最小化的状态,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口时,应该使用这种状态

调用ShowWindow()函数之后,需要调用UpdateWindow()函数来更新窗口。

1 BOOL UpdateWindow(HWND hwnd);

hwnd:调用CreateWindow()成功创建窗口后返回的窗口句柄

UpdateWindow()函数通过发送一个WM_PAINT消息来刷新窗口。如果窗口更新的区域不为空,UpdateWindow()函数绕过应用程序的消息队列,直接发送WM_PAINT消息给指定窗口的窗口过程函数进行处理。如果更新区域为空,则不发送消息。

至此,我们就完成了Window类的设计和注册,窗口的创建、显示及更新,接下来开始处理窗体的消息。

五、创建消息循环

在窗口创建成功之后,需要编写一个消息循环来不断地从消息队列中取出消息,并进行响应。

调用GetMessage()函数从消息队列中取出消息

1 BOOL GetMessage(
2   LPMSG lpMsg,
3   HWND  hWnd,
4   UINT  wMsgFilterMin,
5   UINT  wMsgFilterMax
6 );

lpMsg:指向一个消息结构体(MSG),GetMessage从线程的消息队列中取出的消息将保存在该结构体对象中。

hWnd:指向被接收消息的窗口句柄(指定接收属于哪一个窗口的消息),设置为NULL时,函数接收属于调用线程的所有窗口的窗口消息。

wMsgFilterMin:指定要获取的消息的最小值,通常设置为0

wMsgFilterMax:指定要获取的消息的最大值,如果wMsgFilterMin和wMsgFilterMax都设置为0,则接收所有消息。

关于消息的介绍,可以参考:

https://www.cnblogs.com/zhaotianff/p/11285312.html

取出消息后,需要对消息进行转换。这个时候就需要调用TranslateMessage()函数,该函数将虚拟消息转换为字符消息。字符消息被送到调用线程的消息队列里,当下一次线程调用函数GetMessage()或PeekMessage()时被读出。

1 BOOL TranslateMessage(
2   const MSG *lpMsg
3 );

lpMsg:指向MSG结构的指针,该结构用于存放调用函数GetMessage()或PeekMessage()从消息队列里取出的消息

返回值:如果消息可以得到,返回非零值;如果没有消息,返回值是0

未完

life runs on code

作者: zhaotianff

转载请注明出处

原文地址:https://www.cnblogs.com/zhaotianff/p/11297319.html