【Fr2D开发笔记】二、创建Window

在完成上次的Fr2D的控制台版本后,是时候把它变成一个真正的图形库了。控制台绘制显然是不理想的,因此,我选择了Direct2D。

一、WinAPI

调用Direct2D的前提是创建一个渲染对象窗体。所以,我们需要先使用WindowsAPI创建一个。

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
#include<windows.h>
#include<tchar.h>

HWND ghMainWnd = 0;

bool InitWindowsApp(HINSTANCE instanceHandle, int show);
int Run();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd) {
if (!InitWindowsApp(hInstance, nShowCmd))
return 0;
return Run();
}

bool InitWindowsApp(HINSTANCE instanceHandle, int show) {
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instanceHandle;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = _T("BasicWndClass");

if (!RegisterClass(&wc)) {
MessageBox(0, _T("RegisterClass FAILED"), 0, 0);
return false;
}

ghMainWnd = CreateWindow(
_T("BasicWndClass"),
_T("Win32Basic"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
instanceHandle,
0
);

if (ghMainWnd == 0) {
MessageBox(0, _T("CreateWindow FAILED"), 0, 0);
return false;
}

ShowWindow(ghMainWnd, show);
UpdateWindow(ghMainWnd);

return true;
}

int Run() {
MSG msg = { 0 };
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
//...
}
}
return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_LBUTTONDOWN:
MessageBox(0, _T("Hello, World"), _T("Hello"), MB_OK);
return 0;

case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(ghMainWnd);
return 0;

case WM_KEYUP:
return 0;

case WM_DESTROY:
PostQuitMessage(0);
return 0;
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}

这就是使用WINAPI创建一个窗体的最简代码了,包括注册这个窗体,获取的窗体句柄,消息循环和窗口过程回调函数。其实在vs中创建windows应用的话,vs会直接生成这些代码,但要便于使用于是将其封装。

二、FrWnd类

这里将创建一个窗体个过程粗暴地封装成了FrWnd类,将窗口过程和Display设计成回调函数,Display函数负责写窗体的绘制语句,在注册新窗体时传入即可。

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
#include<windows.h>
#include<tchar.h>
#include<functional>

#define WINPARAMETERS HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd
#define INITPARAMETERS hInstance, nShowCmd

class FrWnd {
public:
FrWnd();
FrWnd(int _height, int _width, WNDPROC proc, std::function<bool()> callback, LPCSTR _name);

HWND GetHandle();
bool Create(HINSTANCE instanceHandle, int show);
int Run();

private:
HWND hwnd;
LPCSTR name;
int height;
int width;
WNDPROC proc;
std::function<bool()> Display;
};

FrWnd::FrWnd() {
height = 800;
width = 600;
name = _T("Test");
}

FrWnd::FrWnd(int _height, int _width, WNDPROC _proc, std::function<bool()> callback, LPCSTR _name = _T("Test")) {
height = _height;
width = _width;
Display = callback;
name = _name;
proc = _proc;
}


bool FrWnd::Create(HINSTANCE instanceHandle, int show) {
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instanceHandle;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = _T("BasicWndClass");

if (!RegisterClass(&wc)) {
MessageBox(0, _T("RegisterClass FAILED"), 0, 0);
return false;
}

hwnd = CreateWindow(
_T("BasicWndClass"),
name,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
height,
width,
0,
0,
instanceHandle,
0
);

if (hwnd == 0) {
MessageBox(0, _T("CreateWindow FAILED"), 0, 0);
return false;
}

ShowWindow(hwnd, show);
UpdateWindow(hwnd);

return true;
}

int FrWnd::Run() {
MSG msg = { 0 };
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
Display(); //此处为回调函数
}
}
return (int)msg.wParam;
}

HWND FrWnd::GetHandle() {
return hwnd;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include"frwnd.h"

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

bool Display() {}
FrWnd myWnd(480, 640, WndProc, Display, "test");

int WINAPI WinMain(WINPARAMETERS) {
if (!myWnd.Create(INITPARAMETERS))
return 0;
myWnd.Run();
return 0;
}

此时,就创建了一个窗体。