Win32编程之创建第一个windows窗口(一)

一、窗口创建过程

  • 定义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
#include <Windows.h>
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
 
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}
 
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    //设计窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WindProc;
    wc.lpszClassName = TEXT("Main");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
     
    //注册窗口类
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统
     
    //在内存中创建窗口
    HWND hWnd = CreateWindow(wc.lpszClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
 
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
 
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0)) {
        TranslateMessage(&nMsg);//将按键消息转换为字符消息
        DispatchMessage(&nMsg);//将消息分发给窗口处理函数
    }
 
    return 0;
}

二、字符编码

1
2
3
4
5
6
7
8
9
10
11
DBCS字符编码:
A  我   是   程   序   员
01 0203 0405 0607 0809 0A0B
但是解析时,可能为:
01 0203 0405 0607 0809 0A0B
0102 0304 0506 0708 090A 0B
 
UNICODE编码:
A     我     是    程     序    员
0001  0203   0405  0607   0809  0A0B
不存在解析的问题

1.宽字节

  • wchar_t:每个字符占2个字节,wchar_t实际上是unsigned shot类型,定义时,需要增加"L",通知编译器按照双字节编译字符串,采用UNICODE编码 
  • 需要使用支持wchar_t函数操作宽字节字符串,例如:wchar_t* pwszText = L"Hello wchar";wprint(L"%s\n", pwszText )
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
#include <Windows.h>
#include <stdio.h>
 
void C_Char() {
    const char* pszText = "hello char";
    printf("%s\n", pszText);
}
 
void W_Char() {
    const wchar_t* pszText = L"hello wchar";
    int len = wcslen(pszText);
    wprintf(L"%s %d\n", pszText, len);
}
 
void T_Char() {
    const TCHAR* pszText = TEXT("hello txt");
 
#ifdef UNICODE
    wprintf(L"%s\n", pszText);
#else
    printf("单:%s\n", pszText);
#endif
}
 
int main() {
    W_Char();
    C_Char();
    T_Char();
 
    system("pause");
 
    return 0;
}

2.UNICODE字符的打印

wprintf对UNICODE字符打印支持不完善,可以在windows下使用WriteConsole API打印UNICODE字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <Windows.h>
#include <stdio.h>
 
void PrintUnicode() {
    const wchar_t* pszText = L"早上好";
    //wprintf(L"%s\n", pszText);
    //获取标准输出句柄
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteConsole(hOut, pszText, wcslen(pszText), NULL, NULL);
}
 
int main() {
    PrintUnicode();
 
    system("pause");
 
    return 0;
}

三、窗口类的概念

  • 窗口类包含了窗口的各种参数信息的数据结构
  • 每个窗口都具有窗口类,基于窗口类创建窗口
  • 每个窗口类都具有一个名称,使用前必须注册到系统

窗口类的分类:

  • 系统已经定义好的窗口类,所有应用程序都可以直接使用
  • 应用程序全局窗口类,当前应用程序所有模块都可以使用
  • 应用程序局部窗口类,当前应用程序中本模块可以使用

1.系统窗口类

不要注册,直接使用窗口类即可,系统已经注册好了

例如:按钮-BUTTON、编辑框-EDIT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <Windows.h>
 
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    //在内存中创建窗口
    HWND hWnd = CreateWindow(TEXT("Button"), TEXT("Window"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
 
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
 
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0)) {
        TranslateMessage(&nMsg);//将按键消息转换为字符消息
        DispatchMessage(&nMsg);//将消息分发给窗口处理函数
    }
 
    return 0;
}

2.全局及局部窗口类 

注册窗口类的函数:

1
ATOM RegisterClass(CONST WNDCLASS *lpWndClass); 

lpWndClass:窗口类的数据,注册成功后,返回一个数字标识

注册窗口类的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct tagWNDCLASSA {
    UINT        style;//窗口类的风格
    WNDPROC     lpfnWndProc;//窗口处理函数
    int         cbClsExtra;//窗口类的附件数据buff的大小
    int         cbWndExtra;//窗口的附件数据buff的大小
    HINSTANCE   hInstance;//当前模块的实例句柄
    HICON       hIcon;//窗口图标句柄
    HCURSOR     hCursor;//鼠标的句柄
    HBRUSH      hbrBackground;//绘制窗口背景的画刷句柄
    LPCSTR      lpszMenuName;//窗口菜单的资源ID字符串
    LPCSTR      lpszClassName;//窗口类的名称
} WNDCLASSA

style窗口类风格:

应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALCLASS

例如:

  WNDCLASS wce = {0};

  wce.style = ...|CS_GLOBALCLASS;

应用程序局部窗口类:在注册窗口类时,不添加CS_GLOBALCLASS风格

  • CS_HREDRAW:当窗口水平变化时,窗口重新绘制
  • CS_VREDRAW:当窗口垂直变化时,窗口重新绘制
  • CS_DBLCLKS:允许窗口接收鼠标双击
  • CS_NOCLOSE:窗口没有关闭按钮

四、创建窗口 

1
2
3
4
5
6
7
8
9
10
11
12
13
WINUSERAPI HWND WINAPI CreateWindowExA(
    _In_ DWORD dwExStyle,//窗口的扩展风格
    _In_opt_ LPCSTR lpClassName,//已经注册的窗口类名称
    _In_opt_ LPCSTR 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);//窗口创建附件参数

窗口的创建可以使用CreateWindow或CreateWindowExA,若是窗口创建成功则返回窗口句柄

CreateWindow函数执行流程:

  • 系统根据传入的窗口名称,在应用程序局部窗口类中查找,如果找到执行第二步,如果未找到则执行第三步
  • 比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回,如果不相等,则继续执行第三步
  • 在应用程序全局窗口类,如果找到,执行第四部,如果未找到则执行第5步
  • 使用找到的窗口类的信息,创建窗口返回
  • 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败

函数内部大致执行流程:

1
2
3
4
5
6
7
if (找到窗口类) {
     申请一大块内存,将窗口的数据信息存入这块内存
       
     return 这块内存的句柄
} else {
     return NULL
}

五、子窗口的创建 

  • 创建时要设置父窗口句柄
  • 创建风格要增减WS_CHILD|WS_VISIBLE
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
#include <Windows.h>
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        break;
    }      
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}
 
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    //设计窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WindProc;
    wc.lpszClassName = TEXT("Main");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
 
    //注册窗口类
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统
 
    //在内存中创建窗口
    HWND hWnd = CreateWindow(wc.lpszClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
 
    //设计子窗口类
    WNDCLASS wcChild = { 0 };
    wcChild.cbClsExtra = 0;
    wcChild.cbWndExtra = 0;
    wcChild.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcChild.hCursor = NULL;
    wcChild.hIcon = NULL;
    wcChild.hInstance = hIns;
    wcChild.lpfnWndProc = DefWindowProc;
    wcChild.lpszClassName = TEXT("Child");
    wcChild.lpszMenuName = NULL;
    wcChild.style = CS_HREDRAW | CS_VREDRAW;
 
    //注册子窗口类
    RegisterClass(&wcChild);
 
    //创建子窗口
    HWND hChildWnd = CreateWindowEx(0, wcChild.lpszClassName, TEXT("SubWindow"), WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
    HWND hChildWnd1 = CreateWindowEx(0, wcChild.lpszClassName, TEXT("SubWindow1"), WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);
     
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
 
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0)) {
        TranslateMessage(&nMsg);//将按键消息转换为字符消息
        DispatchMessage(&nMsg);//将消息分发给窗口处理函数
    }
 
    return 0;
}

  

 
posted @   TechNomad  阅读(212)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示