Windows API 学习笔记(3) 文件, 设备IO, 系统信息
文件, 设备IO, 系统信息
运行环境为vs2022
文件相关
CreateFile
打开/创建文件, 返回文件句柄, 可设置访问权限, 是否共享, 覆盖还是追加.
1 |
|
dwDesiredAccess
访问权限:
GENERIC_READ
:只读GENERIC_WRITE
:只写GENERIC_READ | GENERIC_WRITE
:读写
dwCreationDisposition
文件存在/不存在时的行为:
CREATE_NEW
:新建(若存在则失败)CREATE_ALWAYS
:覆盖已有文件OPEN_EXISTING
:打开已有文件(不存在则失败)OPEN_ALWAYS
:打开或创建TRUNCATE_EXISTING
:清空已有文件
ReadFile / WriteFile
读写文件, 都会移动文件指针.
1 |
|
文件读写实操
向文件追加”hello world!”, 并读出.
1 |
|
存储设备相关
Windows文件系统
物理磁盘 : 最基础的物理结构, 存在分区用来管理.
卷 : 逻辑概念, 操作系统和用户直接交互的“存储容器”.
- 就是常见的C盘, D盘, E盘.
- 一个卷可以由一个或多个物理磁盘构成.
文件系统 : 软件概念, 对卷的上层构造, 用文件系统去描述卷, 进而去操作其底层的物理磁盘.
在Windows操作系统中, 物理磁盘和卷都是可以做为文件被打开的, 进而用来查询一些具体参数.
DeviceIoControl
可以用来访问打开的各种硬件, 获取各种参数与实际状态, 及用来和驱动(设备对象)通信. (物理磁盘和卷便可访问)
1 |
|
看起来很复杂, 但核心都在控制代码处, 你在这里传入的参数用于表明你想获取什么样的设备数据, 在后面填入接收的缓冲区即可.
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
获取磁盘的几何信息下面是向
DeviceIoControl
传入IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
来获取对应磁盘的几何信息的代码 :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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <iostream>
#include<locale>
// 打印磁盘几何结构信息
void PrintDiskGeometry(DISK_GEOMETRY_EX* diskGeometry) {
printf("=== 磁盘物理几何结构 ===\n");
printf("柱面数(Cylinders): %I64d\n", diskGeometry->Geometry.Cylinders.QuadPart);
printf("磁头数(TracksPerCylinder): %lu\n", diskGeometry->Geometry.TracksPerCylinder);
printf("每磁道扇区数(SectorsPerTrack): %lu\n", diskGeometry->Geometry.SectorsPerTrack);
printf("每扇区字节数(BytesPerSector): %lu\n", diskGeometry->Geometry.BytesPerSector);
// 计算并显示磁盘总大小
ULONGLONG totalSize = diskGeometry->Geometry.Cylinders.QuadPart *
diskGeometry->Geometry.TracksPerCylinder *
diskGeometry->Geometry.SectorsPerTrack *
diskGeometry->Geometry.BytesPerSector;
printf("\n=== 磁盘容量计算 ===\n");
printf("计算总容量: %I64u 字节\n", totalSize);
printf("计算总容量: %.2f GB\n", (double)totalSize / (1024 * 1024 * 1024));
// 显示DISK_GEOMETRY_EX中提供的磁盘大小
printf("\n=== 实际磁盘大小 ===\n");
printf("实际磁盘大小: %I64u 字节\n", diskGeometry->DiskSize.QuadPart);
printf("实际磁盘大小: %.2f GB\n", (double)diskGeometry->DiskSize.QuadPart / (1024 * 1024 * 1024));
}
int main(int argc, char* argv[]) {
setlocale(LC_ALL, "");
// 默认查询PhysicalDrive0,或使用命令行参数
wchar_t devicePath[32] = L"\\\\.\\PhysicalDrive0";
if (argc > 1) {
return 0;
}
wprintf(L"正在查询磁盘: %ls\n", devicePath);
// 打开物理磁盘
HANDLE hDevice = CreateFile(
devicePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
printf("无法打开磁盘设备,错误代码: %lu\n", err);
// 获取错误描述
LPVOID errMsg;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
0,
(LPTSTR)&errMsg,
0,
NULL
);
std::wcout << L"错误描述: " << (wchar_t*)errMsg << std::endl;
LocalFree(errMsg);
return 1;
}
// 准备获取磁盘几何结构
DISK_GEOMETRY_EX diskGeometry = { 0 };
DWORD bytesReturned = 0;
// 发送IOCTL请求
BOOL result = DeviceIoControl(
hDevice,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
NULL,
0,
&diskGeometry,
sizeof(diskGeometry),
&bytesReturned,
NULL
);
if (!result) {
DWORD err = GetLastError();
printf("获取磁盘几何信息失败,错误代码: %lu\n", err);
LPVOID errMsg;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
0,
(LPTSTR)&errMsg,
0,
NULL
);
printf("错误描述: %s\n", (char*)errMsg);
LocalFree(errMsg);
CloseHandle(hDevice);
return 1;
}
// 打印获取到的信息
PrintDiskGeometry(&diskGeometry);
// 关闭设备句柄
CloseHandle(hDevice);
return 0;
}运行结果 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14正在查询磁盘: \\.\PhysicalDrive0
=== 磁盘物理几何结构 ===
柱面数(Cylinders): 62260
磁头数(TracksPerCylinder): 255
每磁道扇区数(SectorsPerTrack): 63
每扇区字节数(BytesPerSector): 512
=== 磁盘容量计算 ===
计算总容量: 512105932800 字节
计算总容量: 476.94 GB
=== 实际磁盘大小 ===
实际磁盘大小: 512110190592 字节
实际磁盘大小: 476.94 GBIOCTL_STORAGE_QUERY_PROPERTY
查询物理磁盘的属性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
101
102
103
104
105
106
107
108
109#include <windows.h>
#include <winioctl.h>
#include <iostream>
#include <iomanip>
#include <setupapi.h>
#include <initguid.h>
#include <devguid.h>
#include <locale>
#include <vector>
using namespace std;
void PrintStorageProperty(HANDLE hDevice) {
STORAGE_PROPERTY_QUERY query{};
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
// 第一次调用获取所需缓冲区大小
STORAGE_DESCRIPTOR_HEADER header{};
DWORD bytesReturned = 0;
if (!DeviceIoControl(
hDevice,
IOCTL_STORAGE_QUERY_PROPERTY,
&query,
sizeof(query),
&header,
sizeof(header),
&bytesReturned,
nullptr)) {
wcerr << L"获取存储属性头信息失败,错误码: " << GetLastError() << endl;
return;
}
// 分配缓冲区
vector<BYTE> buffer(header.Size);
auto descriptor = reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(buffer.data());
if (!DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&query, sizeof(query), buffer.data(), header.Size, &bytesReturned, nullptr)) {
wcerr << L"错误: " << GetLastError() << endl;
return;
}
// 安全地获取字符串信息
auto GetStringFromOffset = [&](DWORD offset) -> const char* {
return (offset > 0 && offset < header.Size) ?
reinterpret_cast<const char*>(buffer.data() + offset) : "N/A";
};
wcout << L"=== 磁盘设备信息 ===" << endl;
wcout << L"制造商: " << GetStringFromOffset(descriptor->VendorIdOffset) << endl;
wcout << L"型号: " << GetStringFromOffset(descriptor->ProductIdOffset) << endl;
wcout << L"固件版本: " << GetStringFromOffset(descriptor->ProductRevisionOffset) << endl;
wcout << L"序列号: " << GetStringFromOffset(descriptor->SerialNumberOffset) << endl;
wcout << L"总线类型: ";
switch (descriptor->BusType) {
case BusTypeUnknown: wcout << L"未知"; break;
case BusTypeScsi: wcout << L"SCSI"; break;
case BusTypeAtapi: wcout << L"ATAPI"; break;
case BusTypeAta: wcout << L"ATA"; break;
case BusType1394: wcout << L"IEEE 1394"; break;
case BusTypeSsa: wcout << L"SSA"; break;
case BusTypeFibre: wcout << L"光纤通道"; break;
case BusTypeUsb: wcout << L"USB"; break;
case BusTypeRAID: wcout << L"RAID"; break;
case BusTypeiScsi: wcout << L"iSCSI"; break;
case BusTypeSas: wcout << L"SAS"; break;
case BusTypeSata: wcout << L"SATA"; break;
case BusTypeSd: wcout << L"SD"; break;
case BusTypeMmc: wcout << L"MMC"; break;
case BusTypeVirtual: wcout << L"虚拟"; break;
case BusTypeFileBackedVirtual: wcout << L"文件虚拟"; break;
case BusTypeSpaces: wcout << L"存储空间"; break;
case BusTypeNvme: wcout << L"NVMe"; break;
case BusTypeSCM: wcout << L"SCM"; break;
case BusTypeUfs: wcout << L"UFS"; break;
case BusTypeMax: wcout << L"最大"; break;
default: wcout << L"未知 (" << descriptor->BusType << L")";
}
wcout << endl;
wcout << L"命令队列支持: " << (descriptor->CommandQueueing ? L"是" : L"否") << endl;
}
int main() {
setlocale(LC_ALL, "");
// 打开磁盘0
HANDLE hDevice = CreateFileW(
L"\\\\.\\PhysicalDrive0",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
nullptr);
if (hDevice == INVALID_HANDLE_VALUE) {
wcerr << L"无法打开磁盘设备,错误码: " << GetLastError() << endl;
return 1;
}
// 查询并打印磁盘信息
PrintStorageProperty(hDevice);
CloseHandle(hDevice);
return 0;
}运行结果 :
1
2
3
4
5
6
7=== 磁盘设备信息 ===
制造商: NVMe
型号: WD PC SN740 SDDPNQD-512G-1002
固件版本: 73101000
序列号: E823_8FA6_BF53_0001_001B_448B_4A8D_25AA.
总线类型: NVMe
命令队列支持: 是
这里只是介绍了获取设备信息的用途, 但实际其可以与各种设备进行交互, 设置与输出.
GetDiskFreeSpace / GetDiskFreeSpaceEx
获取卷(盘)的空间信息, 包括总容量, 可用空间等. 前者为旧版, 后者为新版常用.
一般用来在备份或云空间存储时根据空间信息进行灵活判断.
1 |
|
下面是获取C盘信息的代码 :
1 |
|
运行结果 :
1 |
|
肉眼可见的已经爆红了qwq.
GetVolumeInformation
获取卷的基础信息, 比如卷名称, 序列号, 最大文件名长, 文件系统等.
1 |
|
下面检查各种可能的卷, 然后输出对应卷的基础属性 :
1 |
|
运行结果 :
1 |
|
系统信息相关
GetSystemInfo
获取操作系统硬件相关的信息. 如CPU数, 处理器类型, 内存页大小.
1 |
|
GlobalMemoryStatusEx
获取当前内存使用情况, 包括物理内存, 虚拟内存的总量和可用量.
1 |
|
GetSystemTime
获取系统当前的UTC(协调世界时, 北京时区需要 + 8时).
1 |
|
GetTickCount
获取自系统启动以来的毫秒数(32 位整数).
1 |
|
测试代码
1 |
|
运行结果 :
1 |
|