WindowsAPI查缺补漏-文件驱动器目录
碎碎念
硬盘容量=柱面数(磁道数)*磁头数(盘面数)*每磁道扇区数*每扇区字节数。
目录
SetCurrentDirectory
设置当前目录:
1 2 3
| BOOL SetCurrentDirectory( LPCTSTR lpPathName )
|
GetCurrentDirectory
获取当前目录:
1 2 3 4
| DWORD GetCurrentDirectory( _In_ DWORD nBufferLength, _Out_ LPTSTR lpBuffer )
|
GetFullPathName
获取一个文件完整路径:
1 2 3 4 5 6
| DWORD WINAPI GetFullPathName( _In_ LPCTSTR lpFileName, _In_ DWORD nBufferLength, _Out_ LPTSTR lpBuffer, _Outptr_opt_ LPTSTR* lpFilePart )
|
文件操作
CreateFile
创建或打开文件:
1 2 3 4 5 6 7 8 9
| HANDLE WINAPI CreateFile( _In_ LPCTSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecruityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile )
|
对于lpFileName参数,ANSI路径名限制MAX_PATH个字符,Unicode版本路径名称字符串限制为32767个字符。
对于dwDesiredAccess参数,指定对文件的访问权限,有GENERICE_WRITE和GENERIC_READ。
对于dwShareMode参数,表示文件在被打开后是否允许其他进程或线程再次打开文件:
枚举值 |
含义 |
0 |
独占文件,不允许被再次打开 |
FILE_SHARE_READ |
允许读 |
FILE_SHARE_WRITE |
允许写 |
FILE_SHARE_DELETE |
允许删除 |
对于dwCreationDisposition参数,决定文件已经存在或不存在所采取的操作,用GetLastError
获取错误码。
枚举值 |
含义 |
CREATE_NEW |
文件不存在时创建一个新文件。已存在则失败,错误码ERROR_FILE_EXISTS |
CREATE_ALWAYS |
始终创建新文件。成功创建错误码0。已存在但不可写则清空并覆盖,错误码ERROR_ALREADY_EXISTS |
OPEN_EXISTING |
仅打开已存在文件。不存在失败,错误码ERROR_FILE_NOT_FOUND |
OPEN_ALWAYS |
始终打开文件。已存在成功,错误码ERROR_ALREADY_EXISTS。有效路径不存在则创建且成功,错误码0 |
TRUNCATE_EXISTING |
打开文件并截断,存在时使其大小为0。不存在则失败,错误码ERROR_FILE_NOT_FOUND |
对于dwFlagsAndAttributes参数,分为文件标志和系统属性,文件标志:
枚举值 |
含义 |
FILE_ATTRIBUTE_NORMAL |
普通文件 |
FILE_ATTRIBUTE_READONLY |
只读 |
FILE_ATTRIBUTE_HIDDEN |
隐藏 |
FILE_ATTRIBUTE_SYSTEM |
操作系统文件 |
FILE_ATTRIBUTE_ARCHIVE |
待备份/删除 |
FILE_ATTRIBUTE_TEMPORARY |
临时存储,尽量在内存中,不久将删除 |
FILE_ATTRIBUTE_ENCRYPTED |
已加密 |
文件标志:
枚举值 |
含义 |
FILE_FLAG_DELETE_ON_CLOSE |
关闭句柄后立刻删除文件 |
FILE_FLAG_OVERLAPPED |
异步I/O |
FILE_FLAG_NO_BUFFERING |
写操作不用系统缓存 |
FILE_FLAG_WRITE_THROUGH |
写操作不通过中间缓存,修改直接写入硬盘 |
打开已经存在的文件:
1 2 3 4 5
| HANDLE hFile; hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { };
|
新建文件:
1 2 3 4
| hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRTIE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { };
|
关闭文件对象句柄用CloseHandle
。
ReadFile
从指定文件读取数据:
1 2 3 4 5 6 7
| BOOL WINAPI ReadFile( _In_ HANDLE hFile, _Out_ LPVOID lpBuffer, _In_ DWORD nNumberOfBytesToRead, _Out_opt_ LPDWORD lpNumberOfBytesRead, _Inout_opt_ LPOVERLAPPED lpOverlapped )
|
当还没读完nNumberOfBytesToRead时读到文件尾,则返回TRUE,但*lpNumberOfBytesRead为0,通过这个判断是否读到文件末尾。
使用异步I/O时,CreateFile
的dwFlagsAndAttributes应指定FILE_FLAG_OVERLAPPED。
WriteFile
向指定文件写入数据:
1 2 3 4 5 6 7
| BOOL WINAPI WriteFile( _In_ HANDLE hFIle, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped )
|
FlushFileBuffers
用WriteFile
写入文件时,写入的数据可能只保存在高速缓存里。为保证所有数据正确写入硬盘,需要用CloseHandle
关闭文件句柄,或用这个函数。但把关键数据即时写入硬盘可能需要多次调用这个函数,所以最好是在CreateFile
时指定FILE_FLAG_NO_BUFFERING和FILE_FLAG_WRITE_THROUGH标志。
1 2 3
| BOOL WINAPI FlushFileBuffers( _In_ HANDLE hFile )
|
SetFilePointerEx
调整文件指针:
1 2 3 4 5 6
| BOOL WINAPI SetFilePointerEx( _In_ HANDLE hFile, _In_ LARGE_INTEGER liDistanceToMove, _Out_opt_ PLARGE_INTEGER lpNewFilePointer, _In_ DWORD dwMoveMethod )
|
对于dwMoveMethod参数,可以是:
枚举值 |
含义 |
FILE_BEGIN |
文件开头 |
FILE_CURRENT |
文件指针当前位置 |
FILE_END |
文件末尾 |
dwMothMethod指定的位置加上liDistanceToMove就是新文件指针位置,liDistanceToMove正数为向文件尾移动,负数向文件头移动。当从文件尾继续向后移动时,表示拓展文件大小。例如:
1 2 3 4 5 6 7 8 9 10
| TCHAR szStr[] = TEXT("xxx"); static HANDLE hFile; LARGE_INTEGER liDistanceToMove = { 900 }; LARGE_INTEGER liNewFilePointer; hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_END); WriteFile(hFile, szStr, _tcslen(szStr) * sizeof(TCHAR), NULL, NULL); CloseHandle(hFile); };
|
需要获取当前文件指针时,把liDistanceToMove值设为0,dwMoveMethod设为FILE_CURRENT,从lpNewFilePointer返回当前文件指针。例如:
1 2
| LARGE_INTEGER liDistanceToMove = { 0 }; SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_CURRENT);
|
SetEndOfFile
把文件结尾设置为文件指针当前位置,可实现文件截断或扩展。例如设置文件指针为扩展后的大小,并调用该函数即可扩展文件大小。
1 2 3
| BOOL WINAPI SetEndOfFile( _In_ HANDLE hFile )
|
例如文件大小扩展为8GB:
1 2 3 4 5 6 7 8 9
| static HANDLE hFile; LARGE_INTEGER liDistanceToMove; liDistanceToMove.QuadPart = (LONGLONG)8 * 1024 * 1024 * 1024; LARGE_INTEGER liNewFilePointer; hFile = CreateFile(TEXT("D:\\Test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, FILE_BEGIN); SetEndOfFile(hFile); };
|
文件属性
GetFileSizeEx
获取文件大小:
1 2 3 4 5
| #include <windows.h> BOOL WINAPI GetFileSizeEx( _In_ HANDLE hFile, _Out_ PLARGE_INTEGER lpFileSize )
|
GetFileType
获取文件类型:
1 2 3
| DWORD WINAPI GetFileType( _In_ HANDLE hFile )
|
返回值:
枚举值 |
含义 |
FILE_TYPE_UNKNOW |
未知文件类型或调用失败。 |
FILE_TYPE_DISK |
磁盘文件。 |
FILE_TYPE_CHAR |
字符文件如LPT设备或控制台。 |
FILE_TYPE_PIPE |
套接字、命名管道或匿名管道。 |
GetFileTime
获取文件创建/最后访问/最后修改时间:
1 2 3 4 5 6
| BOOL WINAPI GetFileTime( _In_ HANDLE hFile, _Out_opt_ LPFILETIME lpCreationTime, _Out_opt_ LPFILETIME lpLastAccessTime, _Out_opt_ LPFILETIME lpLastWriteTime )
|
FILETIME用FileTimeToSystemTime
转为SYSTEMTIME结构,这里不讲。
SetFileTime
设置文件创建/最后访问/最后修改时间:
1 2 3 4 5 6
| BOOL WINAPI SetFileTime( _In_ HANDLE hFile, _Out_opt_ CONST LPFILETIME lpCreationTime, _Out_opt_ CONST LPFILETIME lpLastAccessTime, _Out_opt_ CONST LPFILETIME lpLastWriteTime )
|
SYSTEMTIME用SystemTimeToFileTime
转为FILETIME结构,这里不讲。
GetFileAttributes/SetFileAttributes
获取/设置文件系统属性,文件系统属性指CreateFile
的dwFlagsAndAttributes指定的FILE_ATTRIBUTES_*值。已废弃用GetFileInformationByHandle
/SetFileInformationByHandle
。
1 2 3 4 5 6 7
| DWORD WINAPI GetFileAttrbutes( _In_ LPCTSTR lpFileName ) BOOL WINAPI SetFileAttributes( _In_ LPCTSTR lpFileName, _In_ DWORD dwFileAttributes )
|
GetFileAttributesEx
比GetFileAttributes
更全面,已废弃用GetFileInformationByHandle
。
1 2 3 4 5
| BOOL WINAPI GetFileAttributesEx( _In_ LPCTSTR lpFileName, _In_ GET_FILEEX_INFO_LEVELS fInfoLevelId, _Out_ LPVOID lpFileInformation )
|
其中GET_FILEEX_INFO_LEVELS结构如下,只能为GetFileExInfoStandard,且lpFileInformation为WIN32_FILE_ATTRIBUTE_DATA结构指针。
1 2 3 4
| typedef enum _GET_FILEEX_INFO_LEVELS { GetFileExInfoStandard, GetFileExMaxInfoLevel } GET_FILEEX_INFO_LEVELS;
|
WIN32_FILE_ATTRIBUTES_DATA结构为:
1 2 3 4 5 6 7 8
| typedef struct _WIN32_FILE_ATTRIBUTE_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; } WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
|
通过文件句柄后去文件属性信息:
1 2 3 4
| BOOL WINAPI GetFileInformationByHandle( _In_ HANDLE hFile, _Out_ LPBY_HANDLE_FILE_INFORMATION lpFileInformation )
|
BY_HANDLE_FILE_INFORMATION比WIN32_FILE_ATTRIBUTE_DATA多了4个字段:
1 2 3 4 5 6 7 8 9 10 11 12
| typedef struct _BY_HANDLE_FILE_INFORMATION { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD dwVolumeSerialNumber; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD nNumberOfLinks; DWORD nFileIndexHigh; DWORD nFileIndexLow; } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION, *LPBY_HANDLE_FILE_INFORMATION;
|
当为了确定两个打开的文件句柄是否为同一个文件时,要同时比较卷序列号和文件ID,因为不同逻辑卷上可能有相同ID的文件。
SetFileInformationByHandle
略。
复制文件
CopyFile
将现有文件复制到新文件:
1 2 3 4 5
| BOOL WINAPI CopyFile( _In_ LPCTSTR lpExistingFileName, _In_ LPCTSTR lpNewFileName, _In_ BOOL bFailIfExists )
|
当lpExistingFileName不存在则调用失败,GetLastError
返回ERROR_FILE_NOT_FOUND。当bFailIfExists为TRUE且lpNewFileName已经存在则调用失败,GetLastError
返回ERROR_FILE_EXISTS。当bFailIfExists为FALSE且lpNewFileName已经存在则覆盖存在文件并成功执行,但如果目标文件有FILE_ATTRIBUTE_HIDDEN或FILE_ATTRIBUTE_READONLY属性则调用失败并GetLastError
返回ERROR_ACCESS_DENIED。
已废弃用CopyFileEx
。
CopyFileEx
复制文件。每当一部分复制操作完成时可调用指定的回调函数,在复制期间可以取消正在进行的复制操作,用于复制较大文件。
1 2 3 4 5 6 7 8
| BOOL WINAPI CopyFileEx( _In_ LPCTSTR lpExistingFileName, _In_ LPCTSTR lpNewFileName, _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine, _In_opt_ LPVOID lpData, _In_opt_ LPBOOL pbCancel, _In_ DWORD dwCopyFlags )
|
在复制操作过程中如果将pbCancel指向的变量设置为TRUE,则复制操作取消并返回FALSE,GetLastError
返回ERROR_REQUEST_ABORTED,并删除目标文件。
dwCopyFlags可以是以下组合:
枚举值 |
含义 |
COPY_FILE_FAIL_IF_EXISTS |
见下。 |
COPY_FILE_ALLOW_DECRYPTED_DESTINATION |
复制加密文件时,尝试用源文件密钥对目标文件加密,无法完成则用默认密钥对目标文件加密,都不能完成调用失败且GetLastError 返回ERROR_ENCRYPTION_FAILED。即使无法加密也依然完成复制操作。 |
COPY_FILE_NO_BUFFERING |
不使用系统I/O缓存,用于传输非常大的文件。 |
当lpExistingFileName不存在则调用失败,GetLastError
返回ERROR_FILE_NOT_FOUND。当dwCopyFlags指定了COPY_FILE_FAIL_IF_EXISTS且lpNewFileName已经存在则调用失败,GetLastError
返回ERROR_FILE_EXISTS。当dwCopyFlags未指定COPY_FILE_FAIL_IF_EXISTS且lpNewFileName已经存在则覆盖存在文件并成功执行,但如果目标文件有FILE_ATTRIBUTE_HIDDEN或FILE_ATTRIBUTE_READONLY属性则调用失败并GetLastError
返回ERROR_ACCESS_DENIED。
回调函数格式:
1 2 3 4 5 6 7 8 9 10 11
| DWORD CALLBACK CopyProgressRoutine( _In_ LARGE_INTEGER TotalFileSize, _In_ LARGE_INTEGER TotalBytesTransferred, _In_ LARGE_INTEGER StreamSize, _In_ LARGE_INTEGER StreamBytesTransferred, _In_ DWORD dwStreamNumber, _In_ DWORD dwCallbackReason, _In_ HANDLE hSourceFile, _In_ HANDLE hDestinationFile, _In_opt_ LPVOID lpData )
|
当文件流已建立并即将开始复制,即首次调用回调函数时dwCallbackReason为CALLBACK_STREAM_SWITCH。完成一部分时dwCallbackReason为CALLBACK_CHUNK_FINISHED,一部分一般是1MB,也可能是64KB。
对于回调函数的返回值,一般返回PROGRESS_CONTINUE表示继续。返回PROGRESS_CANCEL或PROGRESS_STOP时取消复制。返回PROGRESS_QUIET时在以后的复制操作过程中停止调用回调函数。
PathFileExists
判断目录或文件是否存在:
1 2 3
| BOOL PathFileExists( _In_ LPCTSTR pszPath )
|
例子
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
| #include <windows.h> #include <Shlwapi.h> #include "resource.h" #pragma comment(lib, "Shlwapi.lib")
HWND g_hwndDlg; BOOL g_bCancel = FALSE;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI ThreadProc(LPVOID lpParameter);
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred,LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber,DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE hThread = NULL; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; SetDlgItemText(hwndDlg, IDC_EDIT_SOURCE, TEXT("F:\\Test.rar")); SetDlgItemText(hwndDlg, IDC_EDIT_TARGET, TEXT("F:\\Downloads\\Test.rar")); SendMessage(GetDlgItem(hwndDlg, IDC_EDIT_PROCESS), EM_SETLIMITTEXT, 0, 0); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_COPY: { if ((hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL)) != NULL) CloseHandle(hThread); break; }; case IDC_BTN_CANCEL: { g_bCancel = TRUE; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { TCHAR szSource[MAX_PATH] = { 0 }; TCHAR szTarget[MAX_PATH] = { 0 }; BOOL bRet = FALSE; GetDlgItemText(g_hwndDlg, IDC_EDIT_SOURCE, szSource, _countof(szSource)); GetDlgItemText(g_hwndDlg, IDC_EDIT_TARGET, szTarget, _countof(szTarget)); if (!PathFileExists(szSource)) { MessageBox(g_hwndDlg, TEXT("指定的源文件不存在!"), TEXT("提示"), MB_OK); return 0; }; if (PathFileExists(szTarget)) { INT nRet = MessageBox(NULL, TEXT("指定的新文件已经存在,是否覆盖目标文件"), TEXT("提示"), MB_OKCANCEL | MB_ICONINFORMATION | MB_DEFBUTTON2); switch (nRet) { case IDOK: { break; }; case IDCANCEL: { return 0; }; }; }; bRet = CopyFileEx(szSource, szTarget, CopyProgressRoutine, NULL, &g_bCancel, 0); if (!bRet) { if (GetLastError() == ERROR_REQUEST_ABORTED) MessageBox(g_hwndDlg, TEXT("用户取消了复制操作,线程函数返回"), TEXT("已取消"), MB_OK); } else MessageBox(g_hwndDlg, TEXT("已经成功复制了文件,线程函数返回"), TEXT("已成功"), MB_OK); g_bCancel = FALSE; return 0; }; DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) { HWND hwndEdit = GetDlgItem(g_hwndDlg, IDC_EDIT_PROCESS); TCHAR szBuf[256] = { 0 }; wsprintf(szBuf, TEXT("文件总大小:%I64X\t已传输:%I64X\t文件流总大小:%I64X\t已传输:%I64X\t流编号:%d\t\n"), TotalFileSize.QuadPart, TotalBytesTransferred.QuadPart, StreamSize.QuadPart, StreamBytesTransferred.QuadPart, dwStreamNumber); SendMessage(hwndEdit, EM_SETSEL, -1, -1); SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szBuf); return PROGRESS_CONTINUE; };
|
移动与删除
MoveFile
移动一个文件或目录,已废弃用MoveFileEx
。
1 2 3 4
| BOOL WINAPI MoveFile( _In_ LPCTSTR lpExistingFileName, _In_ LPCTSTR lpNewFileName )
|
源文件/目录不存在则调用失败,GetLastError
返回ERROR_FILE_NOT_FOUND。目标文件/目录已存在则调用失败,文件时GetLastError
返回ERROR_FILE_EXISTS ,目录时返回ERROR_ALREADY_EXISTS。目标目录/文件上一层目录不存在时调用失败,GetLastError
返回ERROR_PATH_NOT_FOUND。移动目录时必须在同一个逻辑驱动器中,否则调用失败,GetLastError
返回ERROR_ACCESS_DENIED。
MoveFileEx
移动一个文件或目录:
1 2 3 4 5
| BOOL WINAPI MoveFile( _In_ LPCTSTR lpExistingFileName, _In_ LPCTSTR lpNewFileName _In_ DWORD dwFlags )
|
源文件/目录不存在则调用失败,GetLastError
返回ERROR_FILE_NOT_FOUND。目标文件/目录已存在则调用失败,文件时GetLastError
返回ERROR_FILE_EXISTS ,目录时返回ERROR_ALREADY_EXISTS。目标目录/文件上一层目录不存在时调用失败,GetLastError
返回ERROR_PATH_NOT_FOUND。默认情况下移动目录/文件时必须在同一个逻辑驱动器中,否则调用失败,GetLastError
返回ERROR_NOT_SAME_DEVICE。
dwFlags选项:
枚举值 |
含义 |
MOVEFILE_REPLACE_EXISTING |
目标文件存在时覆盖。 |
MOVEFILE_COPY_ALLOWED |
允许把一个文件移动到其他逻辑驱动器中。 |
MOVEFILE_DELAY_UNTIL_REBOOT |
下次重启系统后移动文件/目录,不能与MOVEFILE_COPY_ALLOWED同时使用。 |
对于MOVEFILE_DELAY_UNTIL_REBOOT选项,MoveFileEx
向注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\PendingFileRenameOperations中添加lpExistingFileName和lpNewFileName,并加上内核“/??/”前缀。当lpNewFileName指定为NULL时重启系统能够后删除lpExistingFileName。
MoveFileWithProgress
功能与MoveFileEx
相同,指定一个接收进度通知的回调函数,具体同上且略。
1 2 3 4 5 6 7
| BOOL WINAPI MoveFileWithProgress( _In_ LPCTSTR lpExistingFileName, _In_opt_ LPCTSTR lpNewFileName, _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine, _In_opt_ LPVOID lpData, _In_ DWORD dwFlags )
|
DeleteFile
删除一个文件:
1 2 3
| BOOL WINAPI DeleteFile( _In_ LPCTSTR lpFileName )
|
删除只读文件时需要用SetFileAttributes
删除FILE_ATTRIBUTE_READONLY属性,否则调用失败且GetLastError
返回ERROR_ACCESS_DENIED。删除一个处于打开状态的文件时需要先用CloseHandle
关闭文件,否则调用失败且GetLastError
返回ERROR_SHARING_VIOLATION,但如果CreateFile
时指定FILE_SHARE_DELETE则无所谓。
无缓冲I/O
使用FILE_FLAG_NO_BUFFERING标志时,读写文件的偏移量必须为扇区大小整数倍,读写的字节数必须是扇区大小的整数倍,缓冲区地址必须是物理扇区大小的整数倍。
机械硬盘物理扇区大小为512字节,固态硬盘为4KB,所以引入逻辑扇区大小的概念。逻辑扇区大小是逻辑寻址单位,物理扇区大小是硬盘原子写入单位。即临时解决兼容性的方案是固态硬盘逻辑扇区为512字节。
一般页面大小大于扇区大小,所以页面对齐的内存也是扇区对齐的。为保证读写文件的缓冲区地址一定是物理扇区大小整数倍,用VirtualAlloc
分配缓冲区,该函数保证内存区域起始地址是分配粒度64KB整数倍。
可用GetDiskFreeSpace
获取逻辑扇区大小。也可用DeviceIoControl
发送IOCTL_DISK_GET_DRIVE_GEOMETRY_EX控制代码获取逻辑扇区大小,发送IOCTL_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
| #include <Windows.h> #include <strsafe.h> INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { HANDLE hDevice = INVALID_HANDLE_VALUE; DISK_GEOMETRY_EX diskGeometryEx = { 0 }; STORAGE_PROPERTY_QUERY storagePropertyQuery; storagePropertyQuery.PropertyId = StorageAccessAlignmentProperty; storagePropertyQuery.QueryType = PropertyStandardQuery; STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR saad = { sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) }; DWORD dwBytesReturned; TCHAR szBuf[512] = { 0 }; TCHAR szTemp[256] = { 0 }; hDevice = CreateFile(TEXT("\\\\.\\PhysicalDrive0"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice != INVALID_HANDLE_VALUE) { DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &diskGeometryEx, sizeof(diskGeometryEx), &dwBytesReturned, NULL); DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(storagePropertyQuery), &saad, sizeof(saad), &dwBytesReturned, NULL); StringCchPrintf(szBuf, _countof(szBuf), TEXT("驱动器0:\r\n逻辑扇区大小:%d\r\n物理扇区大小:%d\r\n\r\n"), diskGeometryEx.Geometry.BytesPerSector, saad.BytesPerPhysicalSector); CloseHandle(hDevice); }; hDevice = INVALID_HANDLE_VALUE; diskGeometryEx = { 0 }; saad = { sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) }; hDevice = CreateFile(TEXT("\\\\.\\PhysicalDrive1"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice != INVALID_HANDLE_VALUE) { DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &diskGeometryEx, sizeof(diskGeometryEx), &dwBytesReturned, NULL); DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(storagePropertyQuery), &saad, sizeof(saad), &dwBytesReturned, NULL); StringCchPrintf(szTemp, _countof(szTemp), TEXT("驱动器1:\r\n逻辑扇区大小:%d\r\n物理扇区大小:%d\r\n\r\n"), diskGeometryEx.Geometry.BytesPerSector, saad.BytesPerPhysicalSector); StringCchCat(szBuf, _countof(szBuf), szTemp); CloseHandle(hDevice); }; MessageBox(NULL, szBuf, TEXT("驱动器扇区大小"), MB_OK); return 0; };
|
其中各结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| typedef struct _DISK_GEOMETRY_EX { DISK_GEOMETRY Geometry; LARGE_INTEGER DiskSize; BYTE Data[1]; } DISK_GEOMETRY_EX, * PDISK_GEOMETRY_EX; typedef struct __WRAPPED__ _DISK_GEOMETRY { LARGE_INTEGER Cylinders; MEDIA_TYPE MediaType; DWORD TracksPerCylinder; DWORD SectorsPerTrack; DWORD BytesPerSector; } DISK_GEOMETRY, * PDISK_GEOMETRY; typedef struct _STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR { DWORD Version; DWORD Size; DWORD BytesPerCacheLine; DWORD BytesOffsetForCacheAlignment; DWORD BytesPerLogicalSector; DWORD BytesPerPhysicalSector; DWORD BytesOffsetForSectorAlignment; } STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR, * PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
|
逻辑驱动器和目录
SetVolumeLabel
为一个卷(逻辑驱动器)设置卷标:
1 2 3 4
| BOOL WINAPI SetVolumeLabel( _In_opt_ LPCTSTR lpRootPathName, _In_opt_ LPCTSTR lpVolumeName )
|
例子:
1
| SetVolumeLabel(TEXT("C:\\"), TEXT("系统"));
|
获取一个逻辑驱动器详细信息:
1 2 3 4 5 6 7 8 9 10
| BOOL WINAPI GetVolumeInformation( _In_opt_ LPCTSTR lpRootPathName, _Out_opt_ LPTSTR lpVolumeNameBuffer, _In_ DWORD nVolumeNameSize, _Out_opt_ LPDWORD lpVolumeSerialNumber, _Out_opt_ LPDWORD lpMaximumComponentLength, _Out_opt_ LPDWORD lpFileSystemFlags, _Out_opt_ LPTSTR lpFileSystemNameBuffer, _In_ DWORD nFileSystemNameSize )
|
卷序列号是格式化硬盘时系统分配的一个序号,每次格式化后都可能变。不变的是硬盘制造商分配的硬盘序列号。
文件组名长度即完整路径文件名中以反斜杠分割的每一部分。
lpFileSystemFlags常用标志:
枚举值 |
含义 |
FILE_CASE_SENSITIVE_SEARCH |
支持区分大小写 |
FILE_CASE_PRESERVED_NAMES |
保存文件/目录时保留大小写 |
FILE_UNICODE_ON_DISK |
文件名支持Unicode |
FILE_FILE_COMPRESSION |
支持文件压缩 |
FILE_SUPPORTS_ENCRYPTION |
支持文件加密 |
FILE_READ_ONLY_VOLUME |
只读 |
FILE_VOLUME_IS_COMPRESSED |
压缩卷 |
lpFileSystemNameBuffer可能返回FAT32、NTFS、ReFS等。
GetLogicalDrives
获取系统中所有可用的逻辑驱动器:
1
| DWORD WINAPI GetLogicalDrives(VOID);
|
位0为驱动器A,位1位驱动器B,以此类推。
GetLogicalDriveStrings
返回字符串类型逻辑驱动器列表:
1 2 3 4
| DWORD WINAPI GetLogicalDriveStrings( _In_ DWORD nBufferLength, _Out_ LPTSTR lpBuffer )
|
一般第一次调用时nBufferLength位0,lpBuffer为NULL,返回所需缓冲区大小,包括终止空字符,分配合适大小后第二次调用。
GetDriveType
确定逻辑驱动器类型:
1 2 3
| UINT WINAPI GetDriveType( _In_opt_ LPCTSTR lpRootPathName )
|
返回值:
枚举值 |
含义 |
DRIVE_UNKNOW |
无法确定 |
DRIVE_NO_ROOT_DIR |
lpRootPathName无效 |
DRIVE_REMOVABLE |
可移动介质如U盘 |
DRIVE_FIXED |
固定介质如固态/可移动硬盘 |
DRIVE_REMOTE |
远程(网络)驱动器 |
DRIVE_CDROM |
CD-ROM光盘驱动器 |
DRIVE_RAMDISK |
RAM磁盘 |
GetDiskFreeSpace
获取一个逻辑驱动器总容量或空闲磁盘空间,已废弃用GetDiskFreeSpaceEx
。
1 2 3 4 5 6 7 8 9
| BOOL WINAPI GetDiskFreeSpace( _In_ LPCTSTR lpRootPathName, _Out_ LPDWORD lpSectorsPerCluster, _Out_ LPDWORD lpBytesPerSector, _Out_ LPDWORD lpNumberOfFreeClusters, _Out_ LPDWORD lpTotalNumberOfClusters )
|
GetDiskFreeSpaceEx
解决GetDiskFreeSpace
计算麻烦的问题:
1 2 3 4 5 6
| BOOL WINAPI GetDiskFreeSpaceEx( _In_opt_ LPCTSTR lpDirectoryName, _Out_opt_ PULARGE_INTEGER lpFreeBytesAvailable, _Out_opt_ PULARGE_INTEGER lpTotalNumberOfBytes, _Out_opt_ PULARGE_INTEGER lpTotalNumberOfFreeBytes )
|
目录操作
CreateDirectory
创建一个目录:
1 2 3 4
| BOOL WINAPI CreateDirectory( _In_ LPCTSTR lpPathName, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes )
|
指定的目录已存在则调用失败,GetLastError
返回ERROR_ALREADY_EXISTS。当将被创建的目录名与该目录下已有文件名重名时调用失败,GetLastError
返回ERROR_ALREADY_EXISTS。上层目录中有不存在的调用失败,GetLastError
返回ERROR_PATH_NOT_FOUND。
RemoveDirectory
删除一个目录:
1 2 3
| BOOL WINAPI RemoveDirectory( _In_ LPCTSTR lpPathName )
|
删除的不是空目录时调用失败,GetLastError
返回ERROR_DIR_NOT_EMPTY。注意这是直接删除而不是移动到回收站。
例子
创建和删除目录,当上级目录不存在时自动创建,当删除的目录不为空时递归删除:
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
| BOOL MyCreateDirectory(LPTSTR lpPathName) { TCHAR szBuf[MAX_PATH] = { 0 }; LPTSTR lp; if (PathFileExists(lpPathName)) return TRUE; if (lpPathName[_tcslen(lpPathName) - 1] == TEXT('\\')) lpPathName[_tcslen(lpPathName) - 1] = TEXT('\0'); lp = _tcsrchr(lpPathName, TEXT('\\')); for (INT i = 0; i < lp - lpPathName; i++) szBuf[i] = *(lpPathName + i); MyCreateDirectory(szBuf); if (CreateDirectory(lpPathName, NULL)) return TRUE; return FALSE; };
BOOL MyRemoveDirectory(LPTSTR lpPathName) { TCHAR szDirectory[MAX_PATH] = { 0 }; TCHAR szSearch[MAX_PATH] = { 0 }; TCHAR szDirFile[MAX_PATH] = { 0 }; HANDLE hFindFile; WIN32_FIND_DATA fd = { 0 }; StringCchCopy(szDirectory, _countof(szDirectory), lpPathName); if (szDirectory[_tcslen(szDirectory) - 1] != TEXT('\\')) StringCchCat(szDirectory, _countof(szDirectory), TEXT("\\")); StringCchCopy(szSearch, _countof(szSearch), szDirectory); StringCchCat(szSearch, _countof(szSearch), TEXT("*.*")); hFindFile = FindFirstFile(szSearch, &fd); if (hFindFile != INVALID_HANDLE_VALUE) { do { if (_tcscmp(fd.cFileName, TEXT(".")) == 0 || _tcscmp(fd.cFileName, TEXT("..")) == 0) continue; StringCchCopy(szDirFile, _countof(szDirFile), szDirectory); StringCchCat(szDirFile, _countof(szDirFile), fd.cFileName); if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) MyRemoveDirectory(szDirFile); else { if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) SetFileAttributes(szDirFile, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); DeleteFile(szDirFile); }; } while (FindNextFile(hFindFile, &fd)); FindClose(hFindFile); }; if (RemoveDirectory(lpPathName)) return TRUE; return FALSE; };
|
GetWindowsDirectory
获取Windows操作系统安装目录,通常是C:\Windows,末尾没有“\”,已废弃用SHGetKnownFolderPath
。
1 2 3 4
| UINT GetWindowsDirectory( _Out_ LPTSTR lpBuffer, _In_ UINT uSize )
|
GetSystemDirectory
获取系统目录,通常是C:\Windows\system32,末尾没有“\”,已废弃用SHGetKnownFolderPath
。
1 2 3 4
| UINT GetSystemDirectory( _Out_ LPTSTR lpBuffer, _In_ UINT uSize )
|
GetTempPath
获取临时目录,临时目录在磁盘空间不足时自动删除,通常C:\Users\用户名\AppData\Local\Temp\,末尾有“\”,已废弃用SHGetKnownFolderPath
。
1 2 3 4
| UINT GetTempPath( _In_ UINT uSize, _Out_ LPTSTR lpBuffer )
|
该函数按照这个顺序获取临时目录:TMP环境变量、TEMP环境变量、USERPROFILE环境变量、Windows目录。
GetUserName
获取系统当前登录用户名:
1 2 3 4
| BOOL WINAPI GetUserName( _Out_ LPTSTR lpBuffer, _Inout_ LPDWORD lpnSize )
|
UNLEN在Lmcons.h中定义。
SHGetKnownFolderPath
获取默写目录完整路径。
1 2 3 4 5 6 7
| #include <Shlobj.h> HRESULT SHGetKnownFolderPath( _In_ REFKNOWNFOLDERID rfid, _In_ DWORD dwFlags, _In_opt_ HANDLE hToken, _Out_ PWSTR *ppszPath )
|
GUID指全局唯一标识符,是一个128位/16字节的二进制数,格式XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,它的生成使用网卡MAC、纳秒级时间、芯片ID码等,保证每次生成的GUID永远不会重复,即一个GUID在同一时空所有机器上都是唯一的。
其中REFKNOWNFOLDERID结构如下:
1 2 3 4 5 6 7 8
| #define REFKNOWNFOLDERID const KNOWNFOLDERID & typedef GUID FOLDERTYPEID; typedef struct _GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[ 8 ]; } GUID;
|
常用目录对应的GUID:
GUID |
含义 |
F38BF404-1D43-42F2-9305-67DE0B28FC23 |
Windows目录,如C:\Windows |
1AC14E77-20E7-4E5D-B744-2EB1AE5198B7 |
系统目录,如C:\Windows\system32 |
0762D272-C50A-4BB0-A382-697DCD729B80 |
用户目录,如C:\Users |
5E6C858F-0E22-4760-9AFE-EA3317B67173 |
当前用户目录,如C:\Users\用户名 |
B4BFCC3A-DB2C-424C-B029-7FE99A87C641 |
用户桌面文件,如C:\Users\用户名\Desktop |
FDD39AD0-238F-46AF-ADB4-6C85480369C7 |
我的文档,如C:\Users\用户名\Documents |
905E63B6-C1BF-494E-B29C-65B732D3D21A |
应用程序安装目录,如C:\Program Files(x86)和C:\Program Files |
62AB5D82-FDC1-4DC3-A9DD-070D1D495D97 |
所有用户应用程序数据目录,如C:\ProgramData |
F1B32785-6FBA-4 |
当前用户应用程序数据目录(配置/临时文件),如C:\Users\用户名\AppData\Local |
3EB685DB-65F9-4CF6-A03A-E3EF65729F3D |
当前用户应用程序数据目录(漫游用户配置文件),如C:\Users\用户名\AppData\Roaming |
8983036C-27C0-404B-8F08-102D10DCFD74 |
鼠标右键发送到目录,如C:\Users\用户名\AppData\Roaming\Microsoft\Windows\SendTo |
82A5EA35-D9CD-47C5-9629-E15D2F714E6E |
所有用户开机自动启动程序目录,如C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup |
B97D20BB-F46A-4C97-BA10-5E3608430854 |
当前用户开机自动启动程序目录,如C:\Users\用户名\AppData\Roadming\Microsoft\Windows\Start Menu\Porgrams\Startup |
374DE290-123F-4565-9164-39C4925E467B |
用户下载文件夹,如C:\Users\ 用户名\Downloads |
FD228CB7-AE11-4AE3-864C-16F3910AB8FE |
字体文件夹,如C:\Windows\Fonts |
例如获取我的文档目录完整路径:
1 2 3 4 5 6 7 8 9 10 11
| #include <Rpcdce.h> #pragma comment(lib,"Rpcrt4.lib") GUID guid; PWSTR lpPath; HRESULT hResult; UuidFromString((RPC_WSTR)TEXT("FDD39AD0-238F-46AF-ADB4-6C85480369C7"), &guid); hResult = SHGetKnownFolderPath(guid, 0, NULL, &lpPath); if (SUCCEEDED(hResult)) { MessageBox(hwndDlg, lpPath, TEXT("文档目录完整路径"), MB_OK); CoTaskMemFree(lpPath); };
|
用CoTaskMemFree
释放lpPath:
1 2 3
| VOID CoTaskMemFree( _In_opt_ LPVOID pv )
|
环境变量
GetEnvironmentStrings
获取指向调用进程的环境块的指针。默认情况下,子进程继承父进程环境变量,由命令行启动的程序继承cmd进程的环境变量。
1
| LPTSTR WINAPI GetEnvironmentStrings(VOID)
|
环境快中环境变量组个数如下:
1 2 3 4
| Var1=Value1\0 Var2=Value2\0 Var3=Value3\0 ...
|
例子:
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
| INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; LPTSTR lpEnvironmentStrings; switch (uMsg) { case WM_INITDIALOG: { hwndEdit = GetDlgItem(hwndDlg, IDC_EDIT_ENV); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_LOOK: { SetWindowText(hwndEdit, TEXT("")); lpEnvironmentStrings = GetEnvironmentStrings(); while (lpEnvironmentStrings[0] != TEXT('\0')) { SendMessage(hwndEdit, EM_SETSEL, -1, -1); SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)lpEnvironmentStrings); SendMessage(hwndEdit, EM_SETSEL, -1, -1); SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)TEXT("\n")); lpEnvironmentStrings += _tcslen(lpEnvironmentStrings) + 1; }; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
GetEnvironmentVariable
获取当前进程指定环境变量的值:
1 2 3 4 5
| DWORD WINAPI GetEnvironmentVariable( _In_opt_ LPCTSTR lpName, _Out_opt_ LPTSTR lpBuffer, _In_ DWORD nSize )
|
可用两次调用法。
SetEnvironmentVariable
设置当前进程中指定环境变量的值:
1 2 3 4
| BOOL WINAPI SetEnvironmentVariable( _In_ LPCTSTR lpName, _In_opt_ LPCTSTR lpValue )
|
当lpName指定的不存在且lpValue不为NULL,则创建该环境变量。当lpName指定一个环境变量且lpValue为NULL,则删除该环境变量。注意这个函数只更改该进程环境变量,其他进程的不会改变,系统环境变量也不会改变。
系统环境变量与注册表HEKY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment下一一对应,修改系统环境变量可操作注册表,然后广播WM_SETTINGCHANGE消息(lParam为字符串“Environment”),其他应用程序收到该系统信息更改通知。
FreeEnvironmentStrings
释放环境块;
1 2 3
| BOOL WINAPI FreeEnvironmentStrings( _In_ LPTSTR lpszEnvironmentBlock )
|
ExpandEnvironmentStrings
展开环境变量字符串,如“%USERPROFILE%”等:
1 2 3 4 5
| DWORD WINAPI ExpandEnvironmentStrings( _In_ LPCTSTR lpSrc, _Out_opt_ LPTSTR lpDst, _In_ DWORD nSize )
|
例如:
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
| INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; LPCTSTR lpSrc[] = { TEXT("SystemDrive\t= %SystemDrive%"),TEXT("windir\t\t= %windir%"),TEXT("TEMP\t\t= %TEMP%"),TEXT("ProgramFiles\t= %ProgramFiles%"),TEXT("USERNAME\t= %USERNAME%"),TEXT("USERPROFILE\t= %USERPROFILE%"),TEXT("ALLUSERSPROFILE\t= %ALLUSERSPROFILE%"),TEXT("APPDATA\t\t= %APPDATA%"),TEXT("LOCALAPPDATA\t= %LOCALAPPDATA%") }; TCHAR szDst[BUFFER_SIZE] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { hwndEdit = GetDlgItem(hwndDlg, IDC_EDIT_ENV); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_LOOK: { for (INT i = 0; i < _countof(lpSrc); i++) { ExpandEnvironmentStrings(lpSrc[i], szDst, BUFFER_SIZE); SendMessage(hwndEdit, EM_SETSEL, -1, -1); SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szDst); SendMessage(hwndEdit, EM_SETSEL, -1, -1); SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)TEXT("\n")); }; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
SHFileOperation
这函数还蛮重要的,单独拎出来一章记。
用于复制、移动、重命名或删除一个文件或目录。
1 2 3
| INT SHFileOperation( _Inout_ LPSHFILEOPSTRUCT lpFileOp )
|
其中SHFILEOPSTRUCT结构包含操作类型和所需信息:
1 2 3 4 5 6 7 8 9 10
| typedef struct _SHFILEOPSTRUCT{ HWND hwnd; UINT wFunc; PCZZSTR pFrom; PCZZSTR pTo; FILEOP_FLAGS fFlags; BOOL fAnyOperationsAborted; LPVOID hNameMappings; PCTSTR lpszProgressTitle; } SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
|
wFunc可以是:FO_COPY复制、FO_MOVE移动、FO_RENAME重命名、FO_DELETE删除。
pFrom和pTo都可以同时指定若干个完整路径名,但每个完整路径名后必须手动添加终止空字符,pTo在复制和移动时不存在时自动创建。
fFlags有些常用设置:
枚举值 |
含义 |
FOF_RENAMEONCOLLISION |
目标未知存在同名文件/目录时自动指定新文件/目录名 |
FOF_ALLOWUNDO |
删除文件/目录时搁回收站 |
FOF_MULTIDESTFILES |
指定多个源/目标文件/目录 |
FOF_NOCONFIRMMKDIR |
需要创建新目录时不弹出对话框提示是否创建 |
FOF_SIMPLEPROGRESS |
显示一个操作进度对话框,标题为lpszProgressTitle |
FOF_SILENT |
不显示操作进度对话框 |
FOF_NOERRORUI |
发生错误时不弹出对话框 |
FOF_NOCONFIRMATION |
弹出所有对话框全部答复确定 |
FOF_NO_UI |
不显示任何对话框 |
fAnyOperationsAborted为返回值,当操作在完成前被用户终止则为TRUE,否则FALSE。
监视目录变化
ReadDirectoryChangesW
监视一个目录中文件/目录变化信息:
1 2 3 4 5 6 7 8 9 10
| BOOL WINAPI ReadDirectoryChagesW( _In_ HANDLE hDirectory, _Out_ LPVOID lpBuffer, _In_ DWORD nBufferLength, _In_ BOOL bWatchSubtree, _In_ DWORD dwNotifyFilter, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped, _In_opt_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
|
对于hDirectory需要获取该目录FILE_LIST_DIRECTORY访问权限。用CreateFile
指定FILE_FLAG_BACKUP_SEMANTICS标志来获取一个目录句柄。
lpBuffer返回后被解析为FILE_NOTIFY_INFORMATION结构。
dwNotifyFilter由常用监视类型,对于同步操作,发生指定事件时函数才返回。
枚举值 |
含义 |
FILE_NOTIFY_CHANGE_FILE_NAME |
文件名 |
FILE_NOTIFY_CHANGE_DIR_NAME |
目录名 |
FILE_NOTIFY_CHANGE_ATTRIBTUES |
属性 |
FILE_NOTIFY_CHANGE_SIZE |
大小 |
FILE_NOTIFY_CHANGE_LAST_WRITE |
最后写入时间 |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
最后访问时间 |
FILE_NOTIFY_CHANGE_CREATION |
创建时间 |
FILE_NOTIFY_CHANGE_SECURITY |
安全描述符 |
同步操作时可将lpBuffer信息解析为FILE_NOTIFY_INFORMATION结构:
1 2 3 4 5 6
| typedef struct _FILE_NOTIFY_INFORMATION { DWORD NextEntryOffset; DWORD Action; DWORD FileNameLength; WCHAR FileName[1]; } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
|
对于Action字段表示发生变化的类型:
枚举值 |
含义 |
FILE_ACTION_ADDED |
新建 |
FILE_ACTION_REMOVED |
删除 |
FILE_ACTION_MODIFIED |
时间戳、大小、属性等修改或创建 |
FILE_ACTION_RENAMED_OLD_NAME |
|
FILE_ACTION_RENAMED_NEW_NAME |
|
对于FILE_ACTION_RENAMED_OLD_NAME,第一个FILE_NOTIFY_INFORMATION的FileName为旧名称。NextEntryOffset指向的结构的FileName为新名称,Action为FILE_ACTION_RENAMED_NEW_NAME。
例子
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 117 118 119 120
| #include <windows.h> #include <shlwapi.h> #include "resource.h" #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #pragma comment(lib, "Shlwapi.lib")
#define WM_DIRECTORYCHANGES (WM_APP + 1)
HWND g_hwndDlg; BOOL g_bStarting; TCHAR g_szShowChanges[1024];
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); DWORD WINAPI ThreadProc(LPVOID lpParameter); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndEditChanges; HANDLE hThread = NULL; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; hwndEditChanges = GetDlgItem(hwndDlg, IDC_EDIT_CHANGES); SetDlgItemText(hwndDlg, IDC_EDIT_PATH, TEXT("C:\\")); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_START: { g_bStarting = TRUE; hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); if (hThread) CloseHandle(hThread); break; }; case IDC_BTN_STOP: { g_bStarting = FALSE; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_DIRECTORYCHANGES: { SendMessage(hwndEditChanges, EM_SETSEL, -1, -1); SendMessage(hwndEditChanges, EM_REPLACESEL, TRUE, (LPARAM)g_szShowChanges); return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { TCHAR szPath[MAX_PATH] = { 0 }; HANDLE hDirectory = INVALID_HANDLE_VALUE; TCHAR szBuffer[1024] = { 0 }; DWORD dwBytesReturned; PFILE_NOTIFY_INFORMATION pFNI, pFNINext; TCHAR szFileName[MAX_PATH], szFileNameNew[MAX_PATH]; SetDlgItemText(g_hwndDlg, IDC_EDIT_CHANGES, TEXT("")); GetDlgItemText(g_hwndDlg, IDC_EDIT_PATH, szPath, _countof(szPath)); hDirectory = CreateFile(szPath, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hDirectory == INVALID_HANDLE_VALUE) { MessageBox(g_hwndDlg, TEXT("CreateFile函数调用失败"), TEXT("Error"), MB_OK); return 0; }; while (g_bStarting) { if (!PathFileExists(szPath)) { MessageBox(g_hwndDlg, TEXT("监视目录文件夹已被删除"), TEXT("Error"), MB_OK); return 0; }; ZeroMemory(szBuffer, sizeof(szBuffer)); ReadDirectoryChangesW(hDirectory, szBuffer, sizeof(szBuffer), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, &dwBytesReturned, NULL, NULL); pFNI = (PFILE_NOTIFY_INFORMATION)szBuffer; ZeroMemory(szFileName, sizeof(szFileName)); ZeroMemory(szFileNameNew, sizeof(szFileNameNew)); memcpy_s(szFileName, sizeof(szFileName), pFNI->FileName, pFNI->FileNameLength); if (pFNI->NextEntryOffset) { pFNINext = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pFNI + pFNI->NextEntryOffset); memcpy_s(szFileNameNew, sizeof(szFileNameNew), pFNINext->FileName, pFNINext->FileNameLength); }; ZeroMemory(g_szShowChanges, sizeof(g_szShowChanges)); switch (pFNI->Action) { case FILE_ACTION_ADDED: { wsprintf(g_szShowChanges, TEXT("新建文件、目录:%s\n"), szFileName); PostMessage(g_hwndDlg, WM_DIRECTORYCHANGES, 0, 0); break; }; case FILE_ACTION_REMOVED: { wsprintf(g_szShowChanges, TEXT("删除文件、目录:%s\n"), szFileName); PostMessage(g_hwndDlg, WM_DIRECTORYCHANGES, 0, 0); break; }; case FILE_ACTION_MODIFIED: { wsprintf(g_szShowChanges, TEXT("修改文件、目录:%s\n"), szFileName); PostMessage(g_hwndDlg, WM_DIRECTORYCHANGES, 0, 0); break; }; case FILE_ACTION_RENAMED_OLD_NAME: { wsprintf(g_szShowChanges, TEXT("文件目录重命名:%s --> %s\n"), szFileName, szFileNameNew); PostMessage(g_hwndDlg, WM_DIRECTORYCHANGES, 0, 0); break; }; }; }; return 0; };
|
获取硬盘序列号
DeviceIoControl
向指定设备驱动程序发送指定控制代码:
1 2 3 4 5 6 7 8 9 10
| BOOL WINAPI DeviceIoControl( _In_ HANDLE hDevice, _In_ DWORD dwIoControlCode, _In_opt_ LPVOID lpInBuffer, _In_opt_ DWORD nInBufferSize, _Out_opt_ LPVOID lpOutBuffer, _Out_opt_ DWORD nOutBufferSize, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped )
|
hDevice用CreateFile
打开句柄,例如逻辑驱动器C为“\\.\C:”,物理驱动器0为“\\.\PhysicalDrive0”。
lpBytesReturned和lpOverlapped不能同时为NULL。
IOCTL_STORAGE_QUERY_PROPERTY
查询存储设备或适配器属性。lpInBuffer指向STORAGE_PROPERTY_QUERY结构:
1 2 3 4 5
| typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; BYTE AdditionalParameters[1]; } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
|
对于PropertyId有:
枚举值 |
含义 |
lpOutBuffer对应结构 |
StorageDeviceProperty |
设备描述符 |
STORAGE_DEVICE_DESCRIPTOR |
StorageAdapterProperty |
适配器描述符 |
STORAGE_ADAPTER_DESCRIPTOR |
StorageDeviceIdProperty |
SCSI重要产品数据页提供的设备ID |
STORAGE_DEVICE_ID_DESCRIPTOR |
StorageDeviceUniqueIdProperty |
设备唯一ID |
STORAGE_DEVICE_UNIQUE_IDENTIFIER |
StorageDeviceWriteCacheProperty |
写缓存属性 |
STORAGE_WRITE_CACHE_PROPERTY |
StorageMiniportProperty |
微型端口驱动程序描述符 |
STORAGE_MINIPORT_DESCRIPTOR |
对于QueryType有:
枚举值 |
含义 |
PropertyStandardQuery |
查询相关描述符 |
PropertyExistsQuery |
驱动程序是否支持指定描述符,lpOutBuffer和nOutBufferSize都为NULL,DeviceIoControl 返回TRUE表示支持 |
其中重点STORAGE_DEVICE_DESCRIPTOR结构如下,注意输出缓冲区中所有字符串都为ASCII格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| typedef _Struct_size_bytes_(Size) struct _STORAGE_DEVICE_DESCRIPTOR { DWORD Version; DWORD Size; BYTE DeviceType; BYTE DeviceTypeModifier; BOOLEAN RemovableMedia; BOOLEAN CommandQueueing; DWORD VendorIdOffset; DWORD ProductIdOffset; DWORD ProductRevisionOffset; DWORD SerialNumberOffset; STORAGE_BUS_TYPE BusType; DWORD RawPropertiesLength; BYTE RawDeviceProperties[1]; } STORAGE_DEVICE_DESCRIPTOR, * PSTORAGE_DEVICE_DESCRIPTOR;
|
其中STORAGE_BUS_TYPE结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| typedef enum _STORAGE_BUS_TYPE { BusTypeUnknown = 0x00, BusTypeScsi, BusTypeAtapi, BusTypeAta, BusType1394, BusTypeSsa, BusTypeFibre, BusTypeUsb, BusTypeRAID, BusTypeiScsi, BusTypeSas, BusTypeSata, BusTypeSd, BusTypeMmc, BusTypeVirtual, BusTypeFileBackedVirtual, BusTypeMax, BusTypeMaxReserved = 0x7F } STORAGE_BUS_TYPE, * PSTORAGE_BUS_TYPE;
|
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
获取一个物理驱动器大小、磁头数、柱面数等。
SMART_GET_VERSION
获取磁盘设备是否支持SMART技术,即自我监控、分析和报告技术,可对硬盘温度、内部电路和盘片表面介质材料等进行检测。SMART信息为512字节,存放在硬盘控制内存中,前2字节为SMART版本信息,之后每12字节为一个SMART属性。
例子
获取物理驱动器ProductIdOffset产品ID、SerialNumberOffset序列号、BusType设备接口类型:
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
| #include <windows.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow){ DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; } INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndEditInfo; TCHAR szDriverName[MAX_PATH] = { 0 }; HANDLE hDriver; STORAGE_PROPERTY_QUERY storagePropertyQuery; CHAR cOutBuffer[1024] = { 0 }; PSTORAGE_DEVICE_DESCRIPTOR pStorageDeviceDesc; DWORD dwBytesReturned; CHAR szBuf[1024] = { 0 }; LPCSTR arrBusType[] = { "未知的总线类型", "SCSI总线类型", "ATAPI总线类型","ATA总线类型", "IEEE 1394总线类型", "SSA总线类型","光纤通道总线类型", "USB", "RAID总线类型","iSCSI总线类型", "串行连接的SCSI总线类型", "SATA","安全数字(SD)总线类型", "多媒体卡(MMC)总线类型", "虚拟总线类型","文件支持的虚拟总线类型" }; switch (uMsg) { case WM_INITDIALOG: { hwndEditInfo = GetDlgItem(hwndDlg, IDC_EDIT_INFO); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_GETINFO: { for (INT i = 0; i < 5; i++) { wsprintf(szDriverName, TEXT("\\\\.\\PhysicalDrive%d"), i); hDriver = CreateFile(szDriverName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if (hDriver == INVALID_HANDLE_VALUE) { wsprintfA(szBuf, "打开物理驱动器%d失败!\r\n\r\n", i); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessageA(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); continue; }; ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY)); storagePropertyQuery.PropertyId = StorageDeviceProperty; storagePropertyQuery.QueryType = PropertyStandardQuery; DeviceIoControl(hDriver, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY), cOutBuffer, sizeof(cOutBuffer), &dwBytesReturned, NULL); pStorageDeviceDesc = (PSTORAGE_DEVICE_DESCRIPTOR)cOutBuffer; wsprintfA(szBuf, "物理驱动器%d\r\n产品 ID:\t%s\r\n序列号:\t%s\r\n接口类型:\t%s\r\n\r\n", i, (LPBYTE)pStorageDeviceDesc + pStorageDeviceDesc->ProductIdOffset, (LPBYTE)pStorageDeviceDesc + pStorageDeviceDesc->SerialNumberOffset, arrBusType[pStorageDeviceDesc->BusType]); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessageA(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); CloseHandle(hDriver); }; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
WMI管理技术
WMI可以访问、配置、管理并监视几乎所有Windows资源,所有WMI接口基于COM组件对象模型,这里不建议用WMI获取信息,因为速度慢。
创建WMI应用程序的步骤:
- 初始化COM,因为WMI基于COM技术,所以必须执行对
CoInitializeEx
和CoInitializeSecurity
函数的调用才能访问WMI。
- 创建到WMI指定命名空间的连接,要创建到WMI指定命名空间的连接,需要首先调用
CoCreateInstance
函数创建与指定CLSID关联的类的对象,该函数在最后一个参数中返回一个IWbemLocator类对象指针,然后可以通过IWbemLocator::ConnectServer
方法连接到WMI指定命名空间,IWbemLocator::ConnectServer
在最后一个参数中返回一个IWbemServices类对象指针(代理)。
- 设置WMI连接的安全级别,获取到IWbemServices代理的指针以后,必须在代理上设置安全性以通过代理访问WMI,如果没有设置适当的安全属性,则COM不允许一个进程访问另一进程,程序可以通过调用
CoSetProxyBlanket
函数来设置IWbemServices代理上的安全级别,在为IWbemServices代理设置安全级别以后,可以使用WMI提供的各种功能。
- 访问WMI,例如可以通过
IWbemServices::ExecQuery
方法执行WQL查询语句,IWbemServices::ExecQuery
在最后一个参数中返回一个IEnumWbemClassObject类对象指针(枚举器),程序可以通过该枚举器访问查询结果。
- 清理工作。
如果需要查询其他设备的信息,更改一下IWbemServices::ExecQuery
方法的WQL查询语句即可,另外IWbemClassObject::Get
方法的第一个参数应该设置为需要获取的字段,例如:
1 2 3 4
| select * from Win32_BaseBoard where SerialNumber is not null #获取主板序列号 select * from Win32_Processor where ProcessorId is not null #获取处理器CPUID select * from Win32_BIOS where SerialNumber is not null #获取BIOS序列号 select * from Win32_NetworkAdapter where MACAddress is not null and not (PNPDeviceID like 'ROOT%') #获取网卡Mac地址
|
其他的自己查:https://docs.microsoft.com/zh-cn/windows/win32/cimwin32prov/computer-system-hardware-classes。
例子:
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
| #include <windows.h> #include <wbemidl.h> #include <Propvarutil.h> #include <comdef.h> #include "resource.h" #pragma comment(lib, "wbemuuid.lib") #pragma comment(lib, "Propsys.lib")
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){ static HWND hwndEditInfo; HRESULT hr; IWbemLocator* pIWbemLocator = NULL; IWbemServices* pIWbemServices = NULL; IEnumWbemClassObject* pIEnumWbemClassObject = NULL; IWbemClassObject* pIWbemClassObject = NULL; ULONG uReturned = 0; VARIANT variant; TCHAR szBuf[256] = { 0 }; switch (uMsg){ case WM_INITDIALOG: { hwndEditInfo = GetDlgItem(hwndDlg, IDC_EDIT_INFO); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_GETINFO: { hr = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hr)) return FALSE; hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hr)) { CoUninitialize(); return FALSE; }; hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pIWbemLocator); if (FAILED(hr)) { CoUninitialize(); return FALSE; }; hr = pIWbemLocator->ConnectServer((BSTR)TEXT("ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pIWbemServices); if (FAILED(hr)) { pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; hr = pIWbemServices->ExecQuery((BSTR)TEXT("WQL"), (BSTR)TEXT("select * from Win32_DiskDrive where SerialNumber is not null"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pIEnumWbemClassObject); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; while (pIEnumWbemClassObject) { hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturned); if (uReturned == 0) break; hr = pIWbemClassObject->Get(TEXT("SerialNumber"), 0, &variant, NULL, NULL); if (SUCCEEDED(hr)) { wsprintf(szBuf, TEXT("硬盘序列号:\t%s\n"), variant.bstrVal); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessage(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); VariantClear(&variant); pIWbemClassObject->Release(); pIWbemClassObject = NULL; }; }; hr = pIWbemServices->ExecQuery((BSTR)TEXT("WQL"), (BSTR)TEXT("select * from Win32_BaseBoard where SerialNumber is not null"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pIEnumWbemClassObject); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturned); if (uReturned == 0) break; hr = pIWbemClassObject->Get(TEXT("SerialNumber"), 0, &variant, NULL, NULL); if (SUCCEEDED(hr)) { wsprintf(szBuf, TEXT("主板序列号:\t%s\n"), variant.bstrVal); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessage(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); VariantClear(&variant); pIWbemClassObject->Release(); pIWbemClassObject = NULL; }; hr = pIWbemServices->ExecQuery((BSTR)TEXT("WQL"), (BSTR)TEXT("select * from Win32_Processor where ProcessorId is not null"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pIEnumWbemClassObject); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturned); if (uReturned == 0) break; hr = pIWbemClassObject->Get(TEXT("ProcessorId"), 0, &variant, NULL, NULL); if (SUCCEEDED(hr)) { wsprintf(szBuf, TEXT("CPUID:\t\t%s\n"), variant.bstrVal); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessage(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); VariantClear(&variant); pIWbemClassObject->Release(); pIWbemClassObject = NULL; }; hr = pIWbemServices->ExecQuery((BSTR)TEXT("WQL"), (BSTR)TEXT("select * from Win32_BIOS where SerialNumber is not null"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pIEnumWbemClassObject); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturned); if (uReturned == 0) break; hr = pIWbemClassObject->Get(TEXT("SerialNumber"), 0, &variant, NULL, NULL); if (SUCCEEDED(hr)) { wsprintf(szBuf, TEXT("BIOS序列号:\t%s\n"), variant.bstrVal); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessage(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); VariantClear(&variant); pIWbemClassObject->Release(); pIWbemClassObject = NULL; }; hr = pIWbemServices->ExecQuery((BSTR)TEXT("WQL"), (BSTR)TEXT("select * from Win32_NetworkAdapter where MACAddress is not null and not (PNPDeviceID like 'ROOT%')"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pIEnumWbemClassObject); if (FAILED(hr)) { pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); return FALSE; }; while (pIEnumWbemClassObject) { hr = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &uReturned); if (uReturned == 0) break; hr = pIWbemClassObject->Get(TEXT("MACAddress"), 0, &variant, NULL, NULL); if (SUCCEEDED(hr)) { wsprintf(szBuf, TEXT("MAC地址:\t%s\n"), variant.bstrVal); SendMessage(hwndEditInfo, EM_SETSEL, -1, -1); SendMessage(hwndEditInfo, EM_REPLACESEL, TRUE, (LPARAM)szBuf); VariantClear(&variant); pIWbemClassObject->Release(); pIWbemClassObject = NULL; }; }; pIEnumWbemClassObject->Release(); pIWbemServices->Release(); pIWbemLocator->Release(); CoUninitialize(); break; }; case IDCANCEL: EndDialog(hwndDlg, 0); break; } return TRUE; }; }; return FALSE; };
|
可移动硬盘和U盘监控
当计算机硬件设备或硬件配置发生变化时系统向窗口过程广播WM_DEVICECHANGE消息。其wParam参数常见值有:
枚举值 |
含义 |
DBT_DEVICEARRAIVAL |
已插入设备或媒体并现在可用 |
DBT_DEVICEQUERYREMOVE |
请求删除设备或媒体,任何应用程序可拒绝该请求并删除操作 |
DBT_EVICEQUERYREMOVEFAILED |
删除设备或媒体的请求已被取消 |
DBT_DEVICEREMOVEPENDING |
设备或介质将被删除,应用程序无法拒绝 |
DBT_DEVICEREMOVECOMPLETE |
设备或媒体已被删除 |
DBT_DEVICETYPESPECIFIC |
发生了特定于设备的事件 |
DBT_CUSTOMEVENT |
发生了自定义事件 |
DBT_USERDEFINED |
用户自定义事件 |
处理完该消息后返回TRUE表示接收请求,返回BROADCAST_QUERY_DENY表示拒绝请求。这里只关注DBT_DEVICEARRAIVAL或DBT_DEVICEDEVICEMOVECOMPLETE,这时lParam为指向DEV_BROADCAST_HDR结构的指针,结构如下:
1 2 3 4 5 6
| struct _DEV_BROADCAST_HDR { DWORD dbch_size; DWORD dbch_devicetype; DWORD dbch_reserved; }; typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR;
|
其中dbch_devicetype表示设备类型:
枚举值 |
含义 |
lParam结构 |
DBT_DEVTYP_DEVICEINTERFACE |
设备类 |
DEV_BROADCAST_DEVICEINTERFACE |
DBT_DEVTYP_HANDLE |
文件系统句柄 |
DEV_BROADCAST_HANDLE |
DBT_DEVTYP_OEM |
OEM或IHV定义的设备类型 |
DEV_BROADCASE_OEM |
DBT_DEVTYP_PORT |
串/并行端口设备 |
DEV_BROADCAST_PORT |
DBT_DEVTYP_VOLUME |
逻辑卷 |
DEV_BROADCAST_VOLUME |
这里只关注DBT_DEVTYP_VOLUME,其中DEV_BROADCAST_VOLUME结构为:
1 2 3 4 5 6 7 8
| struct _DEV_BROADCAST_VOLUME { DWORD dbcv_size; DWORD dbcv_devicetype; DWORD dbcv_reserved; DWORD dbcv_unitmask; WORD dbcv_flags; }; typedef struct _DEV_BROADCAST_VOLUME DEV_BROADCAST_VOLUME;
|
dbcv_unitmask中,如位0是驱动器A、位1是驱动器B。
dbcv_flags是标志位:
枚举值 |
含义 |
DBTF_MEDIA |
更改会影响驱动器中的媒体,否则影响物理设备或驱动器 |
DBTF_NET |
逻辑卷是网络卷 |
例子
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
| #include <windows.h> #include <Dbt.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { PDEV_BROADCAST_HDR pDevBroadcastHdr = NULL; PDEV_BROADCAST_VOLUME pDevBroadcastVolume = NULL; DWORD dwDriverMask, dwIndex; switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_DEVICECHANGE: { switch (wParam) { case DBT_DEVICEARRIVAL: { pDevBroadcastHdr = (PDEV_BROADCAST_HDR)lParam; if (pDevBroadcastHdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { pDevBroadcastVolume = (PDEV_BROADCAST_VOLUME)lParam; dwDriverMask = pDevBroadcastVolume->dbcv_unitmask; dwIndex = 0x00000001; TCHAR szDriverName[] = TEXT("A:\\"); for (szDriverName[0] = TEXT('A'); szDriverName[0] <= TEXT('Z'); szDriverName[0]++) { if ((dwDriverMask & dwIndex) > 0) MessageBox(hwndDlg, szDriverName, TEXT("设备已插入"), MB_OK); dwIndex = dwIndex << 1; }; }; break; }; case DBT_DEVICEREMOVECOMPLETE: { pDevBroadcastHdr = (PDEV_BROADCAST_HDR)lParam; if (pDevBroadcastHdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { pDevBroadcastVolume = (PDEV_BROADCAST_VOLUME)lParam; dwDriverMask = pDevBroadcastVolume->dbcv_unitmask; dwIndex = 0x00000001; TCHAR szDriverName[] = TEXT("A:\\"); for (szDriverName[0] = TEXT('A'); szDriverName[0] <= TEXT('Z'); szDriverName[0]++) { if ((dwDriverMask & dwIndex) > 0) MessageBox(hwndDlg, szDriverName, TEXT("设备已拔出"), MB_OK); dwIndex = dwIndex << 1; }; }; break; }; }; return TRUE; }; }; return FALSE; };
|
获取主板和BIOS序列号
GetSystemFirmwareTable
获取指定固件表:
1 2 3 4 5 6
| UINT WINAPI GetSystemFirmwareTable( _In_ DWORD FirmwareTableProviderSignature, _In_ DWORD FirmwareTableID, _Out_ PVOID pF1irmwareTableBuffer, _In_ DWORD BufferSize )
|
其中FirmwareTableProviderSignature可以是:
值 |
含义 |
'ACPI' |
ACPI固件表提供程序 |
'FIRM' |
原始固件表提供程序 |
'RSMB' |
原始SMBIOS固件表提供程序 |
对于FirmwareTableID获取SMBIOS固件表指定0就行。pFirmwareTableBuffer位NULL时函数返回所需缓冲区大小。
PFirmwareTableBuffer返回的缓冲区时RawSMBIOSTable(现更名为RawSMBIOSData???)结构:
1 2 3 4 5 6 7 8
| struct RawSMBIOSData{ BYTE Used20CallingMethod; BYTE SMBIOSMajorVersion; BYTE SMBIOSMinorVersion; BYTE DmiRevision; DWORD Length; BYTE SMBIOSTableData[]; };
|
注册表HEKY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\mssmbios\Data子键下有个键名SMBiosData,键值是原始SMBIOS固件表,但GetSystemFirmwareTable
不是读的这个。
后面太复杂了略。
例子
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
| #include <windows.h> #include <strsafe.h> #include "resource.h" #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
typedef unsigned __int64 QWORD;
typedef struct _RawSMBIOSTable{ BYTE m_bUsed20CallingMethod; BYTE m_bSMBIOSMajorVersion; BYTE m_bSMBIOSMinorVersion; BYTE m_bDmiRevision; DWORD m_dwLength; BYTE m_bSMBIOSTableData[1]; }RawSMBIOSTable, * PRawSMBIOSTable;
typedef struct _SMBIOSStructHeader{ BYTE m_bType; BYTE m_bLength; WORD m_wHandle; }SMBIOSStructHeader, * PSMBIOSStructHeader; #pragma pack(1)
typedef struct _Type1SystemInformation{ SMBIOSStructHeader m_sHeader; BYTE m_bManufacturer; BYTE m_bProductName; BYTE m_bVersion; BYTE m_bSerialNumber; UUID m_uuid; BYTE m_bWakeupType; BYTE m_bSKUNumber; BYTE m_bFamily; }Type1SystemInformation, * PType1SystemInformation;
typedef struct _Type2BaseboardInformation{ SMBIOSStructHeader m_sHeader; BYTE m_bManufactur; BYTE m_bProduct; BYTE m_bVersion; BYTE m_bSerialNumber; BYTE m_bAssetTag; BYTE m_bFeatureFlags; BYTE m_bLocationInChassis; WORD m_wChassisHandle; BYTE m_bBoardType; }Type2BaseboardInformation, * PType2BaseboardInformation;
typedef struct _Type4ProcessorInformation{ SMBIOSStructHeader m_sHeader; BYTE m_bSocketDesignation; BYTE m_bProcessorType; BYTE m_bProcessorFamily; BYTE m_bProcessorManufacturer; QWORD m_qProcessorID; BYTE m_bProcessorVersion; BYTE m_bVoltage; WORD m_wExternalClock; WORD m_wMaxSpeed; WORD m_wCurrentSpeed; BYTE m_bStatus; BYTE m_bProcessorUpgrade; WORD m_wL1CacheHandle; WORD m_wL2CacheHandle; WORD m_wL3CacheHandle; BYTE m_bSerialNumber; BYTE m_bAssetTag; BYTE m_bPartNumber; BYTE m_bCoreCount; BYTE m_bCoreEnabled; BYTE m_bThreadCount; WORD m_wProcessorCharacteristics; }Type4ProcessorInformation, * PType4ProcessorInformation; #pragma pack()
HINSTANCE g_hInstance;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
LPSTR FingStrFromStruct(PSMBIOSStructHeader pStructHeader, BYTE bNum); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_hInstance = hInstance; DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HWND hwndEdit = NULL; DWORD dwBufferSize = 0; LPBYTE lpBuf = NULL; PRawSMBIOSTable pRawSMBIOSTable = NULL; LPBYTE lpData = NULL; PSMBIOSStructHeader pStructHeader = NULL; CHAR szBuf[1024] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { hwndEdit = GetDlgItem(hwndDlg, IDC_EDIT_INFO); dwBufferSize = GetSystemFirmwareTable('RSMB', 0, NULL, 0); if (dwBufferSize == 0) return TRUE; lpBuf = new BYTE[dwBufferSize]; ZeroMemory(lpBuf, dwBufferSize); if (GetSystemFirmwareTable('RSMB', 0, lpBuf, dwBufferSize) != dwBufferSize) return TRUE; pRawSMBIOSTable = (PRawSMBIOSTable)lpBuf; lpData = pRawSMBIOSTable->m_bSMBIOSTableData; while ((lpData - pRawSMBIOSTable->m_bSMBIOSTableData) < pRawSMBIOSTable->m_dwLength) { pStructHeader = (PSMBIOSStructHeader)lpData; if (pStructHeader->m_bType == 1) { PType1SystemInformation pType1 = (PType1SystemInformation)pStructHeader; CHAR szUUID[64] = { 0 }; wsprintfA(szUUID, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", pType1->m_uuid.Data1, pType1->m_uuid.Data2, pType1->m_uuid.Data3, pType1->m_uuid.Data4[0], pType1->m_uuid.Data4[1], pType1->m_uuid.Data4[2], pType1->m_uuid.Data4[3], pType1->m_uuid.Data4[4], pType1->m_uuid.Data4[5], pType1->m_uuid.Data4[6], pType1->m_uuid.Data4[7]); ZeroMemory(szBuf, sizeof(szBuf)); StringCchPrintfA(szBuf, _countof(szBuf), "系统信息(类型1):\r\nType\t\t%d\r\nLength\t\t0x%X\r\nHandle\t\t%d\r\nManufacturer\t%s\r\nProduct Name\t%s\r\nVersion\t\t%s\r\nSerial Number\t%s\r\nUUID\t\t%s\r\nWake-up Type\t%d\r\nSKU Number\t%s\r\nFamily\t\t%s\r\n\r\n", pType1->m_sHeader.m_bType, pType1->m_sHeader.m_bLength, pType1->m_sHeader.m_wHandle, FingStrFromStruct(pStructHeader, pType1->m_bManufacturer), FingStrFromStruct(pStructHeader, pType1->m_bProductName), FingStrFromStruct(pStructHeader, pType1->m_bVersion), FingStrFromStruct(pStructHeader, pType1->m_bSerialNumber), szUUID, pType1->m_bWakeupType, FingStrFromStruct(pStructHeader, pType1->m_bSKUNumber), FingStrFromStruct(pStructHeader, pType1->m_bFamily)); SendMessageA(hwndEdit, EM_SETSEL, -1, -1); SendMessageA(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szBuf); } else if (pStructHeader->m_bType == 2) { PType2BaseboardInformation pType2 = (PType2BaseboardInformation)pStructHeader; ZeroMemory(szBuf, sizeof(szBuf)); StringCchPrintfA(szBuf, _countof(szBuf), "基板信息(类型2):\r\nType\t\t%d\r\nLength\t\t0x%X\r\nHandle\t\t%d\r\nManufactur\t%s\r\nProduct\t\t%s\r\nVersion\t\t%s\r\nSerial Number\t%s\r\nAsset Tag\t%s\r\nFeature Flags\t%d\r\nLocation In Chassis\t%s\r\nChassis Handle\t%d\r\nBoard Type\t%d\r\n\r\n", pType2->m_sHeader.m_bType, pType2->m_sHeader.m_bLength, pType2->m_sHeader.m_wHandle, FingStrFromStruct(pStructHeader, pType2->m_bManufactur), FingStrFromStruct(pStructHeader, pType2->m_bProduct), FingStrFromStruct(pStructHeader, pType2->m_bVersion), FingStrFromStruct(pStructHeader, pType2->m_bSerialNumber), FingStrFromStruct(pStructHeader, pType2->m_bAssetTag), pType2->m_bFeatureFlags, FingStrFromStruct(pStructHeader, pType2->m_bLocationInChassis), pType2->m_wChassisHandle, pType2->m_bBoardType); SendMessageA(hwndEdit, EM_SETSEL, -1, -1); SendMessageA(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szBuf); } else if (pStructHeader->m_bType == 4) { PType4ProcessorInformation pType4 = (PType4ProcessorInformation)pStructHeader; ZeroMemory(szBuf, sizeof(szBuf)); StringCchPrintfA(szBuf, _countof(szBuf), "处理器信息(类型4):\r\nType\t\t\t%d\r\nLength\t\t\t0x%X\r\nHandle\t\t\t%d\r\nSocket Designation\t\t%s\r\nProcessor Type\t\t%d\r\nProcessor Family\t\t%d\r\nProcessor Manufacturer\t%s\r\nProcessor ID\t\t%I64X\r\nProcessor Version\t\t%s\r\nVoltage\t\t\t%d\r\nExternal Clock\t\t%d\r\nMax Speed\t\t%d\r\nCurrent Speed\t\t%d\r\nStatus\t\t\t%d\r\nProcessor Upgrade\t\t%d\r\nL1 Cache Handle\t\t%d\r\nL2 Cache Handle\t\t%d\r\nL3 Cache Handle\t\t%d\r\nSerial Number\t\t%s\r\nAsset Tag\t\t%s\r\nPart Number\t\t%s\r\nCore Count\t\t%d\r\nCore Enabled\t\t%d\r\nThread Count\t\t%d\r\nProcessor Characteristics\t%d\r\n\r\n", pType4->m_sHeader.m_bType, pType4->m_sHeader.m_bLength, pType4->m_sHeader.m_wHandle, FingStrFromStruct(pStructHeader, pType4->m_bSocketDesignation), pType4->m_bProcessorType, pType4->m_bProcessorFamily, FingStrFromStruct(pStructHeader, pType4->m_bProcessorManufacturer), pType4->m_qProcessorID, FingStrFromStruct(pStructHeader, pType4->m_bProcessorVersion), pType4->m_bVoltage, pType4->m_wExternalClock, pType4->m_wMaxSpeed, pType4->m_wCurrentSpeed, pType4->m_bStatus, pType4->m_bProcessorUpgrade, pType4->m_wL1CacheHandle, pType4->m_wL2CacheHandle, pType4->m_wL3CacheHandle, FingStrFromStruct(pStructHeader, pType4->m_bSerialNumber), FingStrFromStruct(pStructHeader, pType4->m_bAssetTag), FingStrFromStruct(pStructHeader, pType4->m_bPartNumber), pType4->m_bCoreCount, pType4->m_bCoreEnabled, pType4->m_bThreadCount, pType4->m_wProcessorCharacteristics); SendMessageA(hwndEdit, EM_SETSEL, -1, -1); SendMessageA(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szBuf); }; lpData += pStructHeader->m_bLength; while ((*(LPWORD)lpData) != 0) lpData++; lpData += 2; }; delete[]lpBuf; return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OPEN: { break; }; }; return TRUE; }; case WM_CLOSE: { EndDialog(hwndDlg, 0); return TRUE; }; }; return FALSE; }; LPSTR FingStrFromStruct(PSMBIOSStructHeader pStructHeader, BYTE bNum) { LPBYTE lpByte = (LPBYTE)pStructHeader + pStructHeader->m_bLength; for (BYTE i = 1; i < bNum; i++) lpByte += strlen((LPSTR)lpByte) + 1; return (LPSTR)lpByte; };
|
内存映射文件
CreateFileMapping
1 2 3 4 5 6 7 8
| HANDLE WINAPI CreateFileMapping( _In_ HANDLE hFile, _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes, _In_ DWORD flProtect, _In_ DWORD dwMaximumSizeHigh, _In_ DWORD dwMaximumSizeLow, _In_opt_ LPCTSTR lpName )
|
fProtect可以以下选一个:
枚举值 |
含义 |
PAGE_READONLY/PAGE_WRITECOPY |
可读、写时复制 |
PAGE_READWRITE |
可读可写、写时复制 |
PAGE_EXECUTE_READ/PAGE_EXECUTE_WRITECOPY |
可读可执行、写时复制 |
PAGE_EXECUTE_READWRITE |
可读可写可执行、写时复制 |
hFile还可以指定位INVALID_HANDLE_VALUE,系统使用页面交换文件创建或打开一个文件映射对象,通常用于进程间共享数据,此时fProtect还可以同时使用:
枚举值 |
含义 |
SEC_COMMIT |
默认属性,提交所有页面 |
SEC_RESERVE |
页面处于预定/保留状态,需要时用VirtualAlloc 提交 |
SEC_IMAGE |
是可执行文件 |
对于dwMaximumSizeHigh和dwMaximumSizeLow:若文件映像基于hFile指定的文件,这俩都可以为0,文件映射对象大小等于hFile指定文件大小;当hFile指定的文件为0,这俩参数也为0则调用失败返回NULL,GetLastError
返回ERROR_FILE_INVALID;若这俩参数指定的大小大于hFile指定的文件大小则扩展文件到指定大小,扩展失败则调用失败返回HULL,GetLastError
返回ERROR_DISK_FULL;若是基于页面交换文件映射对象,这俩参数必须指定明确大小。
lpName为NULL则创建一个匿名文件映射对象,已存在则打开并返回该文件映射对象句柄。
MapViewOfFile
把文件映射对象的部分或全部映射到进程的虚拟地址空间中,返回内存指针lpMemory,通过该指针读写文件:
1 2 3 4 5 6 7
| LPVOID WINAPI MapViewOfFile( _In_ HANDLE hFileMappingObject, _In_ DWORD dwDesiredAccess, _In_ DWORD dwFileOffsetHigh, _In_ DWORD dwFileOffsetLow, _In_ SIZE_T dwNumberOfBytesToMap )
|
对于dwDesiredAccess有:
枚举值 |
含义 |
FILE_MAP_READ |
可读 |
FILE_MAP_WRITE/FILE_MAP_ALL_ACCESS |
可读可写 |
FILE_MAP_COPY |
写时复制 |
FILE_MAP_EXECUTE |
可执行 |
该函数从dwFileOffsetHigh和dwFileOffsetLow偏移量开始映射dwNumberOfBytesToMap字节。当dwNumberOfBytesToMap位0则映射到文件映射对象末尾。偏移量必须是分配粒度整数倍。
MapViewOfFileEx
同上,可指定映射基地址:
1 2 3 4 5 6 7 8
| LPVOID WINAPI MapViewOfFile( _In_ HANDLE hFileMappingObject, _In_ DWORD dwDesiredAccess, _In_ DWORD dwFileOffsetHigh, _In_ DWORD dwFileOffsetLow, _In_ SIZE_T dwNumberOfBytesToMap, _In_opt_ LPVOID lpBaseAddress )
|
OverFileMapping
打开一个名命名文件映射对象:
1 2 3 4 5
| HANDLE WINAPI OpenFileMapping( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName )
|
FlushViewOfFile
系统对文件视图的页面做缓存处理,吸入文件视图时不一定随时更新磁盘上文件,用该函数强制系统把部分或全部已修改的数据(脏页)写回到磁盘中:
1 2 3 4
| BOOL WINAPI FlushViewOfFile( _In_ LPCVOID lpBaseAddress, _In_ SIZE_T dwNumberOfBytesToFlush )
|
UnmapViewOfFile
撤销对视图的映射:
1 2 3
| BOOL WINAPI UnmapViewOfFile( _In_ LPCVOID lpBaseAddress )
|
例子
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
| #include <windows.h> #include <tchar.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { TCHAR szPath[MAX_PATH] = { 0 }; TCHAR szBuf[512] = { 0 }; LARGE_INTEGER liFileSize; HANDLE hFile, hFileMap; LPVOID lpMemory; switch (uMsg) { case WM_INITDIALOG: { SetDlgItemText(hwndDlg, IDC_EDIT_PATH, TEXT("D:\\Test.txt")); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OPEN: { GetDlgItemText(hwndDlg, IDC_EDIT_PATH, szPath, _countof(szPath)); hFile = CreateFile(szPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hwndDlg, TEXT("CreateFile函数调用失败"), TEXT("提示"), MB_OK); return TRUE; } else { GetFileSizeEx(hFile, &liFileSize); if (liFileSize.QuadPart == 0) { MessageBox(hwndDlg, TEXT("文件大小为0"), TEXT("提示"), MB_OK); CloseHandle(hFile); return TRUE; }; }; hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (!hFileMap) { MessageBox(hwndDlg, TEXT("CreateFileMapping调用失败"), TEXT("提示"), MB_OK); return TRUE; }; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) { MessageBox(hwndDlg, TEXT("MapViewOfFile调用失败"), TEXT("提示"), MB_OK); return TRUE; }; SetDlgItemText(hwndDlg, IDC_EDIT_TEXT, (LPTSTR)lpMemory); UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); CloseHandle(hFile); break; }; case IDC_BTN_APPEND: { if (!GetDlgItemText(hwndDlg, IDC_EDIT_APPEND, szBuf, _countof(szBuf))) { MessageBox(hwndDlg, TEXT("请输入追加内容"), TEXT("提示"), MB_OK); break; }; GetDlgItemText(hwndDlg, IDC_EDIT_PATH, szPath, _countof(szPath)); hFile = CreateFile(szPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hwndDlg, TEXT("CreateFile函数调用失败"), TEXT("提示"), MB_OK); return TRUE; }; GetFileSizeEx(hFile, &liFileSize); hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, liFileSize.HighPart, liFileSize.LowPart + _tcslen(szBuf) * sizeof(TCHAR), NULL); if (!hFileMap) { MessageBox(hwndDlg, TEXT("CreateFileMapping调用失败"), TEXT("提示"), MB_OK); return TRUE; }; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) { MessageBox(hwndDlg, TEXT("MapViewOfFile调用失败"), TEXT("提示"), MB_OK); return TRUE; }; memcpy_s((LPBYTE)lpMemory + liFileSize.QuadPart, _tcslen(szBuf) * sizeof(TCHAR), szBuf, _tcslen(szBuf) * sizeof(TCHAR)); FlushViewOfFile(lpMemory, 0); SetDlgItemText(hwndDlg, IDC_EDIT_TEXT, (LPTSTR)lpMemory); UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); CloseHandle(hFile); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
多进程间数据共享
启动多个同一个.exe时,可执行代码的页面为两个进程所共享。但当一个程序实例修改并写入一个内存页,则将导致混乱,这时引进写时复制。系统统计多少页面可写,从页面交换文件中分配空间来容纳,当线程视图写入一个共享页面时,系统从分配的页面交换文件中找一个空闲页面,并把要修改的页面复制到空闲页面,更新进程页面表,这个进程即可访问它自己的副本。
CoCreateGuid
动态生成一个GUID:
1 2 3 4 5 6
| GUID guid; TCHAR szGUID[64] = { 0 };
CoCreateGuid(&guid);
wsprintf(szGUID, TEXT("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"),guid.Data1, guid.Data2, guid.Data3,guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
|
例子
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
| #include <windows.h> #include "resource.h" #define BUF_SIZE 4096
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HANDLE hFileMap; static LPVOID lpMemory; switch (uMsg) { case WM_INITDIALOG: { hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, TEXT("2F4368E6-09A1-4D5E-ACC9-C1BDBB041BF7")); if (!hFileMap) { MessageBox(hwndDlg, TEXT("CreateFileMapping调用失败"), TEXT("提示"), MB_OK); return TRUE; }; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) { MessageBox(hwndDlg, TEXT("MapViewOfFile调用失败"), TEXT("提示"), MB_OK); return TRUE; }; SetTimer(hwndDlg, 1, 1000, NULL); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_EDIT_SHARE: { if (HIWORD(wParam) == EN_UPDATE) GetDlgItemText(hwndDlg, IDC_EDIT_SHARE, (LPTSTR)lpMemory, BUF_SIZE); break; }; case IDCANCEL: { KillTimer(hwndDlg, 1); UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_TIMER: { SetDlgItemText(hwndDlg, IDC_STATIC_SHARE, (LPTSTR)lpMemory); return TRUE; }; }; return FALSE; };
|
大型文件处理
复制大型文件,注意MapViewOfFile
的文件映射对象偏移量参数必须指定为内存分配粒度整数倍:
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
| BOOL CopyLargeFile(LPCTSTR lpFileName1, LPCTSTR lpFileName2) { HANDLE hFile1, hFile2, hFileMap; LPVOID lpMemory; hFile1 = CreateFile(lpFileName1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile1 == INVALID_HANDLE_VALUE) return FALSE; hFile2 = CreateFile(lpFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile2 == INVALID_HANDLE_VALUE) return FALSE; hFileMap = CreateFileMapping(hFile1, NULL, PAGE_READONLY, 0, 0, NULL); if (!hFileMap) return FALSE; __int64 qwFileSize; DWORD dwFileSizeHigh; SYSTEM_INFO si; qwFileSize = GetFileSize(hFile1, &dwFileSizeHigh); qwFileSize += (((__int64)dwFileSizeHigh) << 32); GetSystemInfo(&si); __int64 qwFileOffset = 0; DWORD dwBytesInBlock; while (qwFileSize > 0) { dwBytesInBlock = si.dwAllocationGranularity; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF), dwBytesInBlock); if (!lpMemory) return FALSE; WriteFile(hFile2, lpMemory, dwBytesInBlock, NULL, NULL); UnmapViewOfFile(lpMemory); qwFileOffset += dwBytesInBlock; qwFileSize -= dwBytesInBlock; }; CloseHandle(hFileMap); CloseHandle(hFile1); CloseHandle(hFile2); return TRUE; };
|
APC异步过程调用
ReadFileEx/WriteFileEx
在指定文件或其他I/O设备中读/写数据,系统异步报告完成状态,并在操作完成/取消时且调用线程处于可通知/提醒的等待状态时调用指定的例程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| BOOL WINAPI ReadFileEx( _In_ HANDLE hFile, _Out_ LPVOID lpBuffer, _In_ DWORD nNumberOfBytesToRead, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ); BOOL WINAPI WriteFileEx( _In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
|
对于hFile,在CreateFile
时dwFlagsAndAttributes需要FILE_FLAG_OVERLAPPED标志。
OVERLAPPED结构为:
1 2 3 4 5 6 7 8 9 10 11 12
| typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; union { struct { DWORD Offset; DWORD OffsetHigh; } DUMMYSTRUCTNAME; PVOID Pointer; } DUMMYUNIONNAME; HANDLE hEvent; } OVERLAPPED, * LPOVERLAPPED;
|
要写入文件末尾时,Offset和OffsetHigh都指定为0xFFFFFFFF。
完成例程lpCompletionRoutine定义形式为:
1 2 3 4 5
| VOID WINAPI OverlappedCompletionRoutine( _In_ DWORD dwErrorCode, _In_ DWORD dwNumberOfBytesTransfered, _Inout_ LPOVERLAPPED lpOverlapped )
|
这里的dwErrorCode和dwNumberOfBytesTransfered与OVERLAPPED结构的Internal和InternalHigh效果一样。异步I/O操作成功完成并执行完成例程,则dwErrorCode值为ERROR_SUCCESS。
使调用线程处于可通知的等待状态的方法有SleepEx
、MsgWaitForMultipleObjectsEx
、WaitForSingleObjectEx
、WaitForMultipleObjectsEx
等,当函数执行成功,异步I/O完成后并等待调用线程进入可通知的等待状态后,系统调用完成例程并执行结束后,等待函数返回值为WAIT_IO_COMPLETION。
系统创建一个线程时,同时创建一个相关联的APC异步过程调用队列。调用ReadFileEx
/WriteFileEx
函数后,系统将完成例程地址传递给设备驱动程序,设备驱动程序在发出I/O请求的线程APC队列中添加一项,该项包括完成例程的地址和发出I/O请求时所使用的OVERLAPPED结构的地址。调用等待函数时,若APC队列中没有项目则一直等待直到超时。出现一项时系统取出并删除,调用完成例程。
例子
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
| #include <windows.h> #include "resource.h" #pragma comment(lib, "Winmm.lib") #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
typedef struct _IOData{ HANDLE m_hFileSource; HANDLE m_hFileDest; HANDLE m_hFileMap; LPVOID m_lpMemory; }IOData, * PIOData;
HINSTANCE g_hInstance; HWND g_hwndDlg;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID WINAPI OverlappedCompletionRoutine( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { g_hInstance = hInstance; DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){ TCHAR szFileNameSource[MAX_PATH] = { 0 }; TCHAR szFileNameDest[MAX_PATH] = { 0 }; OPENFILENAME ofnSource = { 0 }; ofnSource.lStructSize = sizeof(ofnSource); ofnSource.hwndOwner = hwndDlg; ofnSource.lpstrFilter = TEXT("All(*.*)\0*.*\0"); ofnSource.nFilterIndex = 1; ofnSource.lpstrFile = szFileNameSource; ofnSource.lpstrFile[0] = NULL; ofnSource.nMaxFile = _countof(szFileNameSource); ofnSource.lpstrTitle = TEXT("请选择要打开的文件"); ofnSource.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_CREATEPROMPT; OPENFILENAME ofnDest = { 0 }; ofnDest.lStructSize = sizeof(ofnDest); ofnDest.hwndOwner = hwndDlg; ofnDest.lpstrFilter = TEXT("All(*.*)\0*.*\0"); ofnDest.nFilterIndex = 1; ofnDest.lpstrFile = szFileNameDest; ofnDest.lpstrFile[0] = NULL; ofnDest.nMaxFile = _countof(szFileNameDest); ofnDest.lpstrTitle = TEXT("请选择要保存的文件名"); ofnDest.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT; HANDLE hFileSource = INVALID_HANDLE_VALUE, hFileDest = INVALID_HANDLE_VALUE; HANDLE hFileMap = NULL; LPVOID lpMemory = NULL; DWORD dwFileSizeLow = 0; DWORD dwFileSizeHigh = 0; BOOL bRet = FALSE; DWORD dwRet = 0; PIOData pIOData = NULL; LPOVERLAPPED lpOverlapped = NULL; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_SOURCE: { if (GetOpenFileName(&ofnSource)) SetDlgItemText(hwndDlg, IDC_EDIT_SOURCE, szFileNameSource); break; }; case IDC_BTN_DEST: { if (GetSaveFileName(&ofnDest)) SetDlgItemText(hwndDlg, IDC_EDIT_DEST, szFileNameDest); break; }; case IDC_BTN_COPY: { GetDlgItemText(hwndDlg, IDC_EDIT_SOURCE, szFileNameSource, _countof(szFileNameSource)); hFileSource = CreateFile(szFileNameSource, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (hFileSource == INVALID_HANDLE_VALUE) { MessageBox(hwndDlg, TEXT("打开源文件失败"), TEXT("提示"), MB_OK); return TRUE; }; dwFileSizeLow = GetFileSize(hFileSource, &dwFileSizeHigh); hFileMap = CreateFileMapping(hFileSource, NULL, PAGE_READWRITE, 0, 0, NULL); if (!hFileMap) { MessageBox(hwndDlg, TEXT("CreateFileMapping调用失败"), TEXT("提示"), MB_OK); return TRUE; }; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) { MessageBox(hwndDlg, TEXT("MapViewOfFile调用失败"), TEXT("提示"), MB_OK); return TRUE; }; GetDlgItemText(hwndDlg, IDC_EDIT_DEST, szFileNameDest, _countof(szFileNameDest)); hFileDest = CreateFile(szFileNameDest, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (hFileDest == INVALID_HANDLE_VALUE) { MessageBox(hwndDlg, TEXT("创建目标文件失败"), TEXT("提示"), MB_OK); return TRUE; }; pIOData = new IOData; pIOData->m_hFileSource = hFileSource; pIOData->m_hFileDest = hFileDest; pIOData->m_hFileMap = hFileMap; pIOData->m_lpMemory = lpMemory; lpOverlapped = new OVERLAPPED; ZeroMemory(lpOverlapped, sizeof(OVERLAPPED)); lpOverlapped->Offset = 0; lpOverlapped->OffsetHigh = 0; lpOverlapped->hEvent = pIOData; bRet = WriteFileEx(hFileDest, lpMemory, dwFileSizeLow, lpOverlapped, OverlappedCompletionRoutine); PlaySound(TEXT("爱是一缕寂寞的愁.wav"), NULL, SND_FILENAME | SND_ASYNC); break; }; case IDC_BTN_QUERY: { dwRet = SleepEx(INFINITE, TRUE); break; }; }; return TRUE; }; case WM_CLOSE: { EndDialog(hwndDlg, 0); return TRUE; }; }; return FALSE; }; VOID WINAPI OverlappedCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) { PIOData pIOData = (PIOData)(lpOverlapped->hEvent); MessageBox(g_hwndDlg, TEXT("写入目标文件完成"), TEXT("提示"), MB_OK); UnmapViewOfFile(pIOData->m_lpMemory); CloseHandle(pIOData->m_hFileMap); CloseHandle(pIOData->m_hFileSource); CloseHandle(pIOData->m_hFileDest); delete pIOData; delete lpOverlapped; return; };
|
CancelIo
取消调用线程为指定文件或其他I/O设备发出的所有未完成的异步I/O操作:
1 2 3
| BOOL WINAPI CancelIo( _In_ HANDLE hFile )
|
CancelIoEx
同上,取消当前进程的另一个线程中异步I/O操作:
1 2 3 4
| BOOL WINAPI CancelIoEx( _In_ HANDLE hFile, _In_opt_ LPOVERLAPPED lpOverlapped )
|
CancelSynchronousIo
取消指定线程中未完成的同步I/O操作:
1 2 3
| BOOL WINAPI CancelSynchronousIo( _In_ HANDLE hThread )
|
线程句柄需要THREAD_TERMINATE访问权限。
QueueUserAPC
将一个用户模式APC对象添加到指定线程APC队列:
1 2 3 4 5
| DWORD WINAPI QueueUserAPC( _In_ PAPCFUNC pfnAPC, _In_ HANDLE hThread, _In_ ULONG_PTR dwData )
|
APC回调函数定义格式:
1 2 3
| VOID APCProc( _In_ ULONG_PTR Parameter )
|