目录
一、WIN32编程
Windows系统编程 API(也叫 Windows API 或 Win32 API)是 Microsoft 提供的一组 C 风格的函数接口,直接与 Windows 操作系统交互和编程,进行底层系统级功能开发。是一系列接口函数、宏、数据类型、数据结构的集合,为桌面应用程序、系统服务、驱动程序、图形界面、文件/进程/网络控制等底层功能所提供的一组 API 函数,它是构建传统 Windows 程序(.exe/.dll)的核心工具。常用功能可划分为以下几类:
| 分类 | 描述 |
|---|---|
| 核心系统(Kernel32.dll) | 文件I/O(读写文件、目录管理)、内存(虚拟内存、堆分配)、线程和进程(多任务处理)、同步 |
| 用户界面(User32.dll) | 窗口管理、消息循环、输入、对话框 |
| 图形界面(GDI32.dll) | 绘图、字体、图像处理 |
| 网络(ws2_32.dll) | TCP/IP、HTTP、Socket(Wininet, WinSock) |
| 安全(Advapi32.dll) | 注册表(系统配置存储)、用户权限、服务控制 |
| Shell(Shell32.dll) | 文件浏览器、任务栏、快捷方式(文件资源管理器相关) |
| 多媒体(MMSystem.dll) | 音频播放、定时器、MIDI |
| COM/OLE/ActiveX | 面向组件开发与自动化接口 |
| comdlg32.dll | 通用对话框(文件打开、颜色选择) |
1、创建窗口和消息循环(GUI):
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "MyWindowClass";
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow(
"MyWindowClass", // 窗口类名
"Win32 API 窗口示例", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, // X 坐标
CW_USEDEFAULT, // Y 坐标
400, // 宽度
300, // 高度
NULL, // 父窗口
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 附加数据
);
ShowWindow(hwnd, nCmdShow);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
2、文件操作:
#include <windows.h>
#include <stdio.h>
int main() {
// 创建文件
HANDLE hFile = CreateFile(
"C:\\test.txt", // 文件名
GENERIC_WRITE, // 访问模式(写入)
0, // 共享模式(不共享)
NULL, // 安全属性
CREATE_ALWAYS, // 创建方式(总是新建)
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL // 模板文件句柄
);
if (hFile == INVALID_HANDLE_VALUE) {
printf("CreateFile 失败: %d\n", GetLastError());
return 1;
}
// 写入数据
const char* data = "Hello, Win32 API!";
DWORD bytesWritten;
WriteFile(hFile, data, strlen(data), &bytesWritten, NULL);
// 关闭文件
CloseHandle(hFile);
return 0;
}
CreateFile / ReadFile / WriteFile:文件操作
GetFileAttributes / SetFileAttributes:文件属性管理
FindFirstFile / FindNextFile / CreateDirectory / RemoveDirectory :遍历目录
3、进程创建:
#include <windows.h>
int main() {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
//启动程序
BOOL success = CreateProcess(
"C:\\Windows\\notepad.exe", // 应用程序路径
NULL, // 命令行参数
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 是否继承句柄
0, // 创建标志
NULL, // 环境变量
NULL, // 当前目录
&si, // 启动信息
&pi // 进程信息
);
if (success) {
WaitForSingleObject(pi.hProcess, INFINITE); // 等待进程结束
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
printf("CreateProcess 失败: %d\n", GetLastError());
}
return 0;
}
同步与 IPC(进程间通信)
互斥体/信号量:CreateMutex / WaitForSingleObject。
管道(Pipe):
CreatePipe(匿名管道) / CreateNamedPipe(命名管道)。
4、多线程:
#include <windows.h>
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
printf("线程运行: %d\n", GetCurrentThreadId());
return 0;
}
DWORD WINAPI MyThread(LPVOID param) {
MessageBox(NULL, L"Thread running", L"Info", MB_OK);
return 0;
}
int main() {
HANDLE hThread = CreateThread(
NULL, // 安全属性
0, // 栈大小(默认)
ThreadFunc, // 线程函数
NULL, // 参数
0, // 创建标志
NULL // 线程 ID
);
WaitForSingleObject(hThread, INFINITE); // 等待线程结束
CloseHandle(hThread);
return 0;
}
5、内存管理:
#include <windows.h>
int main() {
// 分配虚拟内存
LPVOID pMem = VirtualAlloc(
NULL, // 由系统选择地址
4096, // 分配大小(4KB)
MEM_COMMIT, // 提交物理内存
PAGE_READWRITE // 可读可写
);
if (pMem) {
strcpy((char*)pMem, "Hello, VirtualAlloc!");
printf("%s\n", (char*)pMem);
// 释放内存
VirtualFree(pMem, 0, MEM_RELEASE);
}
return 0;
}
6、注册表操作
-
RegOpenKey/RegQueryValue:读写注册表。 -
RegSetValue/RegDeleteKey:修改注册表。
7、网络编程
-
Winsock API(基于套接字):
socket/bind/listen/accept(类似 BSD Socket)。 -
WinINet API(HTTP/FTP):
InternetOpen/InternetReadFile
二、共享内存使用
对于整个系统而言,主存与CPU的资源都是有限的,随着打开进程数量的增加,若是将所有进程运行所需的代码/数据/栈/共享库都存放在主存中,那么开启一部分进程就可以将主存占用完。使用虚拟内存不用将进程全部内容加载到主存上(局部性立了大功——进程在某段时间只需要用到部分代码及其数据),虚拟内存与主存通过不断的数据交换,只需要占用满足程序运行最小空间的内存即可完成程序运行。
共享内存基于虚拟内存通过内存映射的方式应运而生。虚拟内存为程序分配同样的空间便于管理(意味着进程自认为占用了整个内存)。同时,虚拟内存为每个进程增添地址空间的安全性,保护其地址空间不被其他进程所破坏。
同一台主机下多进程之间通信,其他方式需要通过内核进行四次数据交互才能完成一次通信,但共享内存只需要两次且不经过内核。相对于其他方式,共享内存占用的CPU资源更少且速度更快。不幸的是,共享内存缺少对同步的控制,需要通过其他方式去控制对内存读取同步。弥补共享内存的不足,可以通过对写入数据时设置有效位,这样通过对位的检查就能有效控制数据同步。
原理:
在Windows下每个进程都存在一张页表,通过MMU将进程的虚拟地址映射到真实的物理内存。基于虚拟内存的使用原理,共享内存在此基础上通过函数指定方式将多个进程的虚拟内存同时指向同一片共享内存地址空间,并通过函数参数控制进程的访问权限等参数,从而完成多个进程间的数据通信。

