第4章文件系統
4.2 磁盤和驅動器管理
文件系統的基本概念:
包括磁盤分區,卷,目錄,文件對象,文件句柄,文件映射
1.磁盤分區:
物理磁盤,邏輯磁盤
2.卷:
也稱邏輯驅動器,是NTFS,FAT32等文件系統組織結構的最高層.
卷是存儲設備(硬盤)上由文件系統管理的一塊區域,在邏輯上相互隔離的存儲單元.
windows命名規則:
主文件名+擴展名
windows中文件系統的長度被限制為260個字符.
這260個字符包括卷標,路徑,主文件名和擴展名,分隔符
在DOS下的保留設備名不能做文件名或主文件名.如CON,PRN,AUX,NUL,COM1.....
=================================================================
磁盤和驅動器管理API
GetLogicalDrivers 獲取主機中所有的邏輯驅動器,以Bit Map的形式返回.
GetLogicalDriverString 獲取主機中所有的邏輯驅動器,以驅動器根路徑字符串返回.
FindFirstVolume 查找主機中的第一個驅動器,返回查找句柄.
FindNextVolume 根據FindFirstVolume返回句柄,查找主機中后繼的邏輯驅動器
FindVolumeClose 關閉驅動器查找句柄
GetDriveType 獲取驅動器類型
GetVolumeInformation 獲取邏輯驅動器信息
FindFirstVolumeMountPoint 查找指定卷的第一個掛載點,返回查找句柄
FindNextVolumeMountPoint 根據FindFirstVolumeMountPoint返回的句柄,查找卷的后繼掛載點.
FindVolumeMountPointClose 關閉掛載點查找句柄
GetVolumeNameForVolumeMountPoint 根據指定掛載點獲取相應的卷設備名
SetVolumeMountPoint 將指定卷掛載到指定掛載點處
GetDiskFreeSpace 獲取磁盤空間信息,包括每簇的扇區數,每扇區的字節數,簇數量,空閑的簇數量
GetDiskFreeSpaceEx 獲取用戶可用的空閑空間的字節數,磁盤總容量的字節數
文件和目錄管理API
DeleteFile 刪除參數所指定文件
CopyFile 復制指定文件為一個新文件
MoveFile 將指定文件或目錄移動到指定位置
CreateFile 新建或打開一個文件,獲取文件句柄
ReadFile 讀取由文件句柄指定文件的內容
WriteFile 向由文件句柄指定的文件中寫入內容
GetFileSize 獲取文件大小,返回DWORD中;大小超出DWORD最大值時可指定高32位的DWORD聯合存儲
GetFileSizeEx 獲取文件大小,存儲到一個64位的大整數聯合體中.
CreateDirectory 創建一個目錄
GetCurrentDirectory 獲取當前程序所在目錄
SetCurrentDirectory 設置當前程序所在目錄
GetModuleFileName 獲取當前模塊全路徑
FindFirstFile 查找指定目錄下第一個文件句柄或目錄,獲得查找句柄
FindNextFile 根據FindFirstFile獲得的句柄,循環查找文件或目錄
GetFileAttributes 獲取指定文件目錄屬性,返回一個DWORD值
GetFileAttributesEx 獲取文件或目錄屬性,存儲在WIN32_FILE_ATTRIBUTE_DATA結構體中
SetFileAttributes 將文件屬性設定為指定值
FileTimeToLocalFileTime 將文件時間轉換為本地時間
FileTimeToSystemTime 將文件轉換為系統時間,SYSTEMTIME格式便於顯示
高級文件操作
CreateFileMapping 創建文件的映射對象
MapViewOfFile 創建視圖,將創建的文件映射對象映射到當前進程的地址空間中
FlushViewOfFile 將視圖中的數據都寫入磁盤,對視圖的操作都會反映到磁盤上的文件中
OpenFileMapping 打開已經存在的命名的文件映射對象
UnmapViewOfFile 取消文件映射
GetMappedFileName 從映射對象獲取被映射文件的文件設備名
QueryDosDevice 獲取MS-DOS設備名
=================================================================
2 磁盤和驅動器管理
◇使用兩種方法來遍歷驅動器並獲取驅動器屬性。
◇使用API操作驅動器掛載點。
◇判斷光驅中是否有光盤。
◇獲取磁盤剩余空間、扇區信息等。
4.2.1 遍歷卷並獲取屬性
用到的API函數:
(1) GetLogicalDrives。
獲取主機中所有的邏輯驅動器,以BitMap的形式返回,其函數原型如下:
(2) GetLogicalDriverStrings。
獲取主機中所有驅動器,以驅動器根路徑字符串返回,其函數原型如下:
(3)FindFirstVolume。
查找主機中的第一個驅動器,返回驅動器設備名,其函數原型如下:
(4)FindNextVolume
查找主機中后繼的邏輯驅動器,其函數原型如下:
(5)FindVo1umeClose。
\關閉FindFirstVolume打開的卷遍歷句柄,其函數原型如下:
(6) GetDriveType。
獲取驅動器類型,其函數原型如下:
(7) GetVolumeInformation。
獲取邏輯驅動器信息,其函數原型如下:
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * GetVolumeInfo.c
- * 4.2.1 遍歷驅動器並獲取驅動器屬性
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- /* 預定義 */
- #define BUFSIZE 1024
- /* 函數申明 */
- BOOL GetDirverInfo(LPSTR szDrive);
- /* ************************************
- * 功能 應用程序主函數,遍歷驅動器並調用
- * GetDirverInfo 獲取驅動器屬性
- **************************************/
- void main(void)
- {
- CHAR szLogicalDriveStrings[BUFSIZE];
- PCHAR szDrive;
- ZeroMemory(szLogicalDriveStrings,BUFSIZE);
- // 獲取邏輯驅動器卷標名
- GetLogicalDriveStrings(BUFSIZE - 1,szLogicalDriveStrings);
- szDrive = (PCHAR)szLogicalDriveStrings;
- // 循環處理每個卷
- do
- {
- if(!GetDirverInfo(szDrive))
- {
- printf("/nGet Volume Information Error: %d", GetLastError());
- }
- szDrive += (lstrlen(szDrive)+1);
- }
- while(*szDrive!='/x00');
- }
- /* ************************************
- * BOOL GetDirverInfo(LPSTR szDrive)
- * 功能 獲取驅動器的屬性
- * 參數 LPSTR szDrive
- * 指明要獲取屬性的驅動器的根路徑 如 C:/
- * 返回值 BOOL 是否成功
- **************************************/
- BOOL GetDirverInfo(LPSTR szDrive)
- {
- UINT uDriveType;
- DWORD dwVolumeSerialNumber;
- DWORD dwMaximumComponentLength;
- DWORD dwFileSystemFlags;
- TCHAR szFileSystemNameBuffer[BUFSIZE];
- printf("/n%s/n",szDrive);
- uDriveType = GetDriveType(szDrive);
- // 判斷類型
- switch(uDriveType)
- {
- case DRIVE_UNKNOWN:
- printf("The drive type cannot be determined. 未知的磁盤類型 ");
- break;
- case DRIVE_NO_ROOT_DIR:
- printf("The root path is invalid, for example, no volume is mounted at the path. 說明lpRootPathName是無效的 ");
- break;
- case DRIVE_REMOVABLE:
- printf("The drive is a type that has removable media, for example, a floppy drive or removable hard disk. 可移動磁盤 ");
- break;
- case DRIVE_FIXED:
- printf("The drive is a type that cannot be removed, for example, a fixed hard drive. 固定磁盤 ");
- break;
- case DRIVE_REMOTE:
- printf("The drive is a remote (network) drive. 網絡磁盤 ");
- break;
- case DRIVE_CDROM:
- printf("The drive is a CD-ROM drive. 光驅 ");
- break;
- case DRIVE_RAMDISK:
- printf("The drive is a RAM disk. ");
- break;
- default:
- break;
- }
- if (!GetVolumeInformation(
- szDrive, NULL, 0,
- &dwVolumeSerialNumber,
- &dwMaximumComponentLength,
- &dwFileSystemFlags,
- szFileSystemNameBuffer,
- BUFSIZE
- ))
- {
- return FALSE;
- }
- printf ("/nVolume Serial Number is 存儲驅動器序列號%u",dwVolumeSerialNumber);
- printf ("/nMaximum Component Length is 文件系統所支持的文件組成部分的最大值%u",dwMaximumComponentLength);
- printf ("/nSystem Type is文件系統類 %s/n",szFileSystemNameBuffer);
- if(dwFileSystemFlags & FILE_SUPPORTS_REPARSE_POINTS)
- {
- printf ("The file system does not support volume mount points./n");
- }
- if(dwFileSystemFlags & FILE_VOLUME_QUOTAS)
- {
- printf ("The file system supports disk quotas./n");
- }
- if(dwFileSystemFlags & FILE_CASE_SENSITIVE_SEARCH)
- {
- printf ("The file system supports case-sensitive file names./n");
- }
- //you can use these value to get more informaion
- //
- //FILE_CASE_PRESERVED_NAMES
- //FILE_CASE_SENSITIVE_SEARCH
- //FILE_FILE_COMPRESSION
- //FILE_NAMED_STREAMS
- //FILE_PERSISTENT_ACLS
- //FILE_READ_ONLY_VOLUME
- //FILE_SUPPORTS_ENCRYPTION
- //FILE_SUPPORTS_OBJECT_IDS
- //FILE_SUPPORTS_REPARSE_POINTS
- //FILE_SUPPORTS_SPARSE_FILES
- //FILE_UNICODE_ON_DISK
- //FILE_VOLUME_IS_COMPRESSED
- //FILE_VOLUME_QUOTAS
- printf(".../n");
- return TRUE;
- }
4.2.2 操作驅動器掛載點
一般可以用FindFirstVolumeMountPoint系列的API來找到一個卷的所有掛載點;用
GetVolumeNameForVolumeMountPoint來獲取指定掛載點所指向的卷名,卷名形式為"//?/Volume{GUID}/”;用SetVolumeMountPoint來設置新的掛載點。
通過系統的磁盤管理功能可以設置卷的掛載點,如圖4-2所示。
◇“我的電腦”圖標右鍵菜單中選擇“管理”。
◇彈出“計算機管理”窗口,選擇“磁盤管理”。
◇選中需要掛載的卷,在右鍵菜單中選擇“更改驅動 器名和路徑”。
◇在彈出的對話框中單擊“添加”按鈕,選擇“裝入 以下空白NTFS文件夾”。
◇選擇需要將卷掛載入的文件夾(空白),單擊“確定”按鈕。
◇卷就被裝入文件夾中,之后就可以和訪問文件夾一個訪問這個卷了,如圖4-3和4-4
(1)FindFirstVolumeMountPoint.
獲取指定卷的第一個掛載點,函數原型如下:
以用GetLastError()函數獲取更詳細的錯誤信息。
(2) FindNextVolumeMountPoint
查找指定卷的后繼掛載點,函數原型如下:
(3)FindVolumeMountPointClose.
關閉FindVolumeMountPointClose打開的卷句柄,其函數原型如下:
◇參數
hFindVolumeMountPoint:要關閉的掛載點查找句柄。
◇返回值
(4)GetVolumeNameForVolumeMountPoint。
根據指定的掛載點獲取相應的卷設備名,函數原型如下:
(5)SetVolumeMountPc
將指定卷掛載到指定掛載點處,函數原型如下:
- /* ************************************
- *《Windows應用程序開發》
- * 示例代碼
- * mount.c
- * 4.2.2 卷掛載點操作
- **************************************/
- /* 預編譯聲明 */
- #define _WIN32_WINNT 0x0501
- #include <windows.h>
- #include <stdio.h>
- #include <tchar.h>
- #define BUFSIZE MAX_PATH
- #define FILESYSNAMEBUFSIZE MAX_PATH
- /* ************************************
- * ProcessVolumeMountPoint
- * 功能 列舉掛載點
- **************************************/
- BOOL ProcessVolumeMountPoint (HANDLE hPt,
- TCHAR *PtBuf, DWORD dwPtBufSize,
- TCHAR *Buf)
- {
- BOOL bFlag; // 結果
- TCHAR Path[BUFSIZE]; // 全路徑
- TCHAR Target[BUFSIZE]; // 掛載點設備
- printf ("/tVolume mount point found is /"%s/"/n", PtBuf);
- lstrcpy (Path, Buf);
- lstrcat (Path, PtBuf);
- bFlag = GetVolumeNameForVolumeMountPoint(
- Path, Target, BUFSIZE );
- if (!bFlag)
- printf ("/tAttempt to get volume name for %s failed./n", Path);
- else
- printf ("/tTarget of the volume mount point is %s./n", Target);
- bFlag = FindNextVolumeMountPoint(
- hPt, PtBuf, dwPtBufSize );
- return (bFlag);
- }
- /* ************************************
- * ProcessVolume
- * 功能 判斷卷類型,列舉掛載點
- **************************************/
- BOOL ProcessVolume (HANDLE hVol, TCHAR *Buf, DWORD iBufSize)
- {
- BOOL bFlag; // 返回標志
- HANDLE hPt; // 卷句柄
- TCHAR PtBuf[BUFSIZE]; // 掛載點路徑
- DWORD dwSysFlags; // 文件系統標記
- TCHAR FileSysNameBuf[FILESYSNAMEBUFSIZE];
- printf ("Volume found is /"%s/"./n", Buf);
- // 是否NTFS
- GetVolumeInformation( Buf, NULL, 0, NULL, NULL,
- &dwSysFlags, FileSysNameBuf,
- FILESYSNAMEBUFSIZE);
- if (! (dwSysFlags & FILE_SUPPORTS_REPARSE_POINTS))
- {
- printf ("/tThis file system does not support volume mount points./n");
- }
- else
- {
- // 本卷中的掛載點
- hPt = FindFirstVolumeMountPoint(
- Buf, // 卷的跟跟蹤
- PtBuf, // 掛載點路徑
- BUFSIZE
- );
- if (hPt == INVALID_HANDLE_VALUE)
- {
- printf ("/tNo volume mount points found!/n");
- }
- else
- {
- // 處理掛載點
- bFlag = ProcessVolumeMountPoint (hPt,
- PtBuf,
- BUFSIZE,
- Buf);
- // 循環
- while (bFlag)
- bFlag =
- ProcessVolumeMountPoint (hPt, PtBuf, BUFSIZE, Buf);
- // 結束
- FindVolumeMountPointClose(hPt);
- }
- }
- // 下一個
- bFlag = FindNextVolume(
- hVol, Buf, iBufSize);
- return (bFlag);
- }
- /* ************************************
- * int GetMountPoint(void)
- * 功能 獲取掛載點
- **************************************/
- int GetMountPoint(void)
- {
- TCHAR buf[BUFSIZE]; // 卷標識符
- HANDLE hVol; // 卷句柄
- BOOL bFlag; // 結果標志
- printf("Volume mount points info of this computer:/n/n");
- // 打開卷
- hVol = FindFirstVolume (buf, BUFSIZE );
- if (hVol == INVALID_HANDLE_VALUE)
- {
- printf ("No volumes found!/n");
- return (-1);
- }
- bFlag = ProcessVolume (hVol, buf, BUFSIZE);
- while (bFlag)
- {
- bFlag = ProcessVolume (hVol, buf, BUFSIZE);
- }
- bFlag = FindVolumeClose( hVol );
- return (bFlag);
- }
- /* ************************************
- * void Usage (PCHAR argv)
- * 功能 使用方法
- **************************************/
- void Usage (PCHAR argv)
- {
- printf( "/n/n/t%s, mount a volume at a mount point./n", argv );
- printf( "/tFor example, /"mount D://mnt//drives// E:///"/n" );
- }
- /* ************************************
- * main
- * 功能 入口函數
- **************************************/
- int main( int argc, PCHAR argv[] )
- {
- BOOL bFlag;
- CHAR Buf[BUFSIZE];
- if( argc != 3 )
- {
- GetMountPoint();
- Usage( argv[0] );
- return( -1 );
- }
- bFlag = GetVolumeNameForVolumeMountPoint(
- argv[2], // 輸入掛載點或目錄
- Buf, // 輸出卷名
- BUFSIZE
- );
- if (bFlag != TRUE)
- {
- printf( "Retrieving volume name for %s failed./n", argv[2] );
- return (-2);
- }
- printf( "Volume name of %s is %s/n", argv[2], Buf );
- bFlag = SetVolumeMountPoint(
- argv[1], // 掛載點
- Buf // 需要掛載的卷
- );
- if (!bFlag)
- {
- printf ("Attempt to mount %s at %s failed. error code is/n",
- argv[2], argv[1], GetLastError());
- }
- return (bFlag);
- }
4.2.3 判斷光驅中是否有光盤
(1)GetDiskType與GetVolumeInformation。
這兩個API已經在4.2.1小節介紹過,這里不再贅述。
(2)GetLastError。
獲取在執行中本線程最近的一次錯誤。本函數是很多系統API返回執行錯誤原因的方法。可能使用SetLastError函數設置本線程的Last-Error值。GetLastError函數原型如下:
- * 4.2.3 判斷光驅中是否有光盤
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- #include <tchar.h>
- /* 預定義 */
- #define BUFSIZE 512
- /* ************************************
- * int main( int argc, PCHAR argv[] )
- * 功能 應用程序主函數,根據輸入的驅動器
- * 根路徑參數判斷是否為光驅,是否放
- * 入光盤。
- * 參數 驅動器根路徑,比如“D:/”。
- **************************************/
- int main( int argc, PCHAR argv[] )
- {
- //存儲文件系統類別名
- CHAR szFileSystemNameBuffer[BUFSIZE];
- DWORD dwLastError;
- DWORD dwFileSystemFlags;
- //判斷是否輸入運行時參數
- if( argc != 2 )
- {
- printf("請輸入驅動器的根路徑,比如: /"D:///"/n");
- return( -1 );
- }
- //判斷輸入的驅動器是否為CD/DVD ROM
- if(GetDriveType(argv[1])!=DRIVE_CDROM)
- {
- printf("驅動器 %s 不是 CD/DVD ROM。/n",argv[1]);
- return( -1 );
- }
- //獲取卷信息
- if (!GetVolumeInformation(
- argv[1], NULL, 0,
- NULL,NULL,
- &dwFileSystemFlags,
- szFileSystemNameBuffer,
- BUFSIZE
- ))
- {
- dwLastError = GetLastError();
- if(dwLastError == 21)
- {
- printf("設備未就緒,請放入光盤!/n");
- return 0;
- }
- else
- {
- printf("GetVolumeInformation 錯誤 %d/n",dwLastError);
- return 0;
- }
- }
- printf ("光盤已經放入,文件系統類別 %s。/n",szFileSystemNameBuffer);
- return 0;
- }
4.2.4 獲取磁盤分區的總容量、空閑容量、簇、扇區信息
獲取磁盤分區的總容量和空閑空間的容量可以使用GetDiskFreeSpace函數或GetDiskFree SpaceEx函數。GetDiskFreeSpace使用DWORD類型作為輸出參數,由於DWOR長度為32位,最大只能表示4GB,而一般的磁盤分區大小都大於4GB,所以,GetDiskFreeSpace並不直接返回磁盤的總容量和空閑空間的容量,而是使用總簇數、空閑的簇數、每簇的扇區數、每扇區的字節數來表示。用戶在編程時,可以使用它們的乘積來獲得最終結果。而GetDiskFreeSpaceEx使用ULARGE_INTEGER (DWORD64)類型的數據來存儲磁盤空間總空間和剩余空間,所以可以直接獲得結果。DWORD64可以表示約16777216TB的數據量(DWORD64最大可表示2Byte,lTB=2Byte,
(2)GetDiskFreeSpacEX。
獲取驅動器根路徑作為輸入,獲取用戶可用的空閑空間的字節數、空閑空間的字節數、磁盤總容量的字節數,其函數原型如下:
2.關鍵數據結構
GetDiskFreeSpaceEx函數使用到了數據結構ULARGE_INTEGER,數據類型PULARGE
INTEGER是指向它的指針。ULARGE__ INTEGER的定義如下,此數據結構使用兩個DWORD來表
示64位數據。低位存儲於前,高位存儲於后,與DWORD64的存儲形式是一致的,所以可以直接強制類型轉換為DOWRD64類型。也可以直接使用QuadPart成員,QuadPart成員是ULONGLONG形數據結構,在一般32位主機上,與DWORD64具有同樣的長度。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * diskspace.c
- * 4.2.4 獲取磁盤空間信息
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * BOOL GetDiskSpaceInfo(LPCSTR pszDrive
- * 功能 根據輸入的驅動器,獲取磁盤總容量
- * 空閑空間、簇數量等磁盤信息
- * 參數 驅動器根路徑,比如“D:/”。
- **************************************/
- BOOL GetDiskSpaceInfo(LPCSTR pszDrive)
- {
- DWORD64 qwFreeBytesToCaller, qwTotalBytes, qwFreeBytes;
- DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
- BOOL bResult;
- //使用GetDiskFreeSpaceEx獲取磁盤信息並打印結果
- bResult = GetDiskFreeSpaceEx (pszDrive,
- (PULARGE_INTEGER)&qwFreeBytesToCaller,
- (PULARGE_INTEGER)&qwTotalBytes,
- (PULARGE_INTEGER)&qwFreeBytes);
- if(bResult)
- {
- printf("使用GetDiskFreeSpaceEx獲取磁盤空間信息/n");
- printf("可獲得的空閑空間(字節): /t%I64d/n", qwFreeBytesToCaller);
- printf("空閑空間(字節): /t/t%I64d/n", qwFreeBytes);
- printf("磁盤總容量(字節): /t/t%I64d/n", qwTotalBytes);
- }
- //使用GetDiskFreeSpace獲取磁盤信息並打印結果
- bResult = GetDiskFreeSpace (pszDrive,
- &dwSectPerClust,
- &dwBytesPerSect,
- &dwFreeClusters,
- &dwTotalClusters);
- if(bResult)
- {
- printf("/n使用GetDiskFreeSpace獲取磁盤空間信息/n");
- printf("空閑的簇數量 : /t/t/t%d/n",dwFreeClusters);
- printf("總簇數量 : /t/t/t%d/n",dwTotalClusters);
- printf("每簇的扇區數量 : /t/t%d/n",dwSectPerClust);
- printf("每扇區的容量(字節): /t/t%d/n",dwBytesPerSect);
- printf("空閑空間(字節): /t/t%I64d/n",
- (DWORD64)dwFreeClusters*
- (DWORD64)dwSectPerClust*(DWORD64)dwBytesPerSect);
- printf("磁盤總容量(字節): /t/t%I64d",
- (DWORD64)dwTotalClusters*
- (DWORD64)dwSectPerClust*(DWORD64)dwBytesPerSect);
- }
- return bResult;
- }
- /* ************************************
- * int main( int argc, PCHAR argv[] )
- * 功能 應用程序主函數,根據輸入參數
- * 調用GetDiskSpaceInfo函數獲取
- * 磁盤空間信息
- * 參數 驅動器根路徑,比如“D:/”。
- **************************************/
- int main(int argc, PCHAR argv[])
- {
- GetDiskSpaceInfo (argv[1]);
- }
4.3 文件和目錄管理
文件和目錄管理是Windows系統編程最為基本的內容,幾乎所有的應用程序都會使用到文件和目錄的操作。本節將向讀者演示如何創建目錄、遍歷目錄、創建文件、打開文件、讀寫文件、移動復制刪除文件等。本節通過以下多個實例來講解Windows API對文件和目錄的管理。
◇刪除、復制、重命名、移動文件。
◇創建、打開、讀寫文件。
◇創建、打開目錄。
◇獲取當前目錄、獲取程序所在的目錄、獲取模塊路徑。
◇查找文件、遍歷目錄下的文件和子目錄。
◇遞歸遍歷目錄樹。
◇獲取、設置文件屬性和時間。
4.3.1 刪除、復制、重命名、移動文件
Windows系統為文件的刪除、復制、重命名或移動文件提供了相應的API函數。刪除文件使用DeleteFile函數;復制文件使用CopyFile函數;重命名文件和移動文件實際是一個操作,使用MoveFile函數。這幾個函數的使用都非常簡單,下面分別介紹。
1.關鍵API
(1) DeleteFile。
DeleteFile的功能是刪除文件。以文件路徑作為輸入,指向需要刪除的文件。文件路徑可以是類似於“c:/files/delete.txt”的絕對路徑,也可以是類似於“./delete.txt”的相對路徑,二相對於可執行文件所在的路徑。
(2) CopyFile。
CopyFile的功能是復制文件。通過參數輸入復制文件和源路徑和目的路徑,路徑可以是絕對路徑也可以是相對路徑,還可以通過參數指明如果目的路徑已經存在文件,是否覆蓋。可以使用CopyFileEx函數進行更為高級的操作,比如在復制進行過程中取消復制等。CopyFileEx可以指定
一個回調函數來處理文件復制中所可能發生的各種情況。
3) MoveFile。
MoveFile的功能是移動、重命名文件和目錄。通過參數輸入源路徑和目的路徑,路徑可以是絕對路徑也可以是相對路徑,如果目的路徑的文件或目錄已經存在,則返回失敗。可以使用MoveFileEx函數來指定更多的選項,如果已經存在是否替換等。還可以使用MoveFileWithProgress指定一個回調函數來處理文件移動中所可能發生的各種情況。函數原型如下:
(4) CopyFileEx、MoveFileEx以及MoveFileWithProgreSS.
這3個API函數功能更豐富,但是限於篇幅這里不再做詳細介紹,讀者可以使用SDK文檔學習它們的使用方法。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * wr.c
- * 4.3.2 創建、打開、讀寫文件,獲取文件大小
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * DWORD ReadFileContent(LPSTR szFilePath)
- * 功能 獲取文件大小
- * 讀取文件內容,並以16進制的形式打印出來
- * 參數 LPSTR szFilePath
- * 文件路徑
- **************************************/
- DWORD ReadFileContent(LPSTR szFilePath)
- {
- //文件大小
- HANDLE hFileRead;
- //保存文件大小
- LARGE_INTEGER liFileSize;
- //成功讀取的文件數據大小
- DWORD dwReadedSize;
- //累加計算已經讀取數據的大小
- LONGLONG liTotalRead = 0;
- //文件數據緩存
- BYTE lpFileDataBuffer[32];
- //打開已經存在的文件,讀取內容。
- hFileRead = CreateFile(szFilePath,// 要打開的文件名
- GENERIC_READ, // 以讀方式打開
- FILE_SHARE_READ, // 可共享讀
- NULL, // 默認安全設置
- OPEN_EXISTING, // 只打開已經存在的文件
- FILE_ATTRIBUTE_NORMAL, // 常規文件屬性
- NULL); // 無模板
- //打開文件是否成功。
- if(hFileRead==INVALID_HANDLE_VALUE)
- {
- printf("打開文件失敗:%d",GetLastError());
- }
- if(!GetFileSizeEx(hFileRead,&liFileSize))
- {
- printf("獲取文件大小失敗:%d",GetLastError());
- }
- else
- {
- printf("文件大小為:%d/n",liFileSize.QuadPart);
- }
- //循環讀取並打印文件內容
- while(TRUE)
- {
- DWORD i;
- if(!ReadFile(hFileRead, //讀文件的句柄
- lpFileDataBuffer, //存儲讀取的文件內容
- 32, //讀的大小(字節)
- &dwReadedSize, //實際讀取的大小
- NULL)) //不使用Overlapped
- {
- printf("讀文件錯誤:%d/n",GetLastError());
- break;
- }
- printf("讀取了%d字節,文件內容是:",dwReadedSize);
- for(i=0; i<dwReadedSize; i++)
- {
- printf("0x%x ",lpFileDataBuffer[i]);
- }
- printf("/n");
- liTotalRead += dwReadedSize;
- if(liTotalRead == liFileSize.QuadPart)
- {
- printf("讀文件結束/n");
- break;
- }
- }
- CloseHandle(hFileRead);
- return 0;
- }
- /* ************************************
- * SaveDataToFile
- * 功能 將數據存儲到文件末尾
- * 參數 LPSTR szFilePath 文件路徑
- * LPVOID lpData 需存儲的數據
- * DWORD dwDataSize 數據大小(字節)
- **************************************/
- DWORD SaveDataToFile(
- LPSTR szFilePath,
- LPVOID lpData,
- DWORD dwDataSize)
- {
- //文件句柄
- HANDLE hFileWrite;
- //成功寫入的數據大小
- DWORD dwWritedDateSize;
- //打開已經存在的文件,讀取內容。
- hFileWrite = CreateFile(szFilePath, // 要打開的文件名
- GENERIC_WRITE, // 以寫方式打開
- 0, // 可共享讀
- NULL, // 默認安全設置
- OPEN_ALWAYS, // 打開已經存在的文件,沒用則創建
- FILE_ATTRIBUTE_NORMAL, // 常規文件屬性
- NULL); // 無模板
- //判斷是否打開成功
- if(hFileWrite==INVALID_HANDLE_VALUE)
- {
- printf("打開文件失敗:%d/n",GetLastError());
- }
- //設置文件指針到文件尾
- SetFilePointer(hFileWrite,0,0,FILE_END);
- //將數據寫入文件
- if(!WriteFile(hFileWrite,lpData,dwDataSize,&dwWritedDateSize,NULL))
- {
- printf("寫文件失敗:%d/n",GetLastError());
- }
- else
- {
- printf("寫文件成功,寫入%d字節。/n",dwWritedDateSize);
- }
- CloseHandle(hFileWrite);
- return 0;
- }
- /* ************************************
- * int main(void)
- * 功能 演示使用SaveDataToFile和ReadFileContent函數
- **************************************/
- int main(void)
- {
- LPSTR szFileData = "這是一個例子";
- SaveDataToFile("C://show.txt",szFileData,lstrlen(szFileData));
- ReadFileContent("C://show.txt");
- return 0;
- }
4.3.2 創建、打開、讀寫文件,獲取文件大小
在Windows系統中,創建和打開文件都是使用API函數CreateFile,CreateFile通過指定不同的參數來表示是新建一個文件,打開已經存在的文件,還是重新建立文件等。讀寫文件最為直接的方式是使用ReadFile和WriteFile函數,也可以使用文件鏡像,獲取文件大小一般使用GetFileSize函數,也可以使用GetFileAttributesEx等函數(在4.3.7節介紹)。讀寫文件、獲取文件大小之前都需要使用CreateFile創建或打開的文件,獲得文件句柄。
在文件操作中,文件句柄是一個關鍵的概念。文件句柄惟一標識了一個文件,ReadFile、
WriteFile、GetFileSize等函數是使用文件句柄作為參數來表示,用戶需要讀、寫、獲取大小的文件是哪一個文件。在對文件進行操作前,都必須要使用CreateFile獲得文件句柄。
1.關鍵API
(1)CreateFile
CreateFile是文件操作中最主要的一個函數。幾乎所有的文件操作都需要使用到文件句柄。而CreateFile函數為這些操作建立文件句柄。CreateFile函數定義如下:
(2)ReadFile。
ReadFile動能是從文件中讀出數據。需要使用CreateFile所返回的文件句柄。函數原型如下:
(3)WriteFile。
WriteFile函數的功能是將數據寫入到文件中,寫入到文件指針所在的位置,寫入操作完成后,文件指針會移動到寫入的數據之后,函數原型如下:
(4)GetFileSize、GetFileSizeEX.
GetFileSize、GetFileSizeEX的功能是一致的,都是獲取文件大小,函數原型分別如下。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * files.c
- * 4.3.1 刪除、復制、重命名、移動文件
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * int main( int argc, PCHAR argv[] )
- * 功能 應用程序主函數,根據輸入參數
- * 刪除、復制、重命名文件
- *
- * 參數 刪除文件:
- * -d 文件路徑
- * 將文件路徑1的文件復制到文件路徑2:
- * -c 文件路徑1 文件路徑2
- * 將文件路徑1的文件移動、重命名為文件路徑2的文件
- * -m 文件路徑1 文件路徑2
- **************************************/
- int main(int argc, PCHAR argv[])
- {
- LPSTR y="c:/2.bat";
- LPSTR x="c:/1.bat";
- //-d參數,刪除文件。
- if(0==lstrcmp("-d",argv[1]) && argc==3)
- {
- if(!DeleteFile(argv[2]))
- {
- printf("刪除文件錯誤:%x/n",GetLastError());
- }
- else
- {
- printf("刪除成功!/n");
- }
- }
- //-c參數,復制文件。
- //如果文件存在,詢問用戶是否覆蓋
- else if(0==lstrcmp("-c",argv[1]) && argc==4)
- {
- //復制,不覆蓋已經存在的文件
- if(!CopyFile(argv[2],argv[3],TRUE))
- {
- //LastError == 0x50,文件存在。
- if(GetLastError() == 0x50)
- {
- printf("文件%s已經存在,是否覆蓋?y/n:",argv[3]);
- if('y'==getchar())
- {
- //復制,覆蓋已經存在的文件。
- if(!CopyFile(argv[2],argv[3],FALSE))
- {
- printf("復制文件錯誤,%d/n",GetLastError());
- }
- else
- {
- printf("復制成功!/n");
- }
- }
- else
- {
- return 0;
- }
- }
- }
- else
- {
- printf("復制成功!/n");
- }
- }
- //-m參數,移動、重命名文件。
- else if(0==lstrcmp("-m",argv[1]) && argc==4)
- {
- if(!MoveFile( x,y))
- {
- printf("移動文件錯誤:%d/n",GetLastError());
- }
- else
- {
- printf("移動文件成功!/n");
- }
- }
- else
- {
- printf("參數錯誤!/n");
- }
- }
4.3.3 創建目錄
編程實現創建目錄是非常簡單的,只要使用API函數CreateDirectory即可。
1. 關鍵API
(1) Createdirectory
函數原型如下:
◇參數
lpPathName:輸入參數,所要創建的目錄名或路徑。
lpSecurityAttributes:輸入參數,設置為NULL。
◇返回值
返回BOOL值,表示是否成功。
◇使用說明
如果程序返回失敗,可以使用GetLastError函數獲取錯誤信息。可能的值包括ERROR
ALREADY EXISTS(文件夾已經存在)和ERROR PATH NOT FOUND(路徑不存在)。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * dir.c
- * 4.3.3 創建目錄
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * int main(void)
- * 功能 演示使用CreateDirectory創建目錄
- **************************************/
- int main(void)
- {
- //在程序的當前目錄下創建“sub_dir”子目錄
- LPSTR szDirPath = "sub_dir";
- if (!CreateDirectory(szDirPath, NULL))
- {
- printf("創建目錄 %s 錯誤。/n",szDirPath);
- return 1;
- }
- //在C盤下創建目錄“example_dir”
- szDirPath = "C://example_dir";
- if (!CreateDirectory(szDirPath, NULL))
- {
- printf("創建目錄 %s 錯誤。/n",szDirPath);
- return 1;
- }
- printf("成功/n");
- return 0;
- }
4.3.4 獲取程序所在的目錄、程序模塊路徑,獲取和設置當前目錄
Windows系統提供一組API實現對程序運行時相關目錄的獲取和設置。用戶可以使用
GetCurrentDirectory和SetCurrentDirectory獲取程序的當前目錄,獲取模塊的路徑使用
GetModuleFileName,如果以NULL參數調用GetModuleFileName,將會返回當前模塊的路徑。如果在程序主模塊(exe)中獲取當前模塊路徑,便可以從當前模塊的路徑中提取出程序運行時所在的路徑。
1.關鍵API
(1)GetCurrentDirectory。
獲取進程的當前目錄,函數原型如下:
(2)SetCurrentDirectory。
設置進程的當前目錄,函數原型如下:
(3)GetModuleFileName。
獲取模塊文件名,當第一個參數為NULL時獲取當前模塊路徑,函數原型如下:
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * cur_mod_dir.c
- * 4.3.4 獲取當前目錄、獲取程序所在的目錄、獲取模塊路徑
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * int main(void)
- * 功能 演示使用設置獲取當前路徑
- * 演示獲取模塊路徑
- **************************************/
- int main(void)
- {
- //用於存儲當前路徑
- CHAR szCurrentDirectory[MAX_PATH];
- //用於存儲模塊路徑
- CHAR szMoudlePath[MAX_PATH];
- //Kernel32文件名與句柄
- LPSTR szKernel32 = "kernel32.dll";
- HMODULE hKernel32;
- //當前路徑的長度,也用於判斷獲取是否成功
- DWORD dwCurDirPathLen;
- //獲取進程當前目錄
- dwCurDirPathLen =
- GetCurrentDirectory(MAX_PATH,szCurrentDirectory);
- if(dwCurDirPathLen == 0)
- {
- printf("獲取當前目錄錯誤。/n");
- return 0;
- }
- printf("進程當前目錄為 %s /n",szCurrentDirectory);
- //將進程當前目錄設置為“C:/”
- lstrcpy(szCurrentDirectory, "C://");
- if(!SetCurrentDirectory(szCurrentDirectory))
- {
- printf("設置當前目錄錯誤。/n");
- return 0;
- }
- printf("已經設置當前目錄為 %s /n",szCurrentDirectory);
- //在當前目錄下創建子目錄“current_dir”
- //運行完成后C:盤下將出現文件夾“current_dir”
- CreateDirectory("current_dir", NULL);
- //再次獲取系統當前目錄
- dwCurDirPathLen =
- GetCurrentDirectory(MAX_PATH,szCurrentDirectory);
- if(dwCurDirPathLen == 0)
- {
- printf("獲取當前目錄錯誤。/n");
- return 0;
- }
- printf("GetCurrentDirectory獲取當前目錄為 %s /n",
- szCurrentDirectory);
- //使用NULL參數,獲取本模塊的路徑。
- if(!GetModuleFileName(NULL,szMoudlePath,MAX_PATH))
- {
- printf("獲取模塊路徑錄錯誤。/n");
- return 0;
- }
- printf("本模塊路徑 %s /n",szMoudlePath);
- //獲取Kernel32.dll的模塊句柄。
- hKernel32 = LoadLibrary(szKernel32);
- //使用Kernel32.dll的模塊句柄,獲取其路徑。
- if(!GetModuleFileName(hKernel32,szMoudlePath,MAX_PATH))
- {
- printf("獲取模塊路徑錯誤。/n");
- return 0;
- }
- printf("kernel32模塊路徑 %s /n",szMoudlePath);
- return 0;
- }
4.3.5 查找文件、遍歷指定目錄下的文件和子目錄
Windows API中,有一組專門的函數和結構,用於遍歷目錄,它們是FindFirstFile函數、
FindNextFile函數和WIN32_FIND_DATA結構。使用FindFirstFile和FindNextFile函數並與do-while循環結合,可以完成遍歷目錄的任務,詳見實例4-10。
值得一提的是,FindFirstFile輸入參數的路徑需使用通配符,也就是用戶可以根據一些條件來對查找的文件作簡單的過濾。實例4-10講解查找特定目錄下的所有文件和文件夾。讀者可根據自己的需要,指定查找文件的條件。
1.關鍵API
(1)FindFirstFile。
查找第一個目錄或文件,獲取查找句柄,函數原型如下:
(2)FindNextFile
對文件、文件夾進行循環查找,函數原型如下:
2.關鍵結構
WIN32_FIND_DATA結構用於表示找到的文件,結構中包括文件、目錄的名字,創建、最后訪問和最后寫入時間,文件大小、文件屬性等。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * sub_dir.c
- * 4.3.5 遍歷目錄下的文件和子目錄
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* ************************************
- * DWORD EnumerateFileInDrectory(LPSTR szPath)
- * 功能 遍歷目錄下的文件和子目錄,將顯示文件的
- * 文件和文件夾隱藏、加密的屬性
- * 參數 LPTSTR szPath,為需遍歷的路徑
- * 返回值 0代表執行完成,1代碼發生錯誤
- **************************************/
- DWORD EnumerateFileInDrectory(LPSTR szPath)
- {
- WIN32_FIND_DATA FindFileData;
- HANDLE hListFile;
- CHAR szFilePath[MAX_PATH];
- //構造代表子目錄和文件夾路徑的字符串,使用通配符“*”
- lstrcpy(szFilePath, szPath);
- //注釋的代碼可以用於查找所有以“.txt結尾”的文件。
- //lstrcat(szFilePath, "//*.txt");
- lstrcat(szFilePath, "//*");
- //查找第一個文件/目錄,獲得查找句柄
- hListFile = FindFirstFile(szFilePath,&FindFileData);
- //判斷句柄
- if(hListFile==INVALID_HANDLE_VALUE)
- {
- printf("錯誤:%d",GetLastError());
- return 1;
- }
- else
- {
- do
- {
- /* 如果不想顯示代表本級目錄和上級目錄的“.”和“..”,
- 可以使用注釋部分的代碼過濾。
- if(lstrcmp(FindFileData.cFileName,TEXT("."))==0||
- lstrcmp(FindFileData.cFileName,TEXT(".."))==0)
- {
- continue;
- }
- */
- //打印文件名、目錄名
- printf("%s/t/t",FindFileData.cFileName);
- //判斷文件屬性,加密文件或文件夾
- if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_ENCRYPTED)
- {
- printf("<加密> ");
- }
- //判斷文件屬性,隱藏文件或文件夾
- if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
- {
- printf("<隱藏> ");
- }
- //判斷文件屬性,目錄
- if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
- {
- printf("<DIR> ");
- }
- //讀者可根據文件屬性表中的內容自行添加判斷文件屬性。
- printf("/n");
- }
- while(FindNextFile(hListFile, &FindFileData));
- }
- return 0;
- }
- /* ************************************
- * int main(int argc, PCHAR argv[])
- * 功能 調用ListFileInDrectory
- * 遍歷目錄下的文件和子目錄
- * 參數 argv[1]為需遍歷的路徑,如果為空則獲取
- * 當前路徑
- **************************************/
- int main(int argc, PCHAR argv[])
- {
- if(argc == 2)
- {
- EnumerateFileInDrectory(argv[1]);
- }
- else
- {
- CHAR szCurrentPath[MAX_PATH];
- GetCurrentDirectory(MAX_PATH,szCurrentPath);
- EnumerateFileInDrectory(szCurrentPath);
- }
- return 0;
- }
4.3.6 遞歸遍歷目錄樹
在實例4-10的基礎上稍加改造,進行循環遞歸調用,采用樹形結構深度遍歷的方法。可以遍歷指定目錄中的所有文件、包括子目錄中的文件。代碼實現如實例4-11所示。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * tree.c
- * 4.3.6 遞歸遍歷目錄樹
- * 2007年10月
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* 預處理申明 */
- #pragma comment (lib, "User32.lib")
- /* 函數申明 */
- DWORD ListAllFileInDrectory(LPSTR szPath);
- /* 全局變量 */
- //記錄所有的文件和目錄數
- DWORD dwTotalFileNum = 0;
- /* ************************************
- * DWORD ListAllFileInDrectory(LPSTR szPath)
- * 功能 遍歷目錄及所有子目錄,打印路徑
- *
- * 參數 LPTSTR szPath,為需遍歷的目錄
- *
- * 返回值 0代表執行完成,1代碼發生錯誤
- **************************************/
- DWORD ListAllFileInDrectory(LPSTR szPath)
- {
- CHAR szFilePath[MAX_PATH];
- WIN32_FIND_DATA FindFileData;
- HANDLE hListFile;
- CHAR szFullPath[MAX_PATH];
- //構造代表子目錄和文件夾路徑的字符串,使用通配符“*”
- lstrcpy(szFilePath, szPath);
- lstrcat(szFilePath, "//*");
- //查找第一個文件/目錄,獲得查找句柄
- hListFile = FindFirstFile(szFilePath,&FindFileData);
- if(hListFile==INVALID_HANDLE_VALUE)
- {
- printf("錯誤:%d",GetLastError());
- return 1;
- }
- else
- {
- do
- {
- // 過濾“.”和“..”,不需要遍歷
- if(lstrcmp(FindFileData.cFileName,TEXT("."))==0||
- lstrcmp(FindFileData.cFileName,TEXT(".."))==0)
- {
- continue;
- }
- //構造成全路徑
- wsprintf(szFullPath,"%s//%s",
- szPath,FindFileData.cFileName);
- dwTotalFileNum++;
- //打印
- printf("/n%d/t%s/t",dwTotalFileNum,szFullPath);
- //如果是目錄,則遞歸調用,列舉下級目錄
- if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
- {
- printf("<DIR>");
- ListAllFileInDrectory(szFullPath);
- }
- }
- while(FindNextFile(hListFile, &FindFileData));
- }
- return 0;
- }
- /* ************************************
- * int main(int argc, PCHAR argv[])
- * 功能 調用ListAllFileInDrectory
- * 遍歷目錄下的文件和子目錄
- *
- * 參數 argv[1]為需遍歷的路徑,如果為空則獲取
- * 當前路徑
- *
- * 2007年10月
- *
- **************************************/
- int main(int argc, PCHAR argv[])
- {
- if(argc == 2)
- {
- ListAllFileInDrectory(argv[1]);
- }
- else
- {
- CHAR szCurrentPath[MAX_PATH];
- GetCurrentDirectory(MAX_PATH,szCurrentPath);
- ListAllFileInDrectory(szCurrentPath);
- }
- return 0;
- }
4.3.7 獲取、設置文件屬性和時間
為了獲取文件屬性,用戶可以使用GetFileAttributes與GetFileAttributesEx函數。
GetFileAttributesEx函數除了返回文件屬性外,還返回文件時間信息、文件大小等。
GetFileAttributesEx將返回結果保存在WIN32_FILE_ATTRIBUTE DATA結構中。
獲取的文件時間是以FILETIME格式存在的,如果要正確顯示,還需要對其時區進行調整,調整為本地時區,然后轉換為系統時間格式,便於顯示。
前面在獲取文件大小時已經介紹,NTFS文件系統使用了64位數據來表示文件大小。因為32位的數據最多只能表示4GB的大小。Windows將其分為了高32位和低32位,兩個都需要使用到,這一點尤其要在對大於4GB的文件操作時注意。
1.關鍵API
(1)GetFileAttributeS。
獲取文件或目錄的屬牲-函數原型如下:
(2)GetFileAttributesEx。
獲取文件或目錄的屬性、時間、大小,以WIN32_FILE ATTRIBUTE_DATA結構的形式返回
結果,函數原型如下:
(3) SetFileAttributes.
設置文件或目錄的屬性,函數原型如下:
(4)FileTimeToLocalFileTime。
把文件時間轉換為本地的文件時間,函數原型如下:
5)FileTimeToSystemTime
將文件時間轉換為系統時間(SYSTEMTIME格式),便於顯示,函數原型如下:
2.關鍵數據結構
(1) FILETIM。
此結構用最小的數據量表示的時間,但是不便於用戶查看和顯示。通過API獲取的系統時間都是這種格式的。如果要使用顯示,可以使用FileTimeToSystemTime轉換為便於顯示的SYSTEMTIME結構。
(2) SYSTEMTIME。
此結構使用了較為直觀的方式表示時間。
(3) WIN32_FILE ATTRIBUTE_DATA。
GetFileAttributesEx使用這個結構表示返回結果,包括文件屬性、文件創建時間、文件最后訪問時間、文件最后寫入時間和文件大小。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * attr.c
- * 4.3.7 獲取、設置文件屬性和時間
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* 函數申明 */
- DWORD ShowFileTime(PFILETIME lptime);
- DWORD ShowFileSize(DWORD dwFileSizeHigh,DWORD dwFileSizeLow);
- DWORD ShowFileAttrInfo(DWORD dwAttribute);
- DWORD SetFileHiddenAndReadonly(LPSTR szFileName);
- /* ************************************
- * DWORD ShowFileAttributes(LPSTR szPath)
- * 功能 獲取並顯示文件屬性,
- * 調用ShowFileTime、ShowFileSize和
- * ShowFileAttrInfo函數
- *
- * 參數 LPTSTR szPath,獲取並顯示此文件的屬性
- *
- * 返回值 0代表執行完成,1代碼發生錯誤
- **************************************/
- DWORD ShowFileAttributes(LPSTR szPath)
- {
- //文件屬性結構
- WIN32_FILE_ATTRIBUTE_DATA wfad;
- printf("文件:%s/n",szPath);
- //獲取文件屬性
- if(!GetFileAttributesEx(szPath,
- GetFileExInfoStandard,
- &wfad))
- {
- printf("獲取文件屬性錯誤:%d/n",GetLastError());
- return 1;
- }
- //顯示相關時間
- printf("創建時間:/t");
- ShowFileTime(&(wfad.ftCreationTime));
- printf("最后訪問時間:/t");
- ShowFileTime(&(wfad.ftLastAccessTime));
- printf("最后修改時間:/t");
- ShowFileTime(&(wfad.ftLastWriteTime));
- //顯示文件大小
- ShowFileSize(wfad.nFileSizeHigh,wfad.nFileSizeLow);
- //顯示文件屬性
- ShowFileAttrInfo(wfad.dwFileAttributes);
- return 0;
- }
- /* ************************************
- * DWORD ShowFileAttrInfo(DWORD dwAttribute)
- * 功能 打印將文件屬性
- *
- * 參數 DWORD dwAttribute,文件屬性
- *
- * 返回值 0
- **************************************/
- DWORD ShowFileAttrInfo(DWORD dwAttribute)
- {
- //依次判斷屬性,並顯示。
- printf("文件屬性:/t");
- if(dwAttribute&FILE_ATTRIBUTE_ARCHIVE)
- printf("<ARCHIVE> ");
- if(dwAttribute&FILE_ATTRIBUTE_COMPRESSED)
- printf("<壓縮> ");
- if(dwAttribute&FILE_ATTRIBUTE_DIRECTORY)
- printf("<目錄> ");
- if(dwAttribute&FILE_ATTRIBUTE_ENCRYPTED)
- printf("<加密> ");
- if(dwAttribute&FILE_ATTRIBUTE_HIDDEN)
- printf("<隱藏> ");
- if(dwAttribute&FILE_ATTRIBUTE_NORMAL)
- printf("<NORMAL> ");
- if(dwAttribute&FILE_ATTRIBUTE_OFFLINE)
- printf("<OFFLINE> ");
- if(dwAttribute&FILE_ATTRIBUTE_READONLY)
- printf("<只讀> ");
- if(dwAttribute&FILE_ATTRIBUTE_SPARSE_FILE)
- printf("<SPARSE> ");
- if(dwAttribute&FILE_ATTRIBUTE_SYSTEM)
- printf("<系統文件> ");
- if(dwAttribute&FILE_ATTRIBUTE_TEMPORARY)
- printf("<臨時文件> ");
- printf("/n");
- return 0;
- }
- /* ************************************
- * DWORD ShowFileSize(DWORD dwFileSizeHigh, DWORD dwFileSizeLow)
- * 功能 打印文件大小信息
- *
- * 參數 DWORD dwFileSizeHigh,文件大小高32位
- * DWORD dwFileSizeLow,文件大小低32位
- *
- * 返回值 0
- **************************************/
- DWORD ShowFileSize(DWORD dwFileSizeHigh, DWORD dwFileSizeLow)
- {
- ULONGLONG liFileSize;
- liFileSize = dwFileSizeHigh;
- //高們移動32位
- liFileSize <<= sizeof(DWORD)*8;
- liFileSize += dwFileSizeLow;
- printf("文件大小:/t%I64u 字節/n",liFileSize);
- return 0;
- }
- /* ************************************
- *DWORD ShowFileTime(PFILETIME lptime)
- * 功能 輪換文件時間,將打印
- *
- * 參數 PFILETIME lptime,指向文件時間的指針
- *
- * 返回值 0
- **************************************/
- DWORD ShowFileTime(PFILETIME lptime)
- {
- //文件時間結構
- FILETIME ftLocal;
- //系統時間結構
- SYSTEMTIME st;
- //調整為系統所在時區的時間
- FileTimeToLocalFileTime(
- lptime,
- &ftLocal
- );
- //將文件時間轉換為SYSTEMTIME格式,便於顯示。
- FileTimeToSystemTime(
- &ftLocal,
- &st
- );
- //顯示時間信息字符串
- printf("%4d年%.2d月%#02d日,%.2d:%.2d:%.2d/n",
- st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
- return 0;
- }
- /* ************************************
- * DWORD SetFileHiddenAndReadonly(LPSTR szFileName)
- * 功能 將指定的文件設置為隱藏和只讀
- *
- * 參數 LPSTR szFileName,文件路徑
- *
- * 返回值 0
- **************************************/
- DWORD SetFileHiddenAndReadonly(LPSTR szFileName)
- {
- //獲取原來的文件屬性
- DWORD dwFileAttributes = GetFileAttributes(szFileName);
- //將只讀和隱藏屬性附加到原來的文件屬性上
- dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
- dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
- //設置文件屬性,並判斷是否成功。
- if(SetFileAttributes(szFileName, dwFileAttributes))
- {
- printf("文件%s的隱藏和屬性設置成功/n",szFileName);
- }
- else
- {
- printf("屬性設置; %d/n",GetLastError());
- }
- return 0;
- }
- /* ************************************
- * int main(int argc, PCHAR argv[])
- * 功能 設置和獲取文件屬性等
- *
- * 參數 顯示第一個參數指定文件的屬性、時間、大小
- * 將第二個參數的屬性設置為隱藏、只讀。
- *
- * 返回值 0代表執行完成,1代碼發生錯誤
- **************************************/
- int main(int argc, PCHAR argv[])
- {
- if(argc != 3)
- {
- printf("請輸入參數/n");
- printf("顯示第一個參數指定文件的屬性、時間、大小;/n");
- printf("將第二個參數的屬性設置為隱藏、只讀。");
- return 1;
- }
- ShowFileAttributes(argv[1]);
- SetFileHiddenAndReadonly(argv[2]);
- return 0;
- }
4.4 內存映射文件
本節介紹Mapping File、文件句柄等較高級操作。本節將通過以下幾個實例來講解高級文件系統操作的API。
◇ 使用Mapping File提高文件讀寫的效率。
◇通過Mapping File在進程間共享內存。
◇通過文件句柄獲得文件路徑。
4.4.1 使用Mapping File提高文件讀寫的效率
文件映射( mapping)是一種在將文件內容映射到進程的虛擬地址空間的技術。視圖(View)是一段虛擬地址空間,進程可以通過View來存取文件的內容,視圖是一段內存,可以使用指針來操作視圖。使用的文件映射之后,讀寫文件就如同對讀寫內存一樣簡單。在使用文件映射時需要創建映射對象,映射對象分為命名的和未命名的。映射對象還存取權限。
使用文件映射至少有3個好處,一是因為文件是存儲於硬盤上的,而文件視圖是一段內存,使用文件映射操作時更方便;二是效率更高;三是可以在不同的進程間共享數據。
文件映射依賴於系統虛擬內存管理的分頁機制。
本節將演示如何使用文件映射,下一節將演示如何使用文件映射來進行內存共享。
1. 關鍵API
(1)GetSystemInfo
獲取系統信息,在實例 4-14中用於獲取系統內存分配粒度。有關內存分配粒度的概念參見第5章和虛擬內存管理相關章節。
(2)CreateFileMapping。
創建mapping對象,函數原型如下
(3)MapViewOfFile。
創建視圖,將文件mapping映射到當前進程內存虛擬地址空間。函數原型如下:
(4)FlushViewOfFile。
將視圖中的文件數據寫入到磁盤上。調用此參數后,對映射視圖的內存操作將會及時反映到硬件中的文件。函數原型如下:
◇使用說明
如果不調用此函數,數據最終也會寫回到硬盤,調用此函數后,數據會立刻寫回到硬盤。
(5) FillMemory、CopyMemory。內存操作函數,分別為填充內存和復制內存,詳見第5章
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * file_map.c
- * 4.4.1 使用Mapping File提高文件讀寫的效率
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- /* 預處理申明 */
- #define BUFFSIZE 1024 // 內存大小
- #define FILE_MAP_START 0x28804 // 文件映射的起始的位置
- /* 全局變量 */
- LPTSTR lpcTheFile = TEXT("test.dat"); // 文件名
- /* ************************************
- * int main(void)
- * 功能 演示使用文件mapping
- *
- * 參數 無
- *
- * 返回值 0代表執行完成,1代表發生錯誤
- **************************************/
- int main(void)
- {
- HANDLE hMapFile; // 文件內存映射區域的句柄
- HANDLE hFile; // 文件的句柄
- DWORD dBytesWritten; // 寫入的字節數
- DWORD dwFileSize; // 文件大小
- DWORD dwFileMapSize; // 文件映射的大小
- DWORD dwMapViewSize; // 視圖(View)的大小
- DWORD dwFileMapStart; // 文件映射視圖的起始位置
- DWORD dwSysGran; // 系統內存分配的粒度
- SYSTEM_INFO SysInfo; // 系統信息
- LPVOID lpMapAddress; // 內在映射區域的起始位置
- PCHAR pData; // 數據
- INT i; // 循環變量
- INT iData;
- INT iViewDelta;
- BYTE cMapBuffer[32]; // 存儲從mapping中計出的數據
- // 創建一個文件
- hFile = CreateFile(lpcTheFile,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- //判斷文件是否創建成功
- if (hFile == INVALID_HANDLE_VALUE)
- {
- printf("CreateFile error/n",GetLastError);
- return 1;
- }
- // 依次寫入整數,一共寫入65535個整數
- // 在32位平台下,大小為65535*32
- for (i=0; i<65535; i++)
- {
- WriteFile (hFile, &i, sizeof (i), &dBytesWritten, NULL);
- }
- // 查看寫入完成后的文件大小
- dwFileSize = GetFileSize(hFile, NULL);
- printf("文件大小: %d/n", dwFileSize);
- //獲取系統信息,內存分配粒度
- //獲取分配粒度,進行下面的幾個計算,
- //目的是為了映射的數據與系統內存分配粒度對齊,提高內存訪問效率
- GetSystemInfo(&SysInfo);
- dwSysGran = SysInfo.dwAllocationGranularity;
- //計算mapping的起始位置
- dwFileMapStart = (FILE_MAP_START / dwSysGran) * dwSysGran;
- // 計算mapping view的大小
- dwMapViewSize = (FILE_MAP_START % dwSysGran) + BUFFSIZE;
- // 計算mapping的大小
- dwFileMapSize = FILE_MAP_START + BUFFSIZE;
- // 計算需要讀取的數據的偏移
- iViewDelta = FILE_MAP_START - dwFileMapStart;
- // 創建File mapping
- hMapFile = CreateFileMapping( hFile, // 需要映射的文件的句柄
- NULL, // 安全選項:默認
- PAGE_READWRITE, // 可讀,可寫
- 0, // mapping對象的大小,高位
- dwFileMapSize, // mapping對象的大小,低位
- NULL); // mapping對象的名字
- if (hMapFile == NULL)
- {
- printf("CreateFileMapping error: %d/n", GetLastError() );
- return 1;
- }
- // 映射view
- lpMapAddress = MapViewOfFile(hMapFile, // mapping對象的句柄
- FILE_MAP_ALL_ACCESS, // 可讀,可寫
- 0, // 映射的文件偏移,高32位
- dwFileMapStart, // 映射的文件偏移,低32位
- dwMapViewSize); // 映射到View的數據大小
- if (lpMapAddress == NULL)
- {
- printf("MapViewOfFile error: %d/n", GetLastError());
- return 1;
- }
- printf ("文件map view相對於文件的起始位置: 0x%x/n",
- dwFileMapStart);
- printf ("文件map view的大小:0x%x/n", dwMapViewSize);
- printf ("文件mapping對象的大小:0x%x/n", dwFileMapSize);
- printf ("從相對於map view 0x%x 字節的位置讀取數據,", iViewDelta);
- // 將指向數據的指針偏移,到達我們關心的地方
- pData = (PCHAR) lpMapAddress + iViewDelta;
- // 讀取數據,賦值給變量
- iData = *(PINT)pData;
- // 顯示讀取的數據
- printf ("為:0x%.8x/n", iData);
- // 從mapping中復制數據,32個字節,並打印
- CopyMemory(cMapBuffer,lpMapAddress,32);
- printf("lpMapAddress起始的32字節是:");
- for(i=0; i<32; i++)
- {
- printf("0x%.2x ",cMapBuffer[i]);
- }
- // 將mapping的前32個字節用0xff填充
- FillMemory(lpMapAddress,32,(BYTE)0xff);
- // 將映射的數據寫回到硬盤上
- FlushViewOfFile(lpMapAddress,dwMapViewSize);
- printf("/n已經將lpMapAddress開始的32字節使用0xff填充。/n");
- // 關閉mapping對象
- if(!CloseHandle(hMapFile))
- {
- printf("/nclosing the mapping object error %d!",
- GetLastError());
- }
- //關閉文件
- if(!CloseHandle(hFile))
- {
- printf("/nError %ld occurred closing the file!",
- GetLastError());
- }
- return 0;
- }
4.4.2 通過Mapping File在進程間傳遞和共享數據
進程間通信、共享數據有很多種方法,文件映射是常用的一種方法。因為mapping對象
系統中是全局的,一個進程創建的Mapping對象可以從另外一個進程中打開,映射視圖就
進程間共享的內存了。一般在共享的內存數據量比較大時,選擇使用文件映射進行共享。
1.關鍵API
(1)OpenFileMapping。
打開已經存在的文件映射,函數原型如下:
(2) UnmapViewO伍ile。
取消文件映射,函數原型如下:
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * pro_s1.c
- * 4.4.2 通過Mapping File在進程間共享內存
- **************************************/
- /* 頭文件 */
- #include <windows.h>
- #include <stdio.h>
- #include <conio.h>
- /* 預處理申明 */
- #define BUF_SIZE 256
- /* 全局變量 */
- LPTSTR szName = TEXT("SharedFileMappingObject");
- LPTSTR szMsg = TEXT("進程的消息");
- /* ************************************
- * int main(void)
- * 功能 演示文件mapping共享內存,寫入數據到共享內存
- *
- * 參數 無
- *
- * 返回值 0代表執行完成,代表發生錯誤
- **************************************/
- void main(int argc, PCHAR argv[])
- {
- //文件映射句柄
- HANDLE hMapFile;
- //共享數據緩沖區指針
- LPTSTR pBuf;
- //創建命名的文件映射,不代表任務硬盤上的文件
- hMapFile = CreateFileMapping(
- INVALID_HANDLE_VALUE,
- NULL,
- PAGE_READWRITE,
- 0,
- BUF_SIZE,
- szName);
- if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE)
- {
- printf("CreateFileMapping error: %d/n", GetLastError());
- return;
- }
- //創建View
- pBuf = (LPTSTR) MapViewOfFile(hMapFile,
- FILE_MAP_ALL_ACCESS,
- 0,
- 0,
- BUF_SIZE);
- if (pBuf == NULL)
- {
- printf("MapViewOfFile error %d/n", GetLastError());
- return;
- }
- //將共享數據復制到文件映射中,如果運行時輸入了參數則使用參數
- if(argc==1)
- {
- CopyMemory((PVOID)pBuf, szMsg, strlen(szMsg));
- }
- else
- {
- DWORD dwCopyLen = (lstrlen(argv[1])<BUF_SIZE) ? lstrlen(argv[1]): BUF_SIZE;
- CopyMemory((PVOID)pBuf, argv[1], dwCopyLen);
- }
- printf("運行程序,完成運行后,按任意鍵退出。");
- _getch();
- //取消映射,退出
- UnmapViewOfFile(pBuf);
- CloseHandle(hMapFile);
- }
.實例4-15讀取並顯示共享數據
本實例代碼從實例4-14共享的Mapping對象中讀取數據,並將數據顯示出來。實例4-14與實例4-15是兩個進程,實現了進程間數據的共享。這兩個進程可以通過各自的指針,對同一段內存進行讀寫。
本實例先打開指定對象名的Mapping對象(與實例4-14中使用的CreateFileMapping創建的Mapping對象需有一樣的對象名),然后創建視圖,從視圖中讀取數據。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * pro_s2.c
- * 4.4.2 通過Mapping File在進程間共享內存
- **************************************/
- #include <windows.h>
- #include <stdio.h>
- #include <conio.h>
- /* 預處理申明*/
- #pragma comment (lib, "User32.lib")
- #define BUF_SIZE 256
- /* 全局變量 */
- TCHAR szName[]=TEXT("SharedFileMappingObject");
- /* ************************************
- * int main(void)
- * 功能 演示文件mapping共享內存,從共享數據中讀信息
- *
- * 參數 無
- *
- * 返回值 0代表執行完成,代表發生錯誤
- **************************************/
- void main()
- {
- HANDLE hMapFile;
- LPTSTR pBuf;
- //打開文件mapping
- hMapFile = OpenFileMapping(
- FILE_MAP_ALL_ACCESS,
- FALSE,
- szName);
- if (hMapFile == NULL)
- {
- printf("OpenFileMapping error: %d./n", GetLastError());
- return;
- }
- //映射
- pBuf = MapViewOfFile(hMapFile,
- FILE_MAP_ALL_ACCESS,
- 0,
- 0,
- BUF_SIZE);
- if (pBuf == NULL)
- {
- printf("MapViewOfFile error %d/n", GetLastError());
- return;
- }
- //消息得到的共享數據
- MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);
- //取消mapping,關閉句柄,返回
- UnmapViewOfFile(pBuf);
- CloseHandle(hMapFile);
- }
4.4.3 通過文件句柄獲得文件路徑
Windows系統提供了一個名為GetMappedFileName的API函數,這個函數可以實現從
mapping對象的句柄得到被映射文件的路徑。但是路徑是以設備名的形式給出的,如類似於
“/Device/HarddiskVolume3/1.TXT”的形式。
將設備名轉換為路徑名需要使用到一個API函數-QueryDosDevice,這個函數可以將驅動器的根路徑轉換為設備名,然后進行循環比較,可得文件路徑。
1.關鍵API
(1)GetMappedFileName。
從映射對象獲取被映射文件的文件設備名,函數原型如下:
(2)QueryDosDevice
獲取MS_DOS設備名,函數原型如下:
◇返回值返回DWORD值,如果成功,則返回值為lpTargetPath指向字符串的長度,如果失敗為0。
2.實例4-16通過文件句柄獲取文件路徑
GetFileNameFromHandle通過參數輸入的句柄創建mapping對象,然后調用GetMapped
FileName函數,獲得對象所映射的文件的設備名,然后使用QueryDosDevice函數循環判斷
驅動器設備名與路徑的關系,在找到正確的驅動器路徑后,構造好文件路徑並輸出。
- /* ************************************
- *《精通Windows API》
- * 示例代碼
- * handle_path.cpp
- * 4.4.3 通過文件句柄獲取文件路徑
- **************************************/
- /* 頭文件*/
- #include <windows.h>
- #include <stdio.h>
- #include <tchar.h>
- #include <string.h>
- #include <psapi.h>
- /* 預處理申明*/
- #pragma comment (lib, "Psapi.lib")
- #define BUFSIZE 512
- /* 函數申明*/
- BOOL GetFileNameFromHandle(HANDLE hFile) ;
- /* ************************************
- * BOOL GetFileNameFromHandle(HANDLE hFile)
- * 功能 從文件句柄獲取文件路徑
- *
- * 參數 ANDLE hFile,需要獲得路徑的文件句柄
- *
- * 返回值BOOL 是否成功。
- **************************************/
- BOOL GetFileNameFromHandle(HANDLE hFile)
- {
- TCHAR pszFilename[MAX_PATH+1];
- HANDLE hFileMap;
- PVOID pMem;
- // 獲得文件大小,並決斷是否為
- DWORD dwFileSizeHigh = 0;
- DWORD dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
- if( dwFileSizeLow == 0 && dwFileSizeHigh == 0 )
- {
- printf("不能map文件大小為的文件./n");
- return FALSE;
- }
- // 創建mapping對象
- hFileMap = CreateFileMapping(hFile,
- NULL,
- PAGE_READONLY,
- 0,
- 1,
- NULL);
- if (!hFileMap)
- {
- printf("CreateFileMapping error: %d",GetLastError());
- return FALSE;
- }
- pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
- if (!pMem)
- {
- printf("MapViewOfFile error: %d",GetLastError());
- return FALSE;
- }
- // 從mapping對象獲得文件名
- if (0 == GetMappedFileName (GetCurrentProcess(),
- pMem,
- pszFilename,
- MAX_PATH))
- {
- printf("GetMappedFileName error: %d",GetLastError());
- return FALSE;
- }
- // 將設備名轉換為路徑
- TCHAR szTemp[BUFSIZE] = {0};
- if (0 == GetLogicalDriveStrings(BUFSIZE-1, szTemp))
- {
- printf("GetLogicalDriveStrings error: %d",GetLastError());
- return FALSE;
- }
- TCHAR szName[MAX_PATH];
- TCHAR szDrive[3] = {0};
- BOOL bFound = FALSE;
- PTCHAR p = szTemp;
- do
- {
- CopyMemory(szDrive,p,2*sizeof(TCHAR));
- // 通過路徑查找設備名
- if (!QueryDosDevice(szDrive, szName, BUFSIZE))
- {
- printf("QueryDosDevice error: %d",GetLastError());
- return FALSE;
- }
- UINT uNameLen = lstrlen(szName);
- if (uNameLen < MAX_PATH)
- {
- //比較驅動器的設備名與文件設備名是否匹配
- bFound = strncmp(pszFilename, szName,uNameLen) == 0;
- if (bFound)
- {
- //如果匹配,說明已經找到,構造路徑。
- TCHAR szTempFile[MAX_PATH];
- wsprintf(szTempFile,
- TEXT("%s%s"),
- szDrive,
- pszFilename+uNameLen);
- lstrcpy(pszFilename, szTempFile);
- }
- }
- // 循環到下一個NULL
- while (*p++);
- } while (!bFound && *p);
- UnmapViewOfFile(pMem);
- CloseHandle(hFileMap);
- printf("File path is %s/n", pszFilename);
- return TRUE;
- }
- /* ************************************
- * int main()
- * 功能 查找第一個目錄中第一個txt文件
- * 打開文件,並根據文件句柄獲得文件路徑。
- *
- * 參數 未使用
- *
- * 返回值0表示成功,表示失敗。
- **************************************/
- int main()
- {
- HANDLE hFile;
- HANDLE hFind;
- WIN32_FIND_DATA wfd;
- hFind = FindFirstFile("*.txt",&wfd);
- if(hFind == INVALID_HANDLE_VALUE)
- {
- printf("can not find a file");
- return 1;
- }
- //CloseHandle(hFind);
- printf("find %s at current dir/n",wfd.cFileName);
- hFile = CreateFile(wfd.cFileName,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if(hFile == INVALID_HANDLE_VALUE)
- {
- printf("create file error, %d",GetLastError());
- }
- else
- {
- GetFileNameFromHandle(hFile) ;
- }
- CloseHandle(hFile);
- return 0;
- }