7.1.1 內存映射文件
內存映射文件允許將文件映射到一段內存區域,然後直接訪問這段內存區域就能實現對文件的訪問,而不需要通過調用文件讀寫API函數來實現,讀寫文件就跟讀寫一段內存區域一樣高效簡單,而且對內存的更新操作將寫入到文件中。內存映射文件還可以用於實現進程間通信,此時需要使用命名內存映射文件對象。使用內存映射文件首先需要創建一個內存映射文件對象,創建好之後需要將內存映射文件對象映射到進程的視圖,讀寫操作完成後需要釋放視圖。
1. 創建內存映射文件
創建內存映射文件首先需要打開文件句柄,然後調用CreateFileMapping()函數實現:
HANDLE CreateFileMapping(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName
);
l 參數hFile指定將被映射到內存的文件句柄。如果這個參數被設置爲(HANDLE)INVALID_HANDLE_VALUE,系統將從物理內存中分配空間來創建內存映射文件對象。
l 參數lpFileMappingAttributes指定安全屬性,在Win CE7中不支持,設置爲NULL。
l 參數flProtect指定對內存映射文件視圖的保護訪問權限,表7-8列出了可以取的值。
可取值 | 說明 |
PAGE_READONLY | 對映射區域頁面擁有隻讀訪問權限,對頁面的寫操作或執行都將導致訪問錯誤。文件句柄打開時必須指定GENERIC_READ訪問權限。 |
PAGE_READWRITE | 對頁面具有讀寫訪問權限,文件句柄打開時必須同時指定GENERIC_READ和GENERIC_WRITE訪問權限。 |
PAGE_WRITECOPY | 在Win CE中不支持 |
l 參數dwMaximumSizeHigh和參數dwMaximumSizeLow都用於指定內存映射對象的大小,分別代表高32位值和低32位值。如果將這兩個參數都設置爲0,那麼內存映射對象的大小將被設置爲對應文件的大小。
l 參數lpName指定內存映射文件對象的名字,用於創建命名對象。命名對象可以在不同的進程之間共享數據,如果指定的名字在系統中已經存在,那麼將不會新建內存映射文件對象,而是打開已經存在的對象,並返回句柄,這時多個進程實際上操作的是同一個內存映射文件對象。如果希望創建匿名對象,那麼將這個參數設置爲NULL。
如果函數調用成功,將返回內存映射文件對象的句柄;如果失敗返回NULL。
1. 獲取內存映射文件視圖
創建好內存映射文件對象之後,還不能像文件那樣直接對句柄進行讀寫操作,還需要首先將內存映射文件對象映射到進程地址空間內的視圖,然後就可以像操作內存中的數據一樣,通過指針直接對內存映射文件進行讀寫。函數MapViewOfFile()實現這個功能:
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap
);
l 參數hFileMappingObject爲內存映射文件對象的句柄。
l 參數dwDesiredAccess指定對文件視圖的訪問權限,表7-9列出了可取值。
表7-9 文件視圖的訪問權限
值 | 說明 |
FILE_MAP_ALL_ACCESS | 指定讀寫訪問權限,將文件映射爲讀寫視圖 |
FILE_MAP_READ | 指定只讀訪問權限 |
FILE_MAP_WRITE | 指定讀寫訪問權限 |
l 參數dwFileOffsetHigh和dwFileOffsetLow指定從文件開始位置的偏移量,從這個位置開始的文件內容將被映射到文件視圖中。
l 參數dwNumberOfBytesToMap指定映射的字節數,如果這個參數被設置爲0,將映射到文件結束。
如果函數調用成功,將返回映射視圖的起始地址,使用這個返回值就可以讀寫文件數據;調用失敗將返回NULL。
2. 將映射視圖寫回磁盤
如果對文件映射視圖進行寫入操作,此時修改的內容還只是放在內存中,需要進一步調用FlushViewOfFile()函數將視圖中的數據寫回到磁盤:
BOOL FlushViewOfFile(
LPCVOID lpBaseAddress,
DWORD dwNumberOfBytesToFlush
);
l 參數lpBaseAddress指定映射視圖內寫回數據的起始地址。可以指定爲映射視圖內部的任意位置,我們只需要對那些真正被寫過的區域寫回磁盤即可。
l 參數dwNumberOfBytesToFlush指定寫回的字節數。
寫回磁盤成功,返回TRUE;失敗則返回FALSE。
3. 釋放內存映射文件視圖
使用完映射視圖後需要將其從進程的地址空間中釋放,Win CE提供UnmapViewOfFile()實現:
BOOL UnmapViewOfFile(
LPCVOID lpBaseAddress
);
l 參數lpBaseAddress必須指定爲映射視圖的基地址,也就是之前調用MapViewOfFile()的返回值。
不需要指定映射視圖的大小,因爲系統負責維護每段映射視圖的大小信息。如果釋放文件文件視圖成功,返回TRUE;失敗則返回FALSE。一旦將映射文件視圖釋放,指針lpBaseAddress就變爲無效,不能再對其進行讀寫操作。一個好的風格是在調用UnmapViewOfFile(lpBaseAddress)函數之後,直接將指針lpBaseAddress設置爲NULL。
對內存映射文件使用完之後,需要將其關閉,實際上就是調用CloseHandle()關閉之前CreateFileMapping()返回的句柄。
下面的代碼演示如何在進程內創建內存映射文件視圖,並將其釋放:
HANDLE hMapFile; // 內存映射文件對象句柄
HANDLE hFile; // 文件句柄
LPVOID lpMapAddress; // 指向內存文件映射區域的指針,通過這個指針來進行讀寫
hFile = CreateFile(lpcTheFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hMapFile = CreateFileMapping(
hFile, // 文件句柄
NULL, // 不支持安全屬性
PAGE_READWRITE, // 讀寫訪問權限
0, // 內存映射文件對象大小的高位部分
dwFileMapSize, //內存映射文件對象大小的低位部分
NULL); // 非命名內存映射文件
If(hMapFile)
{
lpMapAddress = MapViewOfFile(
hMapFile, // 內存映射文件對象的句柄
FILE_MAP_ALL_ACCESS, // 讀寫
0, // 文件偏移的高32位部分
dwFileMapStart, //文件偏移的低32位
dwMapViewSize); // 映射字節數
if(lpMapAddress)
{
//映射成功,讀寫訪問
…
//使用完畢,需要釋放內存映射文件視圖
UnmapViewOfFile(lpMapAddress);
}
CloseHandle(hMapFile); //關閉內存映射文件對象
}
CloseHandle(hFile); //關閉文件句柄
7.1.2 存儲管理器
存儲管理器負責管理系統中的所有外部存儲設備,包括所有的文件系統和塊設備驅動,所有的文件,數據甚至音量控制操作都需要通過存儲管理器來完成。存儲管理器主要包括3大部分:塊設備驅動管理器,分區管理器和文件系統驅動 (FSD) 管理器。其中塊設備驅動爲物理存儲器的驅動程序;分區驅動允許在物理存儲設備上劃分多個存儲分區,每個分區可以使用不同的文件格式進行格式化,分區驅動的主要任務是將接收到分區地址轉換成設備的底層物理地址,並將請求傳給下面的塊設備驅動;文件系統驅動將數據以文件和文件夾的結構組織,並負責將數據傳給下面的分區驅動。
1. 打開,關閉及枚舉存儲設備
函數OpenStore()用於打開指定名字的存儲設備:
HANDLE OpenStore(
LPCSTR szDeviceName
);
l 參數szDeviceName指定存儲設備的名稱,如DSK1。
如果打開設備成功,函數返回設備的句柄;失敗則返回INVALID_HANDLE_VALUE。比如當系統中不存在指定名稱的設備時,函數調用就會失敗。
使用完設備句柄之後,需要將其關閉,調用函數CloseHandle()實現。
很多時候,我們並不知道系統中存在哪些設備,這時需要便利系統中的設備,獲取每個設備的信息。Windows CE中提供了FindFirstStore()和FindNextStore()函數來實現。
FindFirstStore()函數獲取系統中註冊的第一個存儲設備信息,並返回搜索句柄,這個返回的搜索句柄將被用於後續的搜索:
HANDLE WINAPI FindFirstStore(
PSTOREINFO pStoreInfo
);
l 參數pStoreInfo爲輸出參數,是一個指向結構體STOREINFO的指針,返回該設備的詳細信息。
結構體STOREINFO的定義如下:
typedef struct {
DWORD cbSize;
TCHAR szDeviceName[DEVICENAMESIZE];
TCHAR szStoreName[STORENAMESIZE];
DWORD dwDeviceClass;
DWORD dwDeviceType;
STORAGEDEVICEINFO sdi;
DWORD dwDeviceFlags;
SECTORNUM snNumSectors;
DWORD dwBytesPerSector;
SECTORNUM snFreeSectors;
SECTORNUM snBiggestPartCreatable;
FILETIME ftCreated;
FILETIME ftLastModified;
DWORD dwAttributes;
DWORD dwPartitionCount;
DWORD dwMountCount;
} STOREINFO, *PSTOREINFO;
l 成員cbSize爲結構體STOREINFO的大小,總是爲sizeof(STOREINFO)。
l 成員szDeviceName爲設備的名稱,爲一個字符數組,最大長度只能爲8。
l 成員szStoreName爲對用戶更具可讀性的存儲名稱,最大長度爲32。
l 成員dwDeviceClass爲設備類別,可取值爲STORAGE_DEVICE_CLASS_BLOCK或 STORAGE_DEVICE_CLASS_MULTIMEDIA。
l 成員dwDeviceType爲設備底層存儲介質類型,可取值有:
n STORAGE_DEVICE_TYPE_ATA
n · STORAGE_DEVICE_TYPE_ATAPI
n · STORAGE_DEVICE_TYPE_CDROM
n · STORAGE_DEVICE_TYPE_CFCARD
n · STORAGE_DEVICE_TYPE_DOC
n · STORAGE_DEVICE_TYPE_DVD
n · STORAGE_DEVICE_TYPE_FLASH
n · STORAGE_DEVICE_TYPE_PCCARD
n · STORAGE_DEVICE_TYPE_PCIIDE
n · STORAGE_DEVICE_TYPE_REMOVABLE_DRIVE
n · STORAGE_DEVICE_TYPE_REMOVABLE_MEDIA
n · STORAGE_DEVICE_TYPE_SRAM
n · STORAGE_DEVICE_TYPE_UNKNOWN
n · STORAGE_DEVICE_TYPE_USB
l 成員sdi爲設備在系統中的全局標識
l 成員dwDeviceFlags爲對設備的訪問支持屬性,可取的值有:
n STORAGE_DEVICE_FLAG_MEDIASENSE
n STORAGE_DEVICE_FLAG_READONLY
n STORAGE_DEVICE_FLAG_READWRITE
n STORAGE_DEVICE_FLAG_TRANSACTED
l 成員snNumSectors爲設備上包含的扇區個數。
l 成員dwBytesPerSector爲該設備上使用的每個扇區的字節數。這樣設備總的容量就可以通過snNumSectors乘以dwBytesPerSector得到。
l 成員snFreeSectors爲該設備上空閒的未被分配的扇區個數。
l 成員snBiggestPartCreatable爲該設備上能夠分配給新創建分區的最大扇區個數。
l 成員ftCreated爲設備最近一次被格式化的時間。
l 成員ftLastModified爲設備的分區表最近一次被更改的時間。
l 成員dwAttributes爲卷的屬性,可能的取值爲:
n STORE_ATTRIBUTE_AUTOFORMAT
n STORE_ATTRIBUTE_AUTOMOUNT
n STORE_ATTRIBUTE_AUTOPART
n STORE_ATTRIBUTE_READONLY
n STORE_ATTRIBUTE_REMOVABLE
n STORE_ATTRIBUTE_UNFORMATTED
l 成員dwPartitionCount爲設備所包含的分區個數
l 成員dwMountCount爲該設備上已經以卷的形式被掛載到文件系統中的分區個數。爲了訪問分區中的數據,首先需要將分區掛載到文件系統中。
打開第一個設備之後,函數FindNextStore()將繼續往下搜索獲取下一個設備:
BOOL WINAPI FindNextStore(
HANDLE hSearch,
PSTOREINFO pStoreInfo
);
l 參數hSearch爲調用FindFirstStore()函數返回的搜索句柄。
l 參數pStoreInfo爲輸出參數,也是一個指向結構體STOREINFO的指針,返回當前搜索到設備信息。
函數調用成功,返回TRUE,否則返回FALSE。
爲了遍歷每一個設備的信息,需要一直使用FindFirstStore()返回的搜索句柄調用FindNextStore()函數,直到函數返回FALSE爲止。
遍歷完成後,需要調用FindCloseStore()函數將搜索句柄關閉:
BOOL WINAPI FindCloseStore(
HANDLE hSearch
);
l 參數hSearch必須爲FindFirstStore()函數返回的搜索句柄。
除了通過遍歷設備獲取設備的信息外,還可以直接通過OpenStore()函數打開的設備句柄來訪問指定設備的信息,這通過顯示調用GetStoreInfo()函數實現:
BOOL WINAPI GetStoreInfo(
HANDLE hStore,
PSTOREINFO pStoreInfo
);
l 參數hStore必須爲OpenStore()函數打開過的設備句柄。
l 參數pStoreInfo爲輸出參數,返回對應設備的信息 (STOREINFO)。
注意對於上面所有用到STOREINFO結構體獲取設備信息的函數,在調用函數之前,必須首先將STOREINFO結構體中的成員cbSize設置爲sizeof(STOREINFO),否則函數執行將出錯。
4. 打開,關閉及枚舉存儲設備內的分區
打開存儲設備之後,就可以訪問該設備的所有分區。訪問一個分區之前同樣需要先將其打開,使用分區完後需要將其關閉,還可以枚舉一個設備內包含的所有分區。
打開指定設備的一個分區,使用函數OpenPartition()實現:
HANDLE WINAPI OpenPartition(
HANDLE hStore,
LPCTSTR szPartitionName
);
l 參數hStore必須爲OpenStore()函數打開的設備句柄。
l 參數szPartitionName指定將被打開的分區的名稱。
如果打開分區成功,函數返回分區的句柄;否則返回INVALID_HANDLE_VALUE。記住使用完分區句柄之後,需要調用CloseHandle()將其關閉。
枚舉一個設備內分區需要調用FindFirstPartition()和FindNextPartition()函數實現:
HANDLE WINAPI FindFirstPartition(
HANDLE hStore,
PPARTINFO pPartInfo
);
l 參數hStore指定分區所在的設備句柄。
l 參數pPartInfo爲輸出參數,用於返回設備內第一個分區的信息。這是一個指向結構體PARTINFO的指針。
如果函數調用成功,將返回一個分區搜索句柄;失敗則返回INVALID_HANDLE_VALUE。
得到分區搜索句柄後,不斷調用FindNextPartition()函數就能夠依次遍歷每一個分區,知道該函數返回FALSE爲止:
BOOL WINAPI FindNextPartition(
HANDLE hSearch,
PPARTINFO pPartInfo
);
l 參數hSearch必須爲上面FindFirstPartition()函數返回的搜索句柄。
l 參數pPartInfo爲輸出參數,爲一個指向結構體PARTINFO的指針,返回當前分區的信息。
對設備內的分區遍歷完成後,需要調用FindClosePartition()函數將之前打開的分區搜索句柄關閉:
BOOL WINAPI FindClosePartition(
HANDLE hSearch
);
唯一的參數hSearch指定將被關閉的分區搜索句柄。
下面介紹用於存放分區信息的結構體PARTINFO,它的定義如下:
typedef struct {
DWORD cbSize;
TCHAR szPartitionName[PARTITIONNAMESIZE];
TCHAR szFileSys[FILESYSNAMESIZE];
TCHAR szVolumeName[VOLUMENAMESIZE];
SECTORNUM snNumSectors;
FILETIME ftCreated;
FILETIME ftLastModified;
DWORD dwAttributes;
BYTE bPartType;
} PARTINFO, *PPARTINFO;
l 成員cbSize總是被設置爲這個結構體的大小sizeof(PARTINFO)。
l 成員szPartitionName爲分區的名稱,這是一個字符數組,其最大長度爲32。
l 成員szFileSys爲分區使用的文件系統名稱,其最大長度也爲32。
l 成員szVolumeName爲分區被掛載到系統中使用的卷名稱。
l 成員snNumSectors爲分區包含的所有扇區個數。
l 成員ftCreated和ftLastModified分別代表分區的創建時間和最後一次被修改的時間。
l 成員dwAttributes代表分區的屬性,可取的值如下:
n PARTITION_ATTRIBUTE_AUTOFORMAT:爲自動格式分區
n PARTITION_ATTRIBUTE_BOOT:爲可引導分區
n PARTITION_ATTRIBUTE_EXPENDABLE:爲可擴展分區
n PARTITION_ATTRIBUTE_MOUNTED:爲已經被掛載的分區
n PARTITION_ATTRIBUTE_READONLY:爲只讀分區
l 成員bPartType代表分區類型或標識,可取的值有。
n DOS下的分區:PART_DOS2_FAT,PART_DOS3_FAT,PART_DOS4_FAT,PART_DOS32 (是FAT32)
n 只被用於邏輯塊地址(LBA): PART_DOS32X13, PART_DOSX13, PART_DOSX13X。
n 無效類型:INVALID
n 位置分區類型:PART_UNKNOWN
另外通過打開的分區句柄也能讀取相應分區的信息,函數GetPartitionInfo()實現這個功能:
BOOL WINAPI GetPartitionInfo(
HANDLE hPartition,
PPARTINFO pPartInfo
);
l 參數hPartition必須爲之前通過OpenPartition()函數打開的分區句柄。
l 參數pPartInfo爲輸出參數,返回指定分區的信息。
如果函數調用成功,返回TRUE,否則返回FLASE。
打開一個分區句柄之後,可以修改分區的屬性,以及修改分區的名稱。
函數SetPartitionAttributes()用於設置一個分區的屬性:
BOOL WINAPI SetPartitionAttributes(
HANDLE hPartition,
DWORD dwAttrs
);
參數hPartition爲已經打開的分區句柄,參數dwAttrs指定新的分區屬性,可取值參考前面介紹的分區信息的屬性。
對分區進行重命名,使用函數RenamePartition()實現:
BOOL WINAPI RenamePartition(
HANDLE hPartition,
LPCTSTR szNewName
);
參數hPartition指定分區句柄,參數szNewName指定分區將使用的新名稱。
5. 創建及刪除分區
往打開的設備內新創建一個指定名字,類型和扇區個數的分區,通過調用函數CreatePartitionEx()實現:
BOOL WINAPI CreatePartitionEx(
HANDLE hStore,
LPCTSTR szPartitionName,
BYTE bPartType,
SECTORNUM snNumSectors
);
l 參數hStore爲將在其上新創建分區的設備句柄。
l 參數szPartitionName指定新創建分區的名稱。
l 參數bPartType指定分區的類型,分區類型將被用於確定預計使用的文件系統。
l 參數snNumSectors指定新創建分區的包含的扇區個數,爲一個64位整數值。
刪除設備內的指定分區使用函數DeletePartition()實現:
BOOL WINAPI DeletePartition(
HANDLE hStore,
LPCTSTR szPartitionName
);
l 參數hStore指定設備句柄。
l 參數szPartitionName指定將被刪除的分區名稱。
注意只有當分區沒有被掛載到系統中,才能對其進行刪除,否則刪除操作將失敗。
6. 掛載及卸載分區
掛載一個指定的分區都文件系統上,使用函數MountPartition()實現:
BOOL WINAPI MountPartition(
HANDLE hPartition
);
l 參數hPartition必須爲之前調用OpenPartition()函數打開的分區句柄。
相對的,訪問完分區之後,需要將分區從文件系統中卸載:
BOOL WINAPI DismountPartition(
HANDLE hPartition
);
參數hPartition爲打開的分區句柄。
還可以一次性將指定設備上已經掛載的分區全部卸載下來:
BOOL WINAPI DismountStore(
HANDLE hStore
);
參數hStore必須爲之前通過OpenStore()函數打開的設備句柄。
7. 格式化分區及卷
函數FormatPartitionEx將指定分區格式化爲指定的類型:
BOOL WINAPI FormatPartitionEx(
HANDLE hPartition,
BYTE bPartType,
BOOL bAuto
);
l 參數hPartition指定將被格式化的分區句柄。
l 參數bPartType指定將被格式化成的分區類型。
l 參數bAuto用於確定是否使用參數bPartType指定的分區類型進行格式化。如果這個參數爲FALSE,那麼使用參數bPartType指定的類型。如果這個參數被設置爲TRUE,將忽略參數bPartType,分區驅動將爲系統默認使用的文件系統類型自動選擇合適的分區類型。
注意這個函數只是將分區的第一個扇區清除,並沒有將文件系統寫入分區,爲了將文件系統格式寫入到分區,需要調用FormatVolume()函數直接對分區掛載的捲進行格式化:
DWORD FormatVolume(
HANDLE hVolume,
PDISK_INFO pdi,
PFORMAT_OPTIONS pfo,
PFN_PROGRESS pfnProgress,
PFN_PROGRESS pfnMessage
);
l 參數hVolume指定將被格式化的分區句柄。
l 參數pdi沒有被用到。
l 參數pfo爲一個指向結構體FORMAT_OPTIONS的指針,指定格式化選項。如果這個參數被設置爲NULL,將使用默認選項進行格式化。
l 參數pfnProgress指向一個用於顯示格式化進度的回調函數。
l 參數pfnMessage也是一個回調函數,當格式化出錯時,將被用於顯示錯誤信息。
如果格式化分區成功,返回ERROR_SUCCESS;失敗將返回ERROR_GEN_FAILURE。
結構體FORMAT_OPTIONS主要是針對FAT文件系統進行格式化設置,因爲在Windows CE所帶的文件系統中,只有FAT是可以格式化卷的,它的定義如下:
typedef struct _FORMAT_OPTIONS {
DWORD dwClusSize;
DWORD dwRootEntries;
DWORD dwFatVersion;
DWORD dwNumFats;
DWORD dwFlags;
} FORMAT_OPTIONS;
l 成員dwClusSize指定分區將使用的簇大小,單位爲字節,必須指定爲2的冪次方。簇是FAT中空間分配的最小單位。
l 成員dwRootEntries指定根目錄入口的個數。
l 成員dwFatVersion指定將使用的FAT版本,可取值有12,16或32。
l 成員dwNumFats指定將被創建的FAT個數。
l 成員dwFlags指定格式化分區的方式,可以取表7-10中的一個值或多個值的組合。
表7-10 成員dwFlags的可取值
值 | 說明 |
FATUTIL_DISABLE_MOUNT_CHK | 關閉檢查分區是否被掛載 |
FATUTIL_FULL_FORMAT | 對捲進行完全格式化 |
FATUTIL_SECURE_WIPE | 對捲進行安全的檫除和格式化 |
FATUTIL_FORMAT_EXFAT | 將卷格式化爲擴展FAT |
FATUTIL_FORMAT_TFAT | 將卷格式化爲支持事務安全的擴展FAT |
另外,函數FormatStore()將刪除設備上的所有分區信息:
BOOL WINAPI FormatStore(
HANDLE hStore
);
7.1 註冊表
7.1.1 概述
與Windows桌面系統一樣,Windows CE系統也使用註冊表來存儲系統、應用程序、驅動、用戶等配置信息,註冊表是存儲配置信息的核心數據庫,目前絕大多數的軟件都會使用註冊表。註冊表被組織成層次化的樹形結構,樹中每個節點包含主鍵和值,主鍵類似於文件系統中的目錄,可以包含多個值及子主鍵,其中最高層的主鍵稱爲根主鍵。Windows CE支持4個根主鍵,表7-12列出了Windows CE支持的4個根主鍵以及每個主鍵下存儲的數據類型。
根主鍵名 | 包含的數據 |
HKEY_LOCAL_MACHINE | 硬件以及驅動的配置數據 |
HKEY_CURRENT_USER | 當前用戶獨有的配置數據 |
HKEY_CLASSES_ROOT | OLE和文件類型匹配配置數據 |
HKEY_USERS | 對所有用戶都可見的配置數據 |
如果Win CE被配置成支持多用戶,HKEY_CURRENT_USER主鍵下的數據爲當前登錄用戶獨有的配置,不同用戶將登錄時,系統將加載相應的數據。如果希望配置數據對所有的用戶可見,就應該放到HKEY_USERS根主鍵下。
值是註冊表中存儲數據的基本單元,可以是多種不同的類型,包括字符串或二進制值。這裏的值並不是我們通常所理解的數值,比如8是一個整數值。在註冊表中,每個值都包含一個名字以及相關的數據,爲一個<name, value>對。
Windows CE支持兩種不同類型的註冊表:基於RAM (RAM-based) 的註冊表和基於hive (hive-based) 的註冊表。其中基於RAM的註冊表將數據以一個對象存儲堆放在RAM上,一旦系統掉電,這些數據將丟失,但是由於RAM讀寫速度遠遠高於外部設備,使用RAM註冊表能夠大大提高對註冊表的讀寫訪問效率。而基於hive的註冊表將數據以文件的形式放到文件系統上,這種特殊的文件被稱爲hive(蜂箱),hive將數據保存在外部存儲器上,所以是非易失性。
對註冊表的操作主要包括:打開創建主鍵,關閉主鍵,讀取設置註冊表值,刪除主鍵,及註冊表值。在對主鍵或主鍵下的值進行操作之前,必須首先打開主鍵,獲取主鍵句柄,然後才能對其進行其它操作,使用完主鍵之後,要將主鍵句柄關閉。
7.2.2 相關API函數
1. 打開和關閉主鍵
在讀取或寫入註冊表中的一個值之前,需要先打開包含該值的主鍵。在Windows CE中,打開主鍵使用Win API函數RegOpenKeyEx():
LONG RegOpenKeyEx(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
);
l 參數hKey指定將被打開的鍵的父鍵句柄,必須指定爲之前打開過的主鍵或者系統保留的根主鍵常數,可取值在表7-12中所列。
l 參數lpSubKey指定將被打開的子健的名字。如果這個參數被指定爲NULL或者爲指向空字符串的指針,那麼這個函數將再次打開參數hKey指向的主鍵句柄,注意需要將句柄關閉。子健可以包含多級子健,每個子健之間使用反斜槓隔開。如果指定名字的主鍵不存在,將導致函數調用失敗。
l 參數ulOptions在Win CE中被保留,總是設置爲0。
l 參數samDesired在Win CE中不支持,設置爲0。
l 參數phkResult爲輸出參數,返回被打開的主鍵的句柄。使用完主鍵句柄之後,需要將其關閉,調用RegCloseKey()函數關閉主鍵句柄,注意不要使用CloseHandle()。
如果打開指定名字的主鍵成功,函數將返回ERROR_SUCCESS;打開失敗將返回一個非0的錯誤碼。
被打開的主鍵句柄使用完之後一定要及時關閉,這樣系統才能回收資源,關閉主鍵句柄通過函數RegCloseKey()實現:
LONG RegCloseKey(
HKEY hKey
);
l 參數hKey指定將被關閉的主鍵句柄。
如果關閉主鍵句柄成功,函數返回ERROR_SUCCESS;如果失敗,將返回非0的錯誤碼。如果對打開的主鍵進行過修改,關閉主鍵將自動對其進行保存。
下面的演示如何打開一個指定名字的主鍵:
HKEY hkResult;
LONG lRes;
lRes = RegOpenKey( HKEY_LOCAL_MACHINE, TEXT("Drivers\\BuiltIn\\Serial"), 0, 0, & hkResult);
if(lRes == ERROR_SUCCESS)
{
//打開主鍵成功,進行處理
…
//最後需要將主鍵關閉
RegCloseKey(hkResult);
}
else
{
//打開主鍵失敗,進行出錯處理
…
}
8. 創建主鍵
除了打開指定主鍵外,還可以向註冊表內新建一個主鍵,這通過函數RegCreateKeyEx()實現:
LONG RegCreateKeyEx(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD Reserved,
LPWSTR lpClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
);
與RegOpenKeyEx()函數類似,參數hKey指定父鍵句柄,參數lpSubKey指定新創建的子主鍵的名字。
l 參數Reserved被保留,總是設置爲0。
l 參數lpClass指定新創建主鍵的類名稱,在Windows CE中,主鍵類名稱的最大字符長度爲255。如果不需要指定主鍵類名稱,將這個參數設置爲NULL。
l 參數dwOptions指示新創建的主鍵是易失性的還是非易失性的,可取的值如表7-13所示。
值 | 說明 |
REG_OPTION_NON_VOLATILE | 默認設置,非易失性主鍵,新創建的主鍵以文件的形式駐在外部存儲器設備上,當系統重啓的時候,主鍵值將保留 |
REG_OPTION_VOLATILE | 易失性主鍵,數據都放在RAM上,一旦系統掉電,這些數據將丟失。易失性主鍵的訪問速度比較快,因爲都在RAM中 |
l 參數samDesired被忽略,設置爲0。
l 參數lpSecurityAttributes用於設置主鍵的安全屬性,在Windows CE中不支持,總是設置爲NULL。Windows CE將自動爲主鍵分配一個默認的安全描述子。
l 參數phkResult爲輸出參數,返回新創建或打開的主鍵句柄。
l 參數lpdwDisposition爲輸出參數,可能值爲REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY,分別表示主鍵爲新創建的還是打開已經存在的。
如果指定名字的主鍵已經存在,那麼這時只是打開主鍵句柄,並不會再新建;如果爲新的主鍵名,將在註冊表的指定位置新創建一個空的不包含任何值的主鍵,然後打開主鍵。
函數調用成功,將返回ERROR_SUCCESS;否則返回非0的錯誤碼。通過查詢輸出參數lpdwDisposition來判斷是否向註冊表內新創建主鍵。
9. 讀取和設置註冊表值
打開主鍵後,就可以對該主鍵下的值進行讀寫訪問。函數RegQueryValueEx()讀取指定值的類型及數據信息:
LONG RegQueryValueEx(
HKEY hKey,
LPCWSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData
);
l 參數hKey爲包含指定值(Value)的主鍵句柄,可以爲前面已經打開的主鍵句柄或爲代表系統根主鍵的保留句柄值。
l 參數lpValueName指定將讀取的值的名稱。這個參數可以設置爲NULL或空字符串,此時將讀取hKey主鍵下名稱爲”Default”的值信息。
l 參數lpReserved被保留,設置爲NULL。
l 參數lpType爲輸出參數,返回指定值的類型信息,表7-13列出了可能的取值。
類型值 | 說明 |
REG_BINARY | 爲任意格式的二進制數據 |
REG_DWORD | 爲32位整數 |
REG_DWORD_BIG_ENDIAN | 爲大端字節序格式的32位整數。在大端字節序格式中,高字節放在內存中的低地址,低字節放在內存中的高地址。如值0x12345678以大端序在內存中將存儲爲(0x12 0x34 0x56 0x78) |
REG_DWORD_LITTLE_ENDIAN | 爲小端字節序格式的32位整數,與大端序相對,高字節存在高地址,低字節存在低地址。 |
REG_EXPAND_SZ | 爲嵌入式環境變量的字符串,比如%PATH% |
REG_LINK | 爲Unicode編碼的符號連接,爲Windows CE內部保留使用,應用程序不能使用該類型 |
REG_MULTI_SZ | 爲一個字符串數組,每個字符串以null結束,字符串數組需要以2個null結束。 |
REG_NONE | 爲未定義的值類型 |
REG_RESOURCE_LIST | 爲一個設備驅動的資源列表 |
REG_SZ | 爲一個以null結束的Unicode字符串。 |
如果不需要獲取值的類型信息,可以將這個參數設置爲NULL。
l 參數lpData爲一個用於接收值數據的緩衝區指針,爲輸出參數。如果不需要讀取值數據,將這個參數設置爲NULL。
l 參數lpcbData指定值數據緩衝區的大小,單位爲字節。爲輸入輸出參數,函數執行完後,這個參數將返回實際讀取的值數據的字節數。如果參數lpData指向的緩衝區太小,不足以接收全部的值數據,函數的執行將失敗,並返回ERROE_MORE_DATA,參數lpcbData將返回需要的緩衝區字節數。只有當參數lpData被設置爲NULL時,這個參數才能設置爲NULL,當然也可以不設置爲NULL,這時函數如果調用成功,會將值數據的大小返回到這個參數,這樣提供了一種在分配緩衝區之前查詢值數據大小的方式,保證分配適當大小的緩衝區。
如果讀取值信息成功,函數將返回ERROR_SUCCESS;失敗將返回非0的錯誤碼。
應用程序還可以設置註冊表值,與讀取註冊表值方法相似,通過指定值名稱來設置,函數RegSetValueEx()實現這個功能:
LONG RegSetValueEx(
HKEY hKey,
LPCWSTR lpValueName,
DWORD Reserved,
DWORD dwType,
const BYTE* lpData,
DWORD cbData
);
這些參數的涵義與上面介紹的讀取註冊表值函數RegQueryValueEx()中的一樣。如果參數lpValueName指定的值名稱在指定主鍵下不存在,將新建一個值。還可以將參數lpValueName設置爲NULL或指向空字符串,這時將對hKey指向的主鍵下的”Default”值進行設置。
在Windows CE中,應用程序還可以通過函數RegQueryInfoKey()讀取整個主鍵的信息:
LONG RegQueryInfoKey(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcbClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime
);
l 參數hKey指定已經被打開的主鍵句柄或爲系統保留的指向根主鍵的句柄。
l 參數lpClass爲輸出參數,指向一個用於接收主鍵的類名稱的緩衝區。如果不需要讀取類名稱,可以將這個參數設置爲NULL。
l 參數lpcbClass指定用於接收類名稱的緩衝區的字節數。函數調用完成,這個參數將返回實際存入緩衝區的字節數。
l 參數lpReserved爲保留,設置爲NULL。
l 參數lpcSubKeys爲輸出參數,用於返回該主鍵所包含的子主鍵的個數,這個參數可以設置爲NULL。
l 參數lpcbMaxSubKeyLen爲輸出參數,用於返回該主鍵的子主鍵中最長名稱的長度,可以設置爲NULL。
l 參數lpcbMaxClassLen爲輸出參數,返回子主鍵中類名稱最長的字符串長度,可以設置爲NULL。
l 參數lpcValues爲輸出參數,返回該主鍵所包含的值得個數,可以設置爲NULL。
l 參數lpcbMaxValueNameLen和lpcbMaxValueLen分別返回該主鍵包含的值中最長名稱的長度和最大值數據部分的長度。
l 參數lpcbSecurityDescriptor和lpftLastWriteTime在Windows CE中都不支持,設置爲NULL。
如果函數執行成功,返回ERROR_SUCCESS;否則返回非0的錯誤碼。
10. 刪除註冊表主鍵和值
刪除註冊表主鍵使用函數RegDeleteKey()實現:
LONG RegDeleteKey(
HKEY hKey,
LPCWSTR lpSubKey
);
l 參數hKey爲將被刪除的主鍵的父主鍵句柄,父主鍵必須先打開。
l 參數lpSubKey指定將被刪除的主鍵的名稱。這個主鍵在刪除時不能處於打開狀態。如果指定的主鍵名稱在父主鍵下並不存在,函數調用將失敗。這個參數不能設置爲NULL。
如果刪除主鍵成功,返回ERROR_SUCCESS,否則返回非0的錯誤碼。注意不要去刪除系統保留的那4個根主鍵,這是不允許的,也是不可能實現的。
刪除註冊表值使用函數RegDeleteValue():
LONG RegDeleteValue(
HKEY hKey,
LPCWSTR lpValueName
);
l 參數hKey指定包含將被刪除值的主鍵句柄,這個主鍵必須先被打開。
l 參數lpValueName指定將被刪除的值名稱。這個參數可以設置爲NULL,此時將刪除hKey主鍵下名稱爲”Default”的值。
如果刪除主鍵成功,返回ERROR_SUCCESS,否則返回非0的錯誤碼。
11. 枚舉註冊表主鍵和值
前面介紹的RegQueryInfoKey()函數只是返回一個主鍵的整體統計信息(比如包含的子主鍵個數,最大子主鍵名稱長度等),沒有關於子主鍵和值的具體信息。如果希望進一步獲取每個子主鍵或值的信息,可以通過Windows CE提供的枚舉主鍵或值的功能來實現。
枚舉一個父主鍵下包含的所有子主鍵信息使用函數RegEnumKeyEx()來實現,這個函數的執行比較特殊,它並不像函數名所標識的那樣一次性返回所有的子主健信息,而是需要通過指定下標來獲取特定的子健信息。所以如果用戶希望遍歷所有的子主鍵,需要重複調用這個函數,並不斷加大下標,直到函數返回ERROR_NO_MORE_ITEMS爲止。這個函數的原型如下:
LONG RegEnumKeyEx(
HKEY hKey,
DWORD dwIndex,
LPWSTR lpName,
LPDWORD lpcName,
LPDWORD lpReserved,
LPWSTR lpClass,
LPDWORD lpcbClass,
PFILETIME lpftLastWriteTime
);
l 參數hKey指定父主鍵句柄,父主鍵必須已經被打開(系統保留的根主鍵除外)。函數將枚舉這個父主鍵下的子主鍵。
l 參數dwIndex指定將要讀取的子主鍵的下標,從0開始。注意子主鍵沒有順序的概念,新創建的子健可能被分配一個任意的下標。所以不要指望通過下標來定位子主鍵,而應該通過子主鍵的名稱來定位。
l 參數lpName爲輸出參數,返回指定子主鍵的名稱,而不是子主鍵所在的完全層次結構路徑
l 參數lpcName指定用於接收子主鍵名稱的緩衝區的長度,函數執行完後,這個參數還將返回實際存儲到緩衝區中的子主鍵名稱的長度。
l 參數lpReserved爲保留,設置爲NULL。
l 參數lpClass爲輸出參數,返回對應子主鍵的類名稱。這個參數可以設置爲NULL,如果並不需要類名稱的話。
l 參數lpcbClass指定用於接收類名稱的緩衝區的長度,函數執行完,將放回實際存入緩衝區的長度。
l 參數lpftLastWriteTime在Windows CE中被忽略,總是設置爲NULL。
與枚舉子主鍵的方式類似,函數RegEnumValue()用於枚舉指定主鍵下的值信息:
LONG RegEnumValue(
HKEY hKey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData
);
l 參數hKey指定主鍵句柄,必須爲被打開過的主鍵(系統保留的根主鍵除外)。
l 參數dwIndex指定將被訪問的值在主鍵中的下標,從0開始。同樣值在主鍵中沒有順序可言,新創建的值可能被分配一個任意下標。
l 參數lpValueName和lpcchValueName分別用於返回指定的值名稱和值名稱長度。
l 參數lpReserved爲保留,設置爲NULL。
l 參數lpType返回值的類型信息,可能值如表7-xx所列。
l 參數lpData和lpcbData分別用於返回值的數據和數據的長度。
這兩個函數都在調用成功時,返回ERROR_SUCCESS,否則返回非0的錯誤碼。
12. 將主鍵信息寫回註冊表
前面介紹過,關閉主鍵句柄會自動將對主鍵的所有改動寫回到註冊表中,但是有些時候需要立即將一些改動寫回,又希望保持主鍵爲打開狀態,Windows CE提供了RegFlushKey()函數實現提前寫回的功能:
LONG RegFlushKey(
HKEY hKey
);
l 參數hKey爲將被寫回的主鍵句柄。
如果函數調用成功,對主鍵的所有修改都將寫回到持久性存儲器中,並返回ERROR_SUCCESS,否則返回非0的錯誤碼。
7.2 小結
本章介紹了Windows CE 7中的文件系統和註冊表的基本概念和操作。首先介紹三種類型的文件系統,然後介紹文件操作的API,包括創建或打開,讀寫文件數據,設置文件讀寫指針位置,讀取設置文件屬性信息,複製移動文件,搜索目錄下所有文件信息等。最後介紹Windows CE 7中的註冊表結構及註冊表的操作API,包括打開、關閉、創建主鍵,讀取和設置註冊表值,刪除註冊表主鍵和值,以及枚舉註冊表主鍵和值。