Windows下共享内存API函数
句柄
句柄作为共享内存机制的重要概念,日常编程中不会经常用到。将句柄具象化可以理解为“刀柄”,操作系统“手握”句柄指向一块区域,区域中存有各个对象的地址、属性等信息,操作系统通过“刀柄”可以顺藤摸瓜找到对象所在以及其他信息。
在windows编程中,有个概念是句柄,句柄指向资源(一切可以利用的物理的逻辑的资源),其中文件操作,可以将文件映射到内存,此处的文件是广义的文件,可以指内存对象,邮件槽等。
在windows中创建一个指向文件的虚拟内存,然后多个进程创建各个进程对这块内存的映射,通过访问各个进程的映射内存对这块虚拟内存进行访问,是共享内存实现的原理。

CreateFileMappingA函数
HANDLE CreateFileMappingA(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName
);
HANDLE hmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, FILESIZE, lpName);

返回值:创建成功返回文件句柄,创建失败则返回NULL。
OpenFileMappingA函数
HANDLE OpenFileMappingA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
OpenFileMappingA(FILE_MAP_ALL_ACCESS,FALSE,lpName);

返回值:成功返回文件句柄,失败则返回NULL。
MapViewOfFile函数
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap
);
MapViewOfFile(hmapfile,FILE_MAP_ALL_ACCESS,0,0,0);

