• 网络编程 初探windows编程


    目录

    一、什么是Winodws编程

    二、开发环境搭建以及如何学习

    三、VA助手安装

    四、第一个Win32程序

    五、窗口类句柄/窗口类对象

    六、Winodws消息循环机制

    七、Windows数据类型


    一、什么是Winodws编程

    Windows 编程指的是在 Microsoft Windows 操作系统上进行软件开发的过程,通常涉及使用 Windows API、.NET Framework、WPF(Windows Presentation Foundation)、WinForms 等技术。下面我将简要介绍一些相关的内容。

    1. Windows API: Windows 应用程序接口(Windows API)是一组定义了 Windows 操作系统核心功能的函数和数据结构的集合。通过调用这些函数,开发者可以实现诸如窗口管理、文件操作、内存管理等底层操作。使用 Windows API 进行编程可以获得最高的灵活性和控制力,但也需要处理更多的细节。

    2. .NET Framework: .NET Framework 是一个由微软开发的应用程序框架,它提供了大量的库和运行时环境,使开发者能够使用多种编程语言(如C#、VB.NET)来开发 Windows 应用程序。.NET Framework 包括了各种功能强大的类库,以及对于 Windows 应用程序开发非常有用的 Windows Forms 和 WPF 框架。

    3. WPF(Windows Presentation Foundation): WPF 是用于创建 Windows 客户端应用程序的 UI 框架,它提供了一种基于 XML 的标记语言 XAML(Extensible Application Markup Language)来定义用户界面,同时支持丰富的数据绑定、样式模板、动画等功能。相较于传统的 Windows Forms,WPF 提供了更加灵活和强大的界面设计方式,可以实现更富有表现力的用户界面。

    4. WinForms: Windows Forms 是 .NET Framework 中的一部分,提供了一种快速开发 Windows 应用程序的方式。开发者可以使用 Visual Studio 中的可视化设计器来创建界面,并通过添加事件处理程序等方式来实现应用程序逻辑。虽然 WinForms 在界面设计方面不如 WPF 灵活,但它仍然是许多传统的 Windows 应用程序的首选开发方式

    .h 文件和 .cpp 文件通常用于 C++ 项目的代码组织。它们有不同的作用:

    1. .h 文件(头文件):

      • 通常包含类的声明、函数原型、常量定义以及其他需要在多个源文件中共享的信息。
      • 头文件中通常不包含函数或类的实际实现,而是只包含其声明,以便其他源文件可以通过包含头文件来访问这些声明。
      • 使用头文件可以帮助提高代码的模块化和可维护性,因为它们允许将代码分割成更小的单元并进行组织。
      • 头文件通常使用 ".h" 扩展名,但也可能使用 ".hpp" 或其他类似的形式。
    2. .cpp 文件(源文件):

      • 包含了类成员函数和全局函数的实际实现,以及全局变量的定义等。
      • 在 .cpp 文件中,你会找到与头文件中声明的内容对应的实际代码。
      • .cpp 文件通常被编译器编译成目标代码,最终链接成可执行程序或库。

    一般来说,C++ 项目中的代码都会以这种方式进行组织,头文件用于声明和共享信息,而 .cpp 文件用于实现声明并包含实际的代码逻辑。这种分离有助于提高代码的可读性、可维护性和重用性。

    API:Application Programming Interface 应用程序编程接口。

    SDK:Software Development Kit 软件开发工具包,一般会包括 API 接口文档,示例文档,帮助文档,使用手册,相关工具等。

    二、开发环境搭建以及如何学习

    Visual Studio Installer中选择

    创建项目选择Windows桌面应用程序即可

    如何学习?多查官方参考文档

    Windows API官方文档:Windows API 索引 - Win32 apps | Microsoft Learn

    三、VA助手安装

    下载地址:https://www.jb51.net/softs/756854.html

    解压密码:www.jb51.net

    如果之前安装失败或者卸载过,参考下面文章解决

    https://blog.csdn.net/m0_52776283/article/details/128329994

    常用快捷键:只记住这几个就可以了,剩下的可以慢慢探索

    • ALT+G:跳到定义
    • ALT+SHIFT+F:查找所有引用
    • ALT+左箭头/右箭头:回退/前进

    四、第一个Win32程序

    1. #include <windows.h>
    2. #include <stdio.h>
    3. /*
    4. 如何创建一个窗口:
    5. 1. 设计窗口类
    6. 2. 注册窗口类
    7. 3. 创建窗口,操作系统会把它绘制出来
    8. 4. 消息循环
    9. 5. 回调函数
    10. */
    11. // LPCTSTR = CHAR* 指向多字节字符串的指针
    12. LPCTSTR clsName = "My";
    13. LPCTSTR msgName = "欢迎学习";
    14. // LRESULT = long
    15. LRESULT CALLBACK MyWinProc(
    16. HWND hwnd, // handle to window
    17. UINT uMsg, // message identifier
    18. WPARAM wParam, // first message parameter word
    19. LPARAM lParam // second message parameter long
    20. )
    21. {
    22. //uMsg 消息类型
    23. int ret;
    24. /*
    25. 声明 HDC hdc; 的目的是创建一个变量 hdc,用于存储设备上下文环境的句柄。
    26. 通过使用设备上下文句柄,可以将绘图操作指定给特定的设备,如屏幕、打印机或图像文件等。
    27. 在实际应用中,通常需要使用特定的函数来获取设备上下文句柄,例如 GetDC 函数用于获取屏幕设备上下文句柄。
    28. 获取到设备上下文句柄后,可以使用它来进行相关的绘图操作,例如绘制线条、填充颜色、显示文本等。
    29. 完成绘图操作后,可能需要使用相应的函数释放设备上下文句柄,以避免资源泄露。
    30. */
    31. HDC hdc;
    32. switch (uMsg)
    33. {
    34. case WM_CHAR:
    35. char szChar[20];
    36. sprintf_s(szChar, "您刚才按下了: %c", wParam);
    37. MessageBox(hwnd, szChar, "char", NULL);
    38. break;
    39. case WM_LBUTTONDOWN:
    40. MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
    41. break;
    42. case WM_PAINT:
    43. // 声明了一个名为 ps 的 PAINTSTRUCT 结构体变量。PAINTSTRUCT 结构体用于存储绘图信息,包括绘图区域的坐标等。
    44. PAINTSTRUCT ps;
    45. // BeginPaint 函数用于开始绘图操作,并返回一个设备上下文句柄 hdc。
    46. // 它接受窗口句柄 hwnd 和指向 PAINTSTRUCT 结构体的指针作为参数。这个函数的调用表示程序即将开始在指定窗口进行绘图操作。
    47. hdc = BeginPaint(hwnd, &ps);
    48. TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
    49. EndPaint(hwnd, &ps);
    50. // 用于结束绘图操作,并清理相关资源。
    51. MessageBox(hwnd, "重绘", "msg", NULL);
    52. break;
    53. case WM_CLOSE:
    54. ret = MessageBox(hwnd, "是否真的结束?", "msg", MB_YESNO);
    55. if (ret == IDYES)
    56. {
    57. /*
    58. DestroyWindow(hwnd) 是一个 Windows API 函数,用于销毁指定窗口。
    59. 在这里,hwnd 是一个窗口句柄,代表要销毁的窗口。通过调用 DestroyWindow(hwnd) 函数,程序可以关闭并销毁与该窗口相关联的资源。
    60. 当调用 DestroyWindow 函数时,系统会发送 WM_DESTROY 消息给窗口过程函数,以通知窗口即将被销毁。窗口过程函数可以处理这个消息,并执行一些清理操作。之后,系统会释放与窗口相关的资源,包括设备上下文句柄、菜单、图标等。
    61. 值得注意的是,使用 DestroyWindow 函数只能销毁由当前进程创建的窗口。如果要销毁其他进程创建的窗口,可以使用 PostMessage 函数发送 WM_CLOSE 消息给目标窗口。
    62. */
    63. DestroyWindow(hwnd);
    64. }
    65. break;
    66. case WM_DESTROY:
    67. /*
    68. PostQuitMessage(0) 是一个 Windows API 函数,用于向消息队列中发送一个退出消息,通知应用程序要求其退出消息循环并终止。
    69. 在这里,参数 0 表示指定的退出代码。当应用程序接收到这个退出消息后,它将会开始退出过程,并使用给定的退出代码作为程序的返回值。
    70. */
    71. PostQuitMessage(0);
    72. break;
    73. default:
    74. // 默认过程函数处理其他消息
    75. return DefWindowProc(hwnd, uMsg, wParam, lParam);
    76. }
    77. return 0;
    78. }
    79. // WINAPI = __stdcall
    80. int WINAPI WinMain(
    81. _In_ HINSTANCE hInstance, // 标识当前程序实例的句柄
    82. _In_opt_ HINSTANCE hPrevInstance,
    83. _In_ LPSTR lpCmdLine,
    84. _In_ int nShowCmd
    85. ) {
    86. // a 设计一个窗口类
    87. //定义和配置窗口对象
    88. WNDCLASS wndcls; // 用来存储窗口类信息的结构体实例。
    89. wndcls.cbClsExtra = NULL; // 表示窗口类的额外类内存,这里设为NULL表示不使用额外类内存。
    90. wndcls.cbWndExtra = NULL; // 表示每个窗口实例的额外窗口内存,这里同样设为NULL表示不使用额外窗口内存
    91. wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 指定了窗口背景的画刷句柄,这里使用 GetStockObject 函数获取了白色画刷。
    92. wndcls.hCursor = LoadCursor(NULL, IDC_ARROW); // 指定了光标的句柄,这里使用 LoadCursor 函数加载了箭头光标。
    93. wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 指定了窗口的图标句柄,这里使用 LoadIcon 函数加载了应用程序图标。
    94. wndcls.hInstance = hInstance; // 指定了窗口类所属的实例句柄。程序的句柄
    95. wndcls.lpfnWndProc = MyWinProc; // 指定了窗口过程的地址,即窗口的消息处理函数。
    96. wndcls.lpszClassName = clsName; // 指定了窗口类的类名。
    97. wndcls.lpszMenuName = NULL; // 指定了窗口的菜单名,这里设置为NULL表示没有菜单。
    98. wndcls.style = CS_HREDRAW | CS_VREDRAW; // 指定了窗口类的风格,CS_HREDRAW | CS_VREDRAW 表示当窗口大小水平或垂直方向发生变化时,窗口的内容需要重绘。
    99. // b 注册窗口类
    100. // 窗口类的信息被注册到系统中,使得之后可以使用这个窗口类来创建窗口实例,以便让系统知道如何处理特定类型的窗口。
    101. RegisterClass(&wndcls);
    102. // c 创建窗口
    103. // 通过调用 CreateWindow 函数,根据指定的窗口类、标题和风格等信息,系统会创建一个窗口实例,并返回该窗口的句柄。
    104. // 句柄可以用于后续对窗口进行操作和管理。
    105. /*
    106. clsName: 窗口类名,这里使用之前注册的窗口类 clsName。
    107. msgName: 窗口标题,用于在窗口的标题栏显示窗口的名称。
    108. WS_OVERLAPPEDWINDOW: 窗口风格,指定了窗口的外观和行为,这里使用了 WS_OVERLAPPEDWINDOW 风格,表示创建一个具有标题栏、边框和最大化/最小化按钮的窗口。
    109. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT: 窗口的初始位置和大小,这里使用 CW_USEDEFAULT 表示使用系统默认值,即由系统决定窗口的位置和大小。
    110. NULL: 父窗口句柄,这里设置为NULL表示新创建的窗口没有父窗口。
    111. hInstance: 窗口所属的实例句柄,用于标识窗口所属的应用程序。
    112. NULL: 与窗口相关联的菜单句柄,这里设置为NULL表示不使用菜单。
    113. */
    114. HWND hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
    115. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance,
    116. NULL);
    117. //d 显示和刷新窗口
    118. ShowWindow(hwnd, SW_SHOWNORMAL); // 用于显示先前创建的窗口实例,这里使用 SW_SHOWNORMAL 表示以原始大小和位置显示窗口。
    119. UpdateWindow(hwnd); // 用于更新先前创建的窗口实例的客户区域。这样做可以立即将窗口客户区域的内容显示出来。
    120. //e 消息循环 GetMessage 只有在接收到 WM_QUIT 才会返回 0
    121. //TranslateMessage 翻译消息 WM_KEYDOWN 和 WM_KEYUP 合并为 WM_CAHR
    122. // MSG是一个结构体
    123. MSG msg;
    124. /*
    125. 当调用 GetMessage 函数时,系统会检查当前线程的消息队列,如果队列中有消息,则将消息复制到 msg 结构体中,并返回非零值;
    126. 如果队列中没有消息,函数会将当前线程置于等待状态,直到有新消息到达为止。
    127. 通常情况下,这行代码会被放在一个循环中,不断地调用 GetMessage 函数以获取并处理消息,直到接收到退出程序的消息为止。
    128. 通过不断地获取消息,程序可以及时响应用户的输入、系统事件等各种消息,从而实现交互式的用户界面和响应式的应用程序逻辑。
    129. */
    130. while (GetMessage(&msg, NULL, NULL, NULL)) // 3NULL 表示没有过滤规则,即获得所有消息
    131. {
    132. /*
    133. 调用了 TranslateMessage 函数,用于将虚拟键消息转换为字符消息。
    134. 具体来说,该函数会检查接收到的消息是否属于虚拟键消息(如键盘按键事件),
    135. 如果是,则将其转换为相应的字符消息(如字符输入事件)。这样可以确保程序正确处理用户的键盘输入。
    136. */
    137. TranslateMessage(&msg);
    138. /*
    139. 调用了 DispatchMessage 函数,用于将消息分派给窗口过程(Window Procedure)来进行处理。
    140. 每个窗口都有一个窗口过程,它是对窗口消息进行处理的函数。
    141. DispatchMessage 函数会根据消息中的窗口句柄找到对应的窗口过程,并将消息传递给该窗口过程进行处理。
    142. */
    143. DispatchMessage(&msg);
    144. }
    145. return msg.wParam;
    146. }

    五、窗口类句柄/窗口类对象

    窗口就是屏幕上的一片区域,接收用户的输入,显示程序的输出。可以包含标题栏,菜单栏,工具栏,控件等。

    句柄[Handle]:(资源的编号,二级指针,门把手),窗口句柄,文件句柄,数据库连接句柄。

    C++窗口类对象与窗口并不是一回事,它们之间惟一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个 C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的 C++窗口类对象销毁与否,要看其生命周期是否结束。但 C++窗口类对象销毁时,与之相关的窗口也将销毁。

    WinMain函数的第一个参数,HINSTANCE hInstance 是一个表示程序实例的句柄(handle)。在Windows操作系统中,每个正在运行的程序都有一个唯一的实例句柄,用于标识该程序在内存中的位置和资源。hInstance 参数在程序启动时由操作系统传递给程序的主函数 WinMain(),以便程序可以使用该句柄来访问程序实例的各种资源,如窗口、图标、菜单等。

    通过使用 hInstance 句柄,程序可以获取程序的路径、图标资源、对话框模板等信息,或者创建和管理窗口、菜单、图标等对象。另外,在多个实例同时运行的情况下,hInstance 句柄还可用于区分不同的程序实例。需要注意的是,hInstance 是一个抽象的句柄值,它不直接表示实际的内存地址。在操作系统中,句柄通常被映射到实际的内存地址或资源描述符上,程序可以通过句柄来间接引用这些内存地址或资源。

    六、Winodws消息循环机制

    事件可分为几种,由输入设备触发的,比如鼠标键盘等等。由窗体控件触发的,比如button控件,file菜单等。还有就是来自Windows内部的事件。这三种称为事件。

    消息,是由事件翻译而来的。事件产生消息。从数据结构角度来讲,消息是一种结构体。结构如下:

    1. typedef struct tagMSG
    2. {
    3. HWND hwnd; //窗口句柄。
    4. UINT message;//消息类型。
    5. WPARAM wParam;//32位附加信息。
    6. LPARAM lParam;//32位附加信息。
    7. DWORD time;//消息发送时间。
    8. POINT pt;//消息发送时,鼠标所在位置。
    9. }MSG;

    消息队列:消息队列有两种,分为系统消息队列和应用程序消息队列。产生的消息首先由Windows系统捕获,放在系统消息队列,再拷贝到对应的应用程序消息队列。32/64位系统为每一个应用程序维护一个消息队列。

    消息循环:系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。每有一个消息,就用GetMessage()取出消息。

    1. while(GetMessage (&msg, NULL, 0, 0))//Windows消息循环。
    2. {
    3. TranslateMessage (&msg) ;//翻译消息,如按键消息,翻译为WM_CHAR
    4. DispatchMessage (&msg) ;//分发消息到对应窗口
    5. }

    GetMessage具有阻塞机制。当消息队列中没有消息时,程序非忙等,而是让权等待。当收到WM_QUIT时,GetMessage返回false,循环停止,同时应用程序终止。

    消息处理:DispatchMessage()把取出来的消息分配给相应的窗口或线程,由窗口过程处理函数DefWindowProc()处理。

    Windows的应用程序靠消息驱动来实现功能。而消息驱动靠消息机制来处理。消息机制就是由消息队列,消息循环,消息处理构成的。

    那么,消息机制是如何运作的呢?

    当用户运行一个应用程序,通过对鼠标的点击或键盘按键,产生一些特定事件。由于Windows一直监控着I/O设备,该事件首先会被翻译成消息,由系统捕获,存放于系统消息队列。经分析,Windows知道该消息应由那个应用程序处理,则拷贝到相应的应用程序消息队列。由于消息循环不断检索自身的消息队列,当发现应用程序消息队列里有消息,就用GetMessage()取出消息,封装成Msg()结构。如果该消息是由键盘按键产生的,用TranslateMessage()翻译为WM_CHAR消息,否则,用DisPatchMessage()将取出的消息分发到相应的应用程序窗口,交由窗口处理程序处理。Windows为每个窗体预留了过程窗口函数,该函数是一个回掉函数,由系统调用,应用程序不能调用。程序员可以通过重载该函数处理我们”感兴趣”的消息。对于不感兴趣的消息,则由系统默认的窗口过程处理程序做出处理。

    上面代码的执行过程为:

    1. 消息循环调用GetMessage()从消息队列中查找消息进行处理,如果消息队列为空,程序将停止执行并等待(程序阻塞)。
    2. 事件发生时导致一个消息加入到消息队列(例如系统注册了一个鼠标点击事件),GetMessage()将返回一个正值,这表明有消息需要被处理,并且消息已经填充到传入的MSG参数中;当传入WM_QUIT消息时返回0;如果返回值为负表明发生了错误。
    3. 取出消息(在Msg变量中)并将其传递给TranslateMessage()函数,这个函数做一些额外的处理:将虚拟键值信息转换为字符信息。这一步实际上是可选的,但有些地方需要用到这一步。
    4. 上面的步骤执行完后,将消息传递给DispatchMessage()函数。DispatchMessage()函数将消息分发到消息的目标窗口,并且查找目标窗口过程函数,给窗口过程函数传递窗口句柄、消息、wParam、lParam等参数然后调用该函数。
    5. 窗口过程函数中,检查消息和其他参数,你可以用它来实现你想要的操作。如果不想处理某些特殊的消息,你应该总是调用DefWindowProc()函数,系统将按按默认的方式处理这些消息(通常认为是不做任何操作)
    6. 一旦一个消息处理完成,窗口过程函数返回,DispatchMessage()函数返回,继续循环处理下一个消息。

    七、Windows数据类型

    Unicode 是世界通用的字符编码标准,使用 16 位数据表示一个字符,一共可以表示 65535 种字符。 ASNI 字符集,使用 8 位数据或将相邻的两个 8 位的数据组合在一起表示特殊的语言字符。如果一个字节是负数,则将其后续的一个字节组合在一起表示一个字符。这种编码方式的字符集也称作“多字节”字符集。

    1. DWORD 32 字节无符号整型数据
    2. DWORD32 32 字节无符号整型数据
    3. DWORD64 64 字节无符号整型数据
    4. HANDLE 对象的句柄,最基本的句柄类型
    5. HICON 图标的句柄
    6. HINSTANCE 程序实例的句柄
    7. HKEY 注册表键的句柄
    8. HMODULE 模块的句柄
    9. HWND 窗口的句柄
    10. INT 32位符号整型数据类型
    11. INT_PTR 指向 INT 类型数据的指针类型
    12. INT32 32位符号整型
    13. INT64 64 位符号整型
    14. LONG32 32 位符号整型
    15. LONG64 64 位符号整型
    16. LPARAM 消息的 L 参数
    17. WPARAM 消息的 W 参数
    18. LPCSTR Windows,ANSI,字符串常量
    19. LPCTSTR 根据环境配置,如果定义了 UNICODE 宏,则是LPCWSTR类型,
    20. 否则是 LPCSTR 类型
    21. LPCWSTR UNICODE 字符串常量
    22. LPDWORD 指向 DWORD 类型数据的指针
    23. LPSTR Window,ANSI,字符串变量
    24. LPTSTR 根据环境配置,如果定义了 UNICODE,则是LPWSTR类型,否则是 LPSTR 类型
    25. LPWSTR UNICODE 字符串变量
    26. SIZE_T 表示内存大小,以字节为单位,其最大值是CPU 最大寻址范围TCHAR 如果定义了
    27. UNICODE,则为 WCHAR,否则为CHARWCHAR 16 位 Unicode 字符

    变量命名规则:

  • 相关阅读:
    ChatGPT追祖寻宗:GPT-1论文要点解读
    vue3 | HighCharts实战自定义封装之径向条形图
    【MySQL】数据库进阶之索引内容详解(上篇 索引分类与操作)
    GMT,UTC,CST,DST,RTC,NTP,SNTP,NITZ: 嵌入式的时间
    [02] Multi-sensor KIT: DSP 矩阵运算-加法,减法和逆矩阵,放缩,乘法和转置矩阵
    embedding层的理解
    2.5 Go语言中的切片(Slice)
    【软考软件评测师】基于风险的测试技术
    从AlexNet到chatGPT的演进过程
    OpenAI 函数调用教程
  • 原文地址:https://blog.csdn.net/qq_61553520/article/details/130875597