[toc]

前因

在windows下学习c/c++程序时,总是黑窗口,不禁有了疑惑->窗口程序是怎么开发出来的?后来了解到可以调用win32api开发出windows系统的窗口程序。此篇文章记录一下第一个windows窗口程序的开发。

过程

一 第一个控制台程序

在visualstudio中新建一个c语言程序项目,功能为在控制台界面中显示hello world.

使用visualStudio新建空项目,并添加一个源文件main.cpp,并写出c语言的helloworld代码。

main.cpp

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
printf("hello world");
return 0;
}

编译运行会在控制台里输出hello world,如图所示

二 第一个win32api的使用

修改源main.cpp文件代码,在主函数里调用win32api中的弹窗函数

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <windows.h>

int main()
{
printf("hello world");

MessageBox(NULL,TEXT("hello C"),TEXT("标题"),MB_OK);

system("pause");
return 0;
}

编译运行会弹出一个消息框,如图所示

三 第一个windows窗口程序

1.修改源main.cpp文件代码,调用win32api中的窗口函数

  • 控制台程序的入口点为main()函数,windows窗口程序的默认入口点为Winmain
  • 需要在项目中把改变设置Properties->Linker->System->SubSystem->Windows
  • 第一个窗口程序主要分为一个入口函数WinMain与一个回调函数
  • 创建第一个windows窗口程序用到的主要函数
    1. 注册 RegisterClassEx
    2. 创建窗口 CreateWindowEx
    3. 显示窗口 ShowWindow
    4. 进入消息循环 GetMessage

WinMain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
int  WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Sample Window Class";
/*
1.注册 RegisterClassEx
2.创建窗口 CreateWindowEx
3.显示窗口 ShowWindow
4.进入消息循环 GetMessage
*/
RegisterClassEx(&wc);

HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
100,
100,
800,
600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

ShowWindow(hwnd, SW_NORMAL);

MSG msg = { };
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}


}

WindowProc

1
2
3
4
5
6
7
8
9
10
11
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
}

第一个窗口程序完整代码main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <Windows.h>


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

int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Sample Window Class";
/*
1.注册 RegisterClassEx
2.创建窗口 CreateWindowEx
3.显示窗口 ShowWindow
4.进入消息循环 GetMessage
*/
RegisterClassEx(&wc);

HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
100,
100,
800,
600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

ShowWindow(hwnd, SW_NORMAL);

MSG msg = { };
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}


}

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

switch (uMsg)
{

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
}


编译运行会显示一个窗口

2.完善第一个windows窗口程序,给窗口添加控件

添加一下代码,给窗口程序添加一个按钮

1
2
3
4
5
6
7
8
9
10
11
12
HWND hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
m_hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <Windows.h>


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

int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{

WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Sample Window Class";
/*
1.注册 RegisterClassEx
2.创建窗口 CreateWindowEx
3.显示窗口 ShowWindow
4.进入消息循环 GetMessage
*/
RegisterClassEx(&wc);

HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
100,
100,
800,
600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

HWND hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.

ShowWindow(hwnd, SW_NORMAL);

MSG msg = { };
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}


}

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

switch (uMsg)
{

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
}

3.继续完善第一个windows窗口程序,使用消息处理

  1. 在窗口消息函数中添加以下代码,当鼠标左键点击按钮时弹出一个消息框
1
2
3
case WM_COMMAND:
MessageBox(NULL, TEXT("what's wrong?"), L"title", MB_ICONINFORMATION);
return 0;

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <Windows.h>


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

int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{

WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Sample Window Class";
/*
1.注册 RegisterClassEx
2.创建窗口 CreateWindowEx
3.显示窗口 ShowWindow
4.进入消息循环 GetMessage
*/
RegisterClassEx(&wc);

HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
100,
100,
800,
600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

HWND hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.

ShowWindow(hwnd, SW_NORMAL);

MSG msg = { };
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

}

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

switch (uMsg)
{
case WM_COMMAND:
MessageBox(hwnd, TEXT("what's wrong?"), L"message", MB_ICONINFORMATION);
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
}

4.完善消息处理

完整代码示例main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <Windows.h>


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

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{

WNDCLASSEX wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszMenuName = MAKEINTRESOURCE(101);
wc.lpszClassName = L"MainWindowClass";
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(102));

/*
1.注册 RegisterClassEx
2.创建窗口 CreateWindowEx
3.显示窗口 ShowWindow
4.进入消息循环 GetMessage
*/
// Register the window class for the main window.
if (!hPrevInstance)
{

if (!RegisterClassEx(&wc))
return FALSE;
}
// Create the main window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
wc.lpszClassName, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
100,
100,
800,
600,
NULL, // Parent window
(HMENU)NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

// If the main window cannot be created, terminate the application.
if (!hwnd)
return FALSE;

// Show the window and paint its contents.
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);

BOOL bRet;
MSG msg = { };
// Start the message loop.
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Return the exit code to the system.
return msg.wParam;

}

void OnSize(HWND hwnd, UINT flag, int width, int height)
{
// Handle resizing
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
case WM_CREATE:

// Initialize the window.
CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
hwnd, // Parent window
(HMENU)11, // menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
110, // x position
110, // y position
100, // Button width
100, // Button height
hwnd, // Parent window
(HMENU)12, // menu.
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
return 0;

case WM_PAINT:
// Paint the window's client area.
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
TextOut(hdc, 150, 150, L"This is a winow", 28);
EndPaint(hwnd, &ps);
return 0;
case WM_LBUTTONDOWN:
{
MessageBeep(0);
}
case WM_SIZE:
// Set the size and position of the window.
{
int width = LOWORD(lParam); // Macro to get the low-order word.
int height = HIWORD(lParam); // Macro to get the high-order word.

// Respond to the message:
OnSize(hwnd, (UINT)wParam, width, height);
}
return 0;
case WM_DESTROY:
// Clean up window-specific data objects.
DestroyWindow(hwnd);
PostQuitMessage(0);
return 0;

case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case 11:
//DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
MessageBox(hwnd, TEXT("11"), L"message", MB_ICONINFORMATION);
break;
case 12:
MessageBox(hwnd, TEXT("12"), L"message", MB_ICONINFORMATION);
break;
case 40001:
MessageBox(hwnd, TEXT("about"), L"message", MB_ICONINFORMATION);
}
}
break;


default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
}

win32api封装后创建窗口程序

补充说明

创建窗口的思路演变

1.API创建窗口及消息处理

  • 写一个窗口入口函数,入口函数创建窗口。

  • 写一个消息处理函数,处理窗口程序的每个功能。

2.使用对话框资源创建窗口

  • 在工程中添加资源,控件可以拖动。
  • 将控件的头文件包含进源文件。

资源文件里画的控件图标,在源文件里写控件消息,在入口函数里调用控件api函数将窗口过程与消息绑定。

3.使用MFC创建对话框窗口

调用win32api开发windows窗口程序的项目称为windows桌面程序,在VisualStudio2019中可以创建windows桌面程序的工程,创建方法为选择Windows Desktop Wizard->desktop或windows desktop application

相关链接:

模块 1. 你的第一个 Windows 程序

演练:创建传统的 Windows 桌面应用程序 (C++)

前章: 用api 创建一个windows 窗口【C++ Windows API】