返回值:成功返回映射视图起始地址,失败返回NULL。
UnmapViewOfFile函数
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
UnmapViewOfFile(lpbase);

返回值:成功返回非零,失败返回零。
CloseHandle函数
BOOL CloseHandle(HANDLE hObject);
CloseHandle(hmap);

返回值:成功返回非零。
Handle
Handle的用途就是实现线程之间的通信,例如:当子线程做一个耗时的操作的时候,我们并不知道他什么时候做完,做完了也不知道,那么,我们是不是需要一个东西来通知我们,让我们知道呀,所以说,也就有了Handle的来源,没有Handle这个东西,对我们来说,非常的麻烦。句柄(Handle)是一个用于标识和管理系统资源的抽象标识符。不同类型的句柄用于管理不同类型的资源。
演示:
1、写入端:
#include<iostream>
#include<windows.h>
using namespace std;
int main() {
unsigned long buffsize = 100;
// 创建或打开一个命名的内存映射文件对象
HANDLE m_handle= CreateFileMapping(
INVALID_HANDLE_VALUE, // 使用系统分页文件: INVALID_HANDLE_VALUE表示共享未与文件关联的内存
NULL, // 默认安全性,决定返回的句柄是否可以被子进程继承,如果为 NULL,则不能。
PAGE_READWRITE, // 可读写权限,当取 PAGE_READWRITE 时,表示对文件映射对象有可读可写的权限
0, // 最大对象大小(高位)
buffsize, // 最大对象大小(低位)
L"SharedMemory"
);
if (m_handle == NULL) {
cout << "CreateFileMapping failed: " << GetLastError() << endl;
return 1;
}
// 映射缓冲区视图,如果函数成功,则返回值是映射视图的起始地址。
// 将文件映射对象映射进调用的进程的虚拟地址空间
LPVOID pVoid = MapViewOfFile(
m_handle, // 映射对象句柄
FILE_MAP_ALL_ACCESS, // 可读写许可
0,//表示文件映射起始偏移的高32位
0,//表示文件映射起始偏移的低32位
buffsize// 映射大小,文件中要映射的字节数。0表示映射整个文件映射对象,单位是字节。
);
if (pVoid == NULL) {
cout << "MapViewOfFile failed" << GetLastError << endl;
CloseHandle(m_handle);
return 1;
}
HANDLE Semaphore_write = CreateSemaphore(NULL, 1, 1 ,L"MySemaphore");
if (Semaphore_write == NULL) {
cout << "CreateSemaphore failed: " << GetLastError() << endl;
UnmapViewOfFile(pVoid);
CloseHandle(m_handle);
return 1;
}
int num=1;
while (true) {
WaitForSingleObject(Semaphore_write,INFINITE);
memcpy(pVoid,&num, sizeof(num));
FlushViewOfFile(pVoid, buffsize); // 确保写入生效
cout << "write:num=" << num << endl;
num++;
ReleaseSemaphore(Semaphore_write, 1, NULL);
Sleep(1000);
}
UnmapViewOfFile(pVoid); //取消文件映射后,进程中该部分的地址会被释放(可以理解成free操作),然后该部分的内存可以被用于其他分配。
CloseHandle(Semaphore_write);
CloseHandle(m_handle);
}
2、读取端:
#include<iostream>
#include<windows.h>
using namespace std;
int main() {
unsigned long buffsize = 100;
HANDLE m_handle = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE, // 继承标志
L"SharedMemory"
);
if (m_handle == NULL) {
cout << "CreateFileMapping failed: " << GetLastError() << endl;
return 1;
}
LPVOID pVoid = MapViewOfFile(
m_handle,
FILE_MAP_ALL_ACCESS,
0,
0,
buffsize
);
if (pVoid == NULL) {
cout << "MapViewOfFile failed" << GetLastError << endl;
CloseHandle(m_handle);
return 1;
}
HANDLE Semaphore_read = OpenSemaphore(
SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, // 需要这两个权限
FALSE, // 不继承
L"MySemaphore" // 必须和写入程序一致
);
if (Semaphore_read == NULL) {
cout << "OpenSemaphore failed: " << GetLastError() << endl;
UnmapViewOfFile(pVoid);
CloseHandle(m_handle);
return 1;
}
int read_num = 0;
while (true) {
WaitForSingleObject(Semaphore_read, INFINITE);
memcpy(&read_num, pVoid, sizeof(read_num));
cout << "read num=" << read_num << endl;
ReleaseSemaphore(Semaphore_read, 1, NULL);
Sleep(1000);
}
UnmapViewOfFile(pVoid);
CloseHandle(m_handle);
CloseHandle(Semaphore_read);
return 0;
}
3、运行结果:

