GUID
GUID 是一个 128 位的数字,在全球范围内是独一无二的,常被用于标识软件组件、设备接口等,以保证在不同系统和环境中能唯一识别特定对象。
DEFINE_GUID(GUID_DEVINTERFACE_USCUSTOMKEYS, 0x12345678, 0x1234, 0x5678, 0x12, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78);
DEFINE_GUID:这是一个宏,在 Windows 编程里用于定义 GUID。它在 <guiddef.h> 头文件中被定义,此宏会创建一个 GUID 类型的全局变量,并且对其进行初始化。
GUID_DEVINTERFACE_USCUSTOMKEYS:这是所定义的 GUID 的名称。在后续代码里,可借助这个名称来引用该 GUID。
0x12345678, 0x1234, 0x5678, 0x12, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78:这些是构成 128 位 GUID 的具体数值。GUID 通常会被拆分成几个部分,以便于表示和处理。
枚举设备
#include <windows.h>
#include <iostream>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
#include <tchar.h>
#pragma comment(lib, "setupapi.lib")
// 这里使用 USB 设备类的 GUID 作为示例,你可以根据需要替换为其他设备类的 GUID
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
int main() {
HDEVINFO hDevInfo;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL;
DWORD requiredSize = 0;
DWORD index = 0;
// 获取指定设备类的设备信息集
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
std::cerr << "SetupDiGetClassDevs failed with error: " << GetLastError() << std::endl;
return 1;
}
// 初始化 SP_DEVICE_INTERFACE_DATA 结构体
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// 枚举设备接口
while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, index, &deviceInterfaceData)) {
// 获取所需的缓冲区大小
SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);
// 分配缓冲区
deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredSize);
if (deviceInterfaceDetailData == NULL) {
std::cerr << "LocalAlloc failed with error: " << GetLastError() << std::endl;
SetupDiDestroyDeviceInfoList(hDevInfo);
return 1;
}
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// 获取设备接口的详细信息
if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL, NULL)) {
std::cerr << "SetupDiGetDeviceInterfaceDetail failed with error: " << GetLastError() << std::endl;
LocalFree(deviceInterfaceDetailData);
index++;
continue;
}
// 打印设备接口的详细信息
std::wcout << L"Device Path: " << deviceInterfaceDetailData->DevicePath << std::endl;
// 释放缓冲区
LocalFree(deviceInterfaceDetailData);
index++;
}
// 检查枚举是否因错误而终止
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
std::cerr << "SetupDiEnumDeviceInterfaces failed with error: " << GetLastError() << std::endl;
}
// 销毁设备信息集
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
SetupDiGetClassDevs
HDEVINFO SetupDiGetClassDevs(
const GUID *ClassGuid,
PCTSTR Enumerator,
HWND hwndParent,
DWORD Flags
);
功能:此函数会创建一个包含指定设备类的设备信息集,该集合可用来枚举和操作设备。
参数:
ClassGuid:指向设备类的 GUID(全局唯一标识符)的指针,指定要枚举的设备类。若为 NULL,则会返回所有设备类的设备。
Enumerator:指定设备枚举器名称的字符串,如 USB 或 PCI。若为 NULL,则不限制枚举器。
hwndParent:与设备安装程序相关的父窗口句柄,通常设为 NULL。
Flags:控制函数行为的标志位,常用标志有:
DIGCF_PRESENT:仅返回当前存在的设备。
DIGCF_DEVICEINTERFACE:返回支持指定设备接口类的设备。
返回值:若函数调用成功,会返回设备信息集的句柄;若失败,则返回 INVALID_HANDLE_VALUE。
SetupDiEnumDeviceInterfaces
BOOL SetupDiEnumDeviceInterfaces(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
const GUID *InterfaceClassGuid,
DWORD MemberIndex,
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);
功能:对设备信息集中的设备接口进行枚举。
参数:
DeviceInfoSet:由 SetupDiGetClassDevs 函数返回的设备信息集的句柄。
DeviceInfoData:指向 SP_DEVINFO_DATA 结构体的指针,用于指定特定设备。若为 NULL,则枚举所有设备。
InterfaceClassGuid:指向设备接口类的 GUID 的指针,指定要枚举的接口类。
MemberIndex:要枚举的设备接口的索引,从 0 开始。
DeviceInterfaceData:指向 SP_DEVICE_INTERFACE_DATA 结构体的指针,用于接收枚举到的设备接口信息。
返回值:若函数调用成功,返回 TRUE;若失败或没有更多设备接口可供枚举,返回 FALSE,此时可通过 GetLastError 函数获取错误代码。若错误代码为 ERROR_NO_MORE_ITEMS,则表示枚举结束。
SetupDiGetInterfaceDeviceDetail
BOOL SetupDiGetInterfaceDeviceDetail(
HDEVINFO DeviceInfoSet,
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,
DWORD DeviceInterfaceDetailDataSize,
PDWORD RequiredSize,
PSP_DEVINFO_DATA DeviceInfoData
);
功能:获取指定设备接口的详细信息,像设备路径之类的。
参数:
DeviceInfoSet:由 SetupDiGetClassDevs 函数返回的设备信息集的句柄。
DeviceInterfaceData:指向 SP_DEVICE_INTERFACE_DATA 结构体的指针,包含要获取详细信息的设备接口信息。
DeviceInterfaceDetailData:指向 SP_DEVICE_INTERFACE_DETAIL_DATA 结构体的指针,用于接收设备接口的详细信息。
DeviceInterfaceDetailDataSize:DeviceInterfaceDetailData 结构体的大小。
RequiredSize:指向 DWORD 类型变量的指针,用于接收所需的缓冲区大小。
DeviceInfoData:指向 SP_DEVINFO_DATA 结构体的指针,用于接收设备的详细信息。若为 NULL,则不接收该信息。
返回值:若函数调用成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。若所需的缓冲区大小超过 DeviceInterfaceDetailDataSize,则返回 FALSE,且 RequiredSize 会包含所需的缓冲区大小。
SetupDiDestroyDeviceInfoList
BOOL SetupDiDestroyDeviceInfoList(
HDEVINFO DeviceInfoSet
);
功能:销毁由 SetupDiGetClassDevs 函数创建的设备信息集,释放相关资源。
参数:
DeviceInfoSet:要销毁的设备信息集的句柄。
返回值:若函数调用成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
总结
这些函数通常按以下顺序使用:
调用 SetupDiGetClassDevs 函数创建设备信息集。
利用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集中的设备接口。
针对每个枚举到的设备接口,调用 SetupDiGetInterfaceDeviceDetail 函数获取详细信息。
最后调用 SetupDiDestroyDeviceInfoList 函数销毁设备信息集,释放资源。
总结
这些函数通常按以下顺序使用:
调用 SetupDiGetClassDevs 函数创建设备信息集。
利用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集中的设备接口。
针对每个枚举到的设备接口,调用 SetupDiGetInterfaceDeviceDetail 函数获取详细信息。
最后调用 SetupDiDestroyDeviceInfoList 函数销毁设备信息集,释放资源。
注意
需要注意环境的编码格式,根据实际使用选择
开打管道
m_hReadPipe = CreateFile((LPCSTR)m_ReadPipe, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
CreateFile 是 Windows API 中的一个函数,用于创建或打开文件、目录、物理磁盘、卷、控制台缓冲区、通信资源、邮件槽、管道等对象。此函数会返回一个句柄,后续可使用该句柄对这些对象进行操作。
-
(LPCSTR)m_ReadPipe
含义:这是要打开的对象的名称,在该代码里为管道的名称。m_ReadPipe 应当是一个字符串类型的变量,代表管道的名称。(LPCSTR) 是一个强制类型转换,将 m_ReadPipe 转换为 LPCSTR 类型(即指向以 null 结尾的多字节字符串的指针)。
注意:在 Unicode 字符集环境下,建议使用 CreateFileW 函数并传入 LPCWSTR 类型的参数,以避免字符编码问题。 -
GENERIC_READ | GENERIC_WRITE
含义:这是访问权限标志,用于指定对打开对象的访问方式。
GENERIC_READ:表示对对象具有读权限,也就是可以从对象中读取数据。
GENERIC_WRITE:表示对对象具有写权限,即能够向对象中写入数据。
GENERIC_READ | GENERIC_WRITE:表示同时具有读和写的权限。 -
FILE_SHARE_READ | FILE_SHARE_WRITE
含义:这是共享模式标志,用于指定其他进程对该对象的访问权限。
FILE_SHARE_READ:允许其他进程以读权限打开该对象。
FILE_SHARE_WRITE:允许其他进程以写权限打开该对象。
FILE_SHARE_READ | FILE_SHARE_WRITE:表示其他进程可以同时以读和写的权限打开该对象。 -
NULL
含义:这是指向 SECURITY_ATTRIBUTES 结构体的指针,用于指定对象的安全属性。若为 NULL,则表示使用默认的安全属性,并且该对象的句柄不能被子进程继承。 -
OPEN_EXISTING
含义:这是创建方式标志,用于指定如果对象不存在时的处理方式。OPEN_EXISTING 表示仅当对象已经存在时才打开它,如果对象不存在,函数将失败并返回 INVALID_HANDLE_VALUE。 -
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED
含义:这是文件属性和标志的组合。
FILE_ATTRIBUTE_NORMAL:表示文件没有特殊的属性,是最常见的文件属性。
FILE_FLAG_OVERLAPPED:表示以异步(重叠)方式打开文件。在异步模式下,对文件的读写操作会立即返回,而不会等待操作完成,可通过 OVERLAPPED 结构体来处理操作结果。 -
0
含义:这是模板文件的句柄,用于指定一个模板文件的句柄,新创建的文件将继承该模板文件的属性。若为 0,则表示不使用模板文件。
返回值
若函数调用成功,会返回一个有效的句柄,该句柄可用于后续对管道的读写操作。
若失败,会返回 INVALID_HANDLE_VALUE,可通过 GetLastError 函数获取具体的错误代码。
不要忘记还有 CloseHandle(m_hReadPipe);
CloseHandle 是 Windows API 中的一个函数,它的主要作用是关闭对象的句柄。在 Windows 操作系统里,许多系统资源(像文件、管道、线程、进程等)都会用句柄来标识,当不再需要使用这些资源时,就需要调用 CloseHandle 函数来关闭对应的句柄,从而释放系统资源。
异步读取
#include <windows.h>
#include <iostream>
#include <string>
const char* WRITE_PIPE_NAME = "\\\\.\\pipe\\Pipe0";
const char* READ_PIPE_NAME = "\\\\.\\pipe\\Pipe1";
const int BUFFER_SIZE = 1024;
const int WRITE_INTERVAL_MS = 3000;
const int MAIN_LOOP_INTERVAL_MS = 100; // 主循环间隔设为 100 毫秒
void WriteAsync(HANDLE hPipe, int writeCount, HANDLE hWriteEvent) {
OVERLAPPED overlapped = { 0 };
overlapped.hEvent = hWriteEvent;
std::string message = "Data write number " + std::to_string(writeCount);
DWORD bytesWritten;
if (!WriteFile(hPipe, message.c_str(), static_cast<DWORD>(message.length()), NULL, &overlapped)) {
if (GetLastError() != ERROR_IO_PENDING) {
std::cerr << "WriteFile failed: " << GetLastError() << std::endl;
return;
}
}
DWORD waitResult = WaitForSingleObject(hWriteEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
if (!GetOverlappedResult(hPipe, &overlapped, &bytesWritten, FALSE)) {
std::cerr << "GetOverlappedResult failed: " << GetLastError() << std::endl;
} else {
std::cout << "Asynchronous write completed. Bytes written: " << bytesWritten << std::endl;
}
ResetEvent(hWriteEvent);
} else {
std::cerr << "WaitForSingleObject failed: " << waitResult << std::endl;
}
}
void ReadAsync(HANDLE hPipe, HANDLE hReadEvent) {
OVERLAPPED overlapped = { 0 };
overlapped.hEvent = hReadEvent;
char buffer[BUFFER_SIZE];
DWORD bytesRead;
if (!ReadFile(hPipe, buffer, BUFFER_SIZE, NULL, &overlapped)) {
if (GetLastError() != ERROR_IO_PENDING) {
std::cerr << "ReadFile failed: " << GetLastError() << std::endl;
return;
}
}
DWORD waitResult = WaitForSingleObject(hReadEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
if (!GetOverlappedResult(hPipe, &overlapped, &bytesRead, FALSE)) {
std::cerr << "GetOverlappedResult failed: " << GetLastError() << std::endl;
} else if (bytesRead > 0) {
buffer[bytesRead] = '\0';
std::cout << "Asynchronous read completed. Bytes read: " << bytesRead << std::endl;
std::cout << "Read data: " << buffer << std::endl;
}
ResetEvent(hReadEvent);
} else {
std::cerr << "WaitForSingleObject failed: " << waitResult << std::endl;
}
}
int main() {
HANDLE hWritePipe = CreateFile(
WRITE_PIPE_NAME,
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (hWritePipe == INVALID_HANDLE_VALUE) {
std::cerr << "CreateFile for write pipe failed: " << GetLastError() << std::endl;
return 1;
}
HANDLE hReadPipe = CreateFile(
READ_PIPE_NAME,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (hReadPipe == INVALID_HANDLE_VALUE) {
std::cerr << "CreateFile for read pipe failed: " << GetLastError() << std::endl;
CloseHandle(hWritePipe);
return 1;
}
HANDLE hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hWriteEvent == NULL) {
std::cerr << "CreateEvent for write failed: " << GetLastError() << std::endl;
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
return 1;
}
HANDLE hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hReadEvent == NULL) {
std::cerr << "CreateEvent for read failed: " << GetLastError() << std::endl;
CloseHandle(hWriteEvent);
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
return 1;
}
int writeCount = 1;
DWORD lastWriteTime = GetTickCount();
while (true) {
DWORD currentTime = GetTickCount();
if (currentTime - lastWriteTime >= WRITE_INTERVAL_MS) {
WriteAsync(hWritePipe, writeCount++, hWriteEvent);
lastWriteTime = currentTime;
}
ReadAsync(hReadPipe, hReadEvent);
Sleep(MAIN_LOOP_INTERVAL_MS);
}
CloseHandle(hWriteEvent);
CloseHandle(hReadEvent);
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
return 0;
}
Pipe0 每 3 秒写入一次数据,同时在主循环中以更短的时间间隔尝试从 Pipe1 读取数据,且写入和读取操作分别使用独立的事件对象。
CreateEvent
创建或打开一个命名或未命名的事件对象,事件对象是一种同步对象,用于线程间或进程间的同步,可用来通知其他线程某个操作已经完成。
常用于异步操作中,当异步操作完成时,将事件对象设置为有信号状态,通知其他线程操作已完成。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
lpEventAttributes:指向 SECURITY_ATTRIBUTES 结构体的指针,该结构体决定返回的句柄是否可被子进程继承。若为 NULL,则句柄不能被继承。
bManualReset:指定事件对象的复位方式。若为 TRUE,则为手动复位事件,需要调用 ResetEvent 函数将其设置为无信号状态;若为 FALSE,则为自动复位事件,当一个等待线程被释放后,系统会自动将其设置为无信号状态。
bInitialState:指定事件对象的初始状态。若为 TRUE,则初始状态为有信号状态;若为 FALSE,则初始状态为无信号状态。
lpName:指定事件对象的名称,若为 NULL,则创建一个未命名的事件对象。
返回值
若函数成功,返回事件对象的句柄;若失败,返回 NULL,可通过 GetLastError 函数获取错误代码。
WaitForSingleObject
等待指定的对象(如事件、互斥体、信号量等)变为有信号状态,或者等待指定的时间间隔过去。
在异步操作中,用于等待异步操作完成的通知(即事件对象变为有信号状态)。
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数
hHandle:要等待的对象的句柄。
dwMilliseconds:等待的时间间隔,以毫秒为单位。若为 INFINITE,则表示无限期等待,直到对象变为有信号状态。
返回值
返回一个指示等待结果的 DWORD 值,常见的返回值有:
WAIT_OBJECT_0:表示等待的对象变为有信号状态。
WAIT_TIMEOUT:表示等待的时间间隔已过,对象仍未变为有信号状态。
ResetEvent
BOOL ResetEvent(
HANDLE hEvent
);
功能
将指定的事件对象设置为无信号状态。
参数
hEvent:要设置的事件对象的句柄。
返回值
若函数成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
使用场景
对于手动复位事件,在处理完事件通知后,需要调用该函数将事件对象重置为无信号状态,以便下次使用。
GetOverlappedResult
BOOL GetOverlappedResult(
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
BOOL bWait
);
功能
获取指定的异步(重叠)操作的结果,包括操作是否成功以及传输的字节数。
参数
hFile:进行异步操作的对象的句柄,如文件、管道等。
lpOverlapped:指向 OVERLAPPED 结构体的指针,该结构体包含了异步操作的相关信息。
lpNumberOfBytesTransferred:指向 DWORD 变量的指针,用于接收异步操作传输的字节数。
bWait:指定函数是否等待异步操作完成。若为 TRUE,则函数会等待操作完成;若为 FALSE,则若操作尚未完成,函数会立即返回 FALSE。
返回值
若函数成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
使用场景
在异步操作中,当事件对象变为有信号状态,表示异步操作可能已经完成,此时可调用该函数获取操作的具体结果。