三、HANDLE
在 Windows 编程中,HANDLE 是一个通用资源句柄。它是一个 typedef void* HANDLE;也就是一个指向系统内核对象的指针(或标识符)。换句话说,HANDLE 是 Windows 内核用来 标识一个被操作系统管理的对象的“编号”或“指针”。它不是你能直接解引用的指针,而是交给 Windows API 使用的不透明句柄(opaque handle)。
在 Windows 里,几乎所有的系统资源都用 HANDLE 来表示。
| 资源类型 | 创建 API | 对应关闭 API | HANDLE 用途 |
|---|---|---|---|
| 文件 / 目录 | CreateFile() | CloseHandle() | 文件 I/O 操作 |
| 线程 | CreateThread() | CloseHandle() | 控制线程、等待线程结束 |
| 进程 | CreateProcess() | CloseHandle() | 管理或等待进程结束 |
| 事件对象 | CreateEvent() | CloseHandle() | 多线程同步 |
| 互斥体 | CreateMutex() | CloseHandle() | 线程同步 |
| 信号量 | CreateSemaphore() | CloseHandle() | 控制资源访问 |
| 管道 | CreatePipe() | CloseHandle() | 进程间通信 |
| 文件映射 | CreateFileMapping() | CloseHandle() | 内存共享、映射文件 |
| 控制台输入输出 | GetStdHandle(STD_OUTPUT_HANDLE) | 不用关闭 | 控制台输入/输出 |
| 注册表键 | RegOpenKeyEx() | RegCloseKey() | 注册表操作(专用句柄类型) |
这些句柄都是通过 Windows API 获得,最终都可以传给 CloseHandle() 来释放资源(除特殊句柄如 GetStdHandle)。
Windows 是一个基于内核对象模型(Kernel Object Model)的系统。操作系统在内核空间维护各种对象,比如文件、线程、事件、进程、信号量等。用户态程序不直接访问这些内核结构,而是通过 HANDLE 来间接访问。这有几个好处:
-
安全性:应用程序看不到实际内核数据结构;
-
跨进程隔离:句柄表是进程独立的;
-
引用计数:Windows 内核根据句柄引用计数来控制对象生命周期;
-
统一接口:所有对象都能被统一管理(创建、等待、关闭等)。
使用示例:
#include <windows.h>
#include <iostream>
int main()
{
HANDLE hFile = CreateFile(
L"test.txt", // 文件名
GENERIC_WRITE, // 访问权限
0, // 共享模式
NULL, // 安全属性
CREATE_ALWAYS, // 创建模式
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL // 模板文件句柄
);
if (hFile == INVALID_HANDLE_VALUE)
{
std::cerr << "Failed to open file" << std::endl;
return 1;
}
const char* data = "Hello, HANDLE!\r\n";
DWORD written = 0;
WriteFile(hFile, data, strlen(data), &written, NULL);
CloseHandle(hFile);
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
std::cout << "Thread running..." << std::endl;
return 0;
}
int main()
{
HANDLE hThread = CreateThread(
NULL, 0, ThreadFunc, NULL, 0, NULL
);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return 0;
}
//////////事件/同步对象/////////////
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, L"MyEvent");
// 通知事件
SetEvent(hEvent);
// 等待事件
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hEvent);
HANDLE 与文件流的区别
| 对比项 | HANDLE | FILE* | std::ofstream |
|---|---|---|---|
| 来源 | Windows API | C 标准库 | C++ 标准库 |
| 属于 | Win32 内核对象 | CRT (C Runtime) | C++ STL |
| 可移植性 | Windows 专用 | 跨平台 | 跨平台 |
| 性能 | 更接近系统底层,速度快 | 较高层封装 | 最上层封装 |
| 典型操作 | ReadFile / WriteFile | fread / fwrite | << 运算符 |
| 关闭方式 | CloseHandle() | fclose() | 析构时自动关闭 |
| 控制粒度 | 精细(可重叠 I/O、异步) | 一般 | 一般 |
HANDLE 与内核对象的生命周期
每个进程有自己的 句柄表(Handle Table),当调用如 CreateFile 时:
-
内核创建一个文件对象;
-
内核在当前进程的句柄表中注册一条记录;
-
返回一个 HANDLE 值(通常是一个整数索引或指针);
-
当你调用
CloseHandle时,该表项被删除,引用计数减一; -
当引用计数为 0 时,内核销毁该对象。
| 关键词 | 含义 |
|---|---|
typedef void* HANDLE; | 其实就是一个不透明指针 |
CloseHandle(h); | 关闭任何内核对象 |
INVALID_HANDLE_VALUE | 错误时的返回值(不是 NULL) |
WaitForSingleObject(h, INFINITE) | 等待内核对象(线程、事件等) |
DuplicateHandle | 复制句柄,用于跨进程共享 |
GetStdHandle(STD_OUTPUT_HANDLE) | 获取标准输出/输入句柄 |
四、LPVOID
typedef void *PVOID;
typedef PVOID LPVOID;
LPVOID 就是 void*
| 类型 | 实际含义 | 说明 |
|---|---|---|
BOOL | int | 逻辑值(TRUE/FALSE) |
BYTE | unsigned char | 8 位无符号 |
WORD | unsigned short | 16 位 |
DWORD | unsigned long | 32 位 |
HANDLE | void* | 内核对象句柄 |
PVOID | void* | Pointer to VOID |
LPVOID | void* | Long Pointer to VOID |
LPCVOID | const void* | Long Pointer to Const VOID |
LPSTR | char* | 指向 ANSI 字符串 |
LPCSTR | const char* | 指向常量 ANSI 字符串 |
LPWSTR | wchar_t* | 指向 Unicode 字符串 |
LPCWSTR | const wchar_t* | 指向常量 Unicode 字符串 |
LP 是 “Long Pointer” 的缩写。
在 16 位 Windows(Windows 3.x)中,有“近指针(near pointer)”和“远指针(far pointer)”的概念。后来到了 32 位/64 位系统,这种区别没了,但命名习惯保留了下来。
因此:
LPVOID = Long Pointer to Void → 实际上就是 void*
LPSTR = Long Pointer to String (char*)
LPWSTR = Long Pointer to Wide String (wchar_t*)
它一般用在通用数据指针参数中使用:
//线程入口函数
DWORD WINAPI MyThreadProc(LPVOID lpParam)
{
int* pValue = (int*)lpParam; // 强制转换
printf("Thread param = %d\n", *pValue);
return 0;
}
int main()
{
int data = 42;
HANDLE hThread = CreateThread(NULL, 0, MyThreadProc, &data, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
这里的 lpParam 就是一个“万能参数指针”,可以传任何结构体地址进去。LPVOID 让函数更通用,像 C 的 void* 一样。
内存操作:
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer, // 输出缓冲区
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
这里的 LPVOID lpBuffer 用来接收数据,类型不确定,所以用 void*。这就是 LPVOID 存在的意义:“可以指向任何类型的内存区域”。
现代C++中:
void* pData; // 等价于 LPVOID
const void* pConst; // 等价于 LPCVOID
wchar_t* pWide; // 等价于 LPWSTR
const wchar_t* pConstWide; // 等价于 LPCWSTR

1万+

被折叠的 条评论
为什么被折叠?



