文件系统是操作系统用于明确磁盘或分区上的文件的方法和数据结构;即在磁盘上组织文件的方法。也指用于存储文件的磁盘或分区,或文件系统种类。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:与文件管理有关软件、被管理文件以及实施文件管理所需数据结构。从系统角度来看,文件系统是对文件存储器空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。注册表(Registry,繁体中文版Windows称之为登录)是Microsoft Windows中的一个重要的数据库,用于存储系统和应用程序的设置信息。早在Windows 3.0推出OLE技术的时候,注册表就已经出现。随后推出的Windows NT是第一个从系统级别广泛使用注册表的操作系统。但是,从Microsoft Windows 95开始,注册表才真正成为Windows用户经常接触的内容,并在其后的操作系统中继续沿用至今。本章主要讲解Windows Embedded Compact 7中的文件系统和注册表管理。
7.1 Windows Embedded Compact 7中的文件系统
7.1.1 文件系统概述
Windows CE提供了三种类型的文件系统——RAM-based文件系统、ROM-based文件系统以及用于支持ATA(Advanced Technology Attachment)设备和SRAM卡等外围储存设备的FAT文件系统。前两种文件系统属于Windows CE的内建文件系统,后者属于可安装性文件系统。另外,嵌入式系统的开发人员也可以编写自己的文件系统,并在系统中注册使用。Windows CE提供了platform-independent API,不论是何种储存设备,所有对文件系统的存取都是通过Win32 API完成。
Windows CE的文件系统是非常灵活的模块,它可以兼容用户自己开发的文件系统、文件过滤器以及其他的各种块设备文件系统。所有文件系统以及和文件操作相关的API都由FileSys.exe进程提供。这个进程模块实现了对象存储和存储管理器的功能。在Windows CE .NET中所有文件和文件管理系统都存在于一个以“\”开始的名字空间下。所有文件都位于从根文件开始的一棵树中,并且以特定的路径惟一标识。这点和Windows桌面系统非常相似,只是没有驱动器号。在Windows CE中,设备被mount到根下的文件夹中。这样,当新的存储卡被添加到系统中的时候,其映射的路径可能为”\Storage Card”。
FileSys.exe主要由以下3个部分组成:ROM文件系统,对象存储以及存储管理器。其中对象存储是Windows CE中一个独特的基于RAM的文件系统,不同于传统的的基于磁盘分区的文件系统,对象存储是一个内存堆,逻辑上包含RAM系统注册表,可选的RAM文件系统和可选的数据库。对象存储的主要功能包括:管理栈和内存堆,压缩和展开文件。存储管理器负责管理系统中的存储设备以及用于访问它们的文件系统,包括存储驱动程序,分区驱动程序,文件系统驱动程序和文件系统筛选器。
注意,在Windows CE中,不支持当前目录,文件都是通过完整路径来表示的。而且不能使用驱动器名,不同的存储卷都被挂载到根目录下的某个子目录。
7.1.2 Windows CE文件操作API
在Windows CE文件系统中,支持文件扩展名的概念,不同类型的文件使用不同的扩展名,文件名的格式还是使用“名字.扩展名”。比如可执行文件都使用”.exe”作为扩展名,这将被Shell用于区分一个文件是否为可执行文件。在Windows CE文件系统中,可以支持多种文件属性标志,比如只读、系统、存档、压缩及隐藏等。
Windows CE支持Win32 API中的大多数文件IO功能,如CreateFile()函数创建新文件,ReadFile(), WriteFile()函数实现对文件数据的读写。Windows CE不能支持对文件的重叠IO,所以文件无法通过FILE_FLAG_OVERLAPPED标志形式打开。下面分别介绍对文件操作的这些API函数。
1. 创建、打开文件
创建一个新文件,打开一个已经存在的文件,或截断已经存在的文件,可以调用Win32中的标准函数CreateFile()来完成:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
l 参数lpFileName指定将要创建或打开的文件名。由于WinCE 7中不支持当前路径,这里指定的文件名必须使用完整路径,否则不含完整路径的文件名将被系统默认指定为对象存储中的根目录下。
l 参数dwDesiredAccess指定对文件对象的访问权限,应用程序可以只读访问,只写访问,或读写访问,可取的值如表7-1所示。
表7-1 参数dwDesiredAccess的可取值
可取值 | 说明 |
GENERIC_EXECUTE | 指定可执行访问 |
GENERIC_READ | 指定读访问,应用程序可以从文件中读取数据,内部文件指针自动移动到目标位置 |
GENERIC_WRITE | 指定写访问,应用程序能够将数据写到文件中,内部文件指针自动移动到目标位置 |
这几个参数可以组合使用,比如指定对文件的读写访问权限可以使用GENERIC_READ | GENERIC_WRITE。参数 dwDesiredAccess还可以设置为0,表示既不请求读权限也不请求写权限,这时打开文件主要用于获取文件的属性。
l 参数dwShareMode指定文件对象的共享属性,允许进程将对文件的访问权限共享给其它进程,可取值如表7-2所列。
表7-2 参数dwShareMode的可取值
可取值 | 说明 |
FILE_SHARE_READ | 表示后续其它进程对该文件对象的打开操作,只有当请求只读访问权限时才成功 |
FILE_SHARE_WRITE | 表示后续其它进程对该文件对象的打开操作,只有当请求只写访问权限时才成功 |
可以将上面两个值组合使用,允许共享读写访问权限。如果将参数dwShareMode设置为0,表示禁止共享文件对象。后续其它进程对文件的打开操作都将失败,直到当前的文件句柄被关闭为止。
l 参数lpSecurityAttributes指定文件访问的安全属性,在WinCE中不支持,设置为NULL.
l 参数dwCreationDisposition指定打开或创建文件的方式,需要处理指定名字的文件是否已经存在这两种情况,可取的值如表7-3所列。
表7-3 参数dwCreationDisposition的可取值
可取值 | 说明 |
CREATE_ALWAYS | 总是创建一个新文件。如果文件已经存在,那么原文件的内容和属性都将被清空 |
OPEN_EXISTING | 打开文件。如果文件不存在,函数调用将失败 |
CREATE_NEW | 创建新文件。如果文件已经存在,函数调用将失败,这与CREATE_ALWAYS不同 |
OPEN_ALWAYS | 总是打开文件。如果文件不存在,就创建一个新文件。注意这个参数与CREATE_ALWAYS不同,当文件存在时,不会将文件内容清空。 |
TRUNCATE_EXISTING | 打开文件并将文件内容清空,文件的大小将变为0。使用这个属性要求进程必须请求对文件的GENERIC_WRITE访问权限。如果文件不存在,函数调用将失败。 |
l 参数dwFlagsAndAttributes指定文件的属性和标志,这个参数只在新创建文件时有用,表7-4列出可取的属性,表7-5列出可取的标志,其中多个属性值可以组合使用。
表7-4 参数dwFlagsAndAttributes可取的属性之
属性值 | 说明 |
FILE_ATTRIBUTE_ARCHIVE | 设置文件为存档,应用程序可以使用这个属性来标记备份或可删除的文件 |
FILE_ATTRIBUTE_COMPRESSED | 设置文件或目录为可压缩。对于文件,其包含的所有数据为压缩过的。对于目录,其内部新建的文件和子目录都将默认设置为压缩 |
FILE_ATTRIBUTE_HIDDEN | 设置文件为隐藏,对常规的目录列表不可见 |
FILE_ATTRIBUTE_NORMAL | 文件的默认属性,会被其它属性覆盖,只有单独使用时才有效 |
FILE_ATTRIBUTE_READONLY | 设置文件为只读 |
FILE_ATTRIBUTE_ROMMODULE | 表示该文件为一个DLL模块 |
FILE_ATTRIBUTE_SYSTEM | 设置文件系统位,表示该文件对操作系统很重要 |
FILE_ATTRIBUTE_TEMPORARY | 在WinCE中不支持 |
表7-5 参数dwFlagsAndAttributes可取的文件标志
可取标志 | 说明 |
FILE_FLAG_NO_BUFFERING | 设置文件打开后不使用系统缓存,这个标志不影响磁盘缓存 |
FILE_FLAG_OVERLAPPED | 这个标志在WinCE中不支持 |
FILE_FLAG_RANDOM_ACCESS | 指示文件将被随机访问,可被系统用于优化文件缓存策略 |
FILE_FLAG_WRITE_THROUGH | 设置对文件的写操作直接写回到磁盘,而不会缓存在内存中 |
l 参数hTemplateFile在WinCE中不支持,设置为0。
如果函数调用成功,返回打开的文件句柄,如果函数调用失败,将返回INVALID_HANDLE_VALUE。当参数dwCreationDisposition被设置为CREATE_ALWAYS或者OPEN_ALWAYS时,如果文件在函数调用之前已经存在,此时尽管函数调用成功,GetLastError()函数还是会返回ERROR_ALREADY_EXISTS;如果文件在函数调用之前不存在,GetLastError()函数将返回0。这个可用于判断文件是否已经在函数调用之前存在。
下面的代码演示如何打开一个文件:
HANDLE hFile; //文件句柄
hFile = CreateFile (TEXT("\\MYFILE.TXT"), // 打开MYFILE.TXT文件
GENERIC_READ | GENERIC_WRITE, //读写访问打开
FILE_SHARE_READ, // 共享读访问权限
NULL, // 不支持文件访问的安全属性
OPEN_EXISTING, // 只打开已经存在的文件
FILE_ATTRIBUTE_NORMAL, // 默认属性
NULL); // 不支持
if (hFile == INVALID_HANDLE_VALUE)
{
// 打开文件句柄失败,出错处理
return;
}
2. 读写文件
在WinCE中,读写文件使用Win32提供的API函数ReadFile()和WriteFile()实现,它们都接受之前打开的文件句柄作为参数,如果读写成功,都返回TRUE(非0):
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
l 参数hFile指定将被读取数据的文件句柄,这个文件句柄在被创建或打开时必须具有读访问权限 (GENERIC_READ)。
l 参数lpBuffer为用于接收文件数据的缓冲区的指针。
l 参数nNumberOfBytesToRead指定从文件中读取数据的最大字节数。
l 参数lpNumberOfBytesRead为输出参数,返回实际读取的字节数。
l 参数lpOverlapped为一个指向OVERLAPPED结构体的指针,用于异步文件读写,一般设置为NULL。
从文件中读取数据,将从当前文件指针指向的位置开始,读取完成后,系统将自动更新文件指针的位置(向后移动所读取的字节数)。如果文件指针到达末尾,函数调用将返回TRUE,同时将输出参数*lpNumberOfBytesRead设置为0。进一步如果文件指针在读取操作之前已经到达末尾,函数调用还是会返回TRUE(成功)。WinCE中不支持异步文件读写操作,下面的代码演示如何判断文件指针是否已经到达末尾:
// 同步读文件操作
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL) ;
// 判断是否达到文件末尾
if (bResult && (nBytesRead == 0) )
{
// 文件指针已经达到末尾
}
向文件内写入数据的WriteFile()函数的原型如下:
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
l 参数hFile指定将被写入数据的文件句柄,要求打开或创建文件时必须具有写访问权限(GENERIC_WRITE)。
l 参数lpBuffer指定包含数据的缓冲,这些数据将被写入文件
l 参数nNumberOfBytesToWrite指定写入文件的最大字节数。可以将这个参数设置为0,表示将执行一次空的写操作,空的写操作不会将任何数据写入文件,也不会截断文件,但是可以改变文件的时间戳属性(比如文件的最后一次被更改时间)。
l 参数lpNumberOfBytesWritten为输出参数,返回实际写入文件的数据字节数。
l 参数lpOverlapped为一个指向OVERLAPPED结构体的指针,用于异步文件读写,一般设置为NULL。
从当前文件指针所指向的位置开始写入数据,写入完成后,系统将自动更新文件指针到写入的数据之后。
3. 手动设置文件指针位置
下面介绍文件指针的位置,实际上系统在每个文件对象的内部都维护一个文件指针,指向文件读写操作的起始位置。当应用程序打开或新创建文件时,文件指针将被设置为0。当进行文件读写操作后,系统会自动更新文件指针的位置。除此之外,还可以手工设置文件指针的位置,这通过Win API提供的SetFilePointer()实现,这里使用的是相对位置的移动,比如相对文件开头,当前指针位置或者文件末尾的移动的字节数,这个函数的原型如下:
DWORD SetFilePointer(
HANDLE hFile,
LONG lDistanceToMove,
PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod
);
l 参数hFile指定将被移动文件指针位置的文件句柄。
l 参数lDistanceToMove用于指定文件指针移动的字节数的低32位。如果不使用后面高32位偏移的话(4GB以内的偏移),这个参数就是移动的字节数,这是一个有符号的32位数,正值表示向前移动,负值表示向后移动,注意不能将文件指针向后移动到超出文件开头;如果使用高32位移动偏移参数的话,这时整个移动字节数为一个64位的有符号数,参数lpDistanceToMoveHigh指定高32位的值,参数lDistanceToMove则指定低32位的值。
l 参数lpDistanceToMoveHigh指定文件指针移动的字节数的高32位。如果不使用高32位偏移时,应该将这个参数设置为NULL。
l 参数dwMoveMethod指定文件指针移动的相对起始位置,表7-6列出可取的值。
表7-6 文件指针移动的起始位置的可取值
可取值 | 说明 |
FILE_BEGIN | 相对文件开头位置的移动 |
FILE_CURRENT | 相对文件指针当前位置的移动 |
FILE_END | 相对文件末尾位置的移动 |
如果移动文件指针成功,函数将返回文件指针移动后到达的新位置;如果函数调用失败,将返回0xFFFFFFFF,此时文件指针不会移动,留在原位置。但是对于大文件(文件大小超过4GB),由于0xFFFFFFFF也可能是一个合法的文件指针位置,所以为了进一步确认是否移动成功,需调用GetLastError()函数来判断,如果返回NO_ERROR,表示移动文件指针成功;如果返回ERROR_NEGATIVE_SEEK,表示新的文件指针位置将为负值,移动文件指针失败。下面的代码演示如何可靠判断文件指针是否移动成功:
// 尝试将文件指针移动到相对文件开头的指定偏移量的位置,并检测是否成功
dwPtr = SetFilePointer (hFile, lDistance, NULL, FILE_BEGIN);
//如果返回0xFFFFFFFF不一定代表失败,需进一步判断
if (dwPtr == 0xFFFFFFFF)
{
// 调用GetLastError()函数获取错误码
dwError = GetLastError();
if (dwError == NO_ERROR)
{
//表示没有出错,将文件指针成功移动到指定位置
}
else if (dwError == ERROR_NEGATIVE_SEEK)
{
//因为将文件指针移动到负值位置而出错(向后超出文件开头位置)
}
else
{
//其它类型错误
}
}
else
{
//移动文件指针位置成功
}
注意将文件向后移动到超出文件末尾位置是合法的,但是文件的大小不会立即增加,直到SetEndOfFile()函数或WriteFile()函数被调用以后才会生效。
如果只是希望查询文件指针当前的位置,可以执行下面语句,这时文件指针没有移动:
dwCurPtr = SetFilePointer (hFile, 0, NULL, FILE_CURRENT);
另外两个比较常见的操作是将文件指针移动到文件开始位置和文件末尾位置:
dwPtr = SetFilePointer (hFile, 0, NULL, FILE_BEGIN); //移到文件开始位置
dwPtr1 = SetFilePointer (hFile, 0, NULL, FILE_END); //移到文件末尾位置
4. 关闭文件
使用文件完毕之后,需要将文件关闭,其实我们之前涉及到的文件操作都是通过文件句柄完成的,所以这里指的关闭文件其实就是关闭文件句柄:
BOOL CloseHandle(
HANDLE hObject
);
l 参数hObject为将被关闭的文件句柄。
关闭文件句柄成功,函数返回TRUE (非0)。关闭句柄操作将文件句柄的内部计数减1,当计数减为0,系统才释放资源。
5. 截短文件到当前位置
在Win CE中,可以将文件截短到当前文件指针所指向的位置,函数SetEndOfFile()实现这个功能:
BOOL SetEndOfFile(
HANDLE hFile
);
l 参数hFile指定文件句柄。
如果函数调用成功,返回TRUE,否则返回FALSE (0)。
这个函数可用于向后扩展文件,首先调用SetFilePointer()函数将文件指针向后移动到超过文件末尾的位置,然后调用SetEndOfFile()函数使扩展生效,注意此时从旧的文件末尾位置到新的文件末尾位置之间的区域是未定义的。
6. 复制、删除及移动文件
之前介绍的操作文件的函数是通过文件句柄对文件包含的内容进行操作(读写),下面介绍的对文件的复制、移动、及删除文件则是对文件节点本身进行操作,直接通过文件名进行操作,而不要首先打开文件句柄,而且这些函数都在成功执行后返回TRUE,执行失败则返回FALSE。
复制文件操作使用函数:
BOOL CopyFile(
LPCTSTR lpExistingFileName,
LPCTSTR lpNewFileName,
BOOL bFailIfExists
);
l 参数lpExistingFileName指定原文件的名字,这个文件必须已经存在,否则失败。
l 参数lpNewFileName指定复制操作的目标文件的名字。
l 参数bFailIfExists设置如果目标文件的名字已经存在时执行的操作。如果这个参数被设置为TRUE,表示如果目标文件名已经存在,那么函数执行失败,不会将源文件复制过去;如果设置为FALSE,则表示即使目标文件名已经存在,也会将源文件复制过去并覆盖已经存在的文件。
删除文件操作使用函数:
BOOL DeleteFile(
LPCTSTR lpFileName
);
l 参数lpFileName指定将被删除的文件名。如果指定文件不存在,则函数调用失败。
函数DeleteAndRenameFile()则首先将源文件内容复制到目标文件,然后删除源文件:
BOOL DeleteAndRenameFile(
LPCWSTR lpszDestFile,
LPCWSTR lpszSourceFile
);
l 参数lpszDestFile指定目标文件名。
l 参数lpszSourceFile指定源文件名。
移动文件操作使用函数:
BOOL MoveFile(
LPCTSTR lpExistingFileName,
LPCTSTR lpNewFileName
);
l 参数lpExistingFileName指定移动操作的源文件名,源文件必须已经存在。
l 参数lpNewFileName指定移动操作的目标文件名,目标文件不能已经存在,否则函数调用失败。
7. 读取及设置文件属性
Win CE提供了GetFileAttributes()和SetFileAttributes()函数来读取和设置文件或目录的属性,这两个函数都通过文件名进行操作:
DWORD GetFileAttributes(
LPCTSTR lpFileName
);
l 参数lpFileName指定文件名或目录名
如果函数调用成功,返回一个DWORD值;否则返回0xFFFFFFFF表示函数调用失败。表7-7列出了可能的返回值。
表7-7 文件属性可取的值
值 | 说明 |
FILE_ATTRIBUTE_ARCHIVE* | 文件或目录具有存档属性,应用程序使用这个属性将文件标记为备份或可删除 |
FILE_ATTRIBUTE_COMPRESSED | 文件或目录是压缩的。对于文件,将所有的数据进行压缩存储;对目录,该目录下的新创建的文件及子目录默认都设置压缩属性 |
FILE_ATTRIBUTE_DIRECTORY | 指定的对象是目录 |
FILE_ATTRIBUTE_ENCRYPTED | 对文件或目录进行加密。对文件,所有数据流进行加密;对目录,新创建的文件和子目录默认都设置加密属性 |
FILE_ATTRIBUTE_HIDDEN* | 文件或目录是隐藏的 |
FILE_ATTRIBUTE_INROM | 只针对文件,为存放在ROM内的只读系统文件,不能对其进行修改 |
FILE_ATTRIBUTE_NORMAL* | 文件的默认属性,只在单独使用时这个属性才有效 |
FILE_ATTRIBUTE_READONLY* | 文件或目录是只读的。对文件,只能读取文件的内容,不能写入,不能删除该文件;对目录,不能将其删除或移动 |
FILE_ATTRIBUTE_ROMMODULE | 为ROM中的系统可执行模块,可以直接从ROM中运行,而不必先加载到RAM(内存)中,不能使用CreateFile()函数访问,只能通过LoadLibrary()及CreateProcess()函数 |
FILE_ATTRIBUTE_ROMSTATICREF | 为一个至少被一个其它文件引用的DLL模块 |
FILE_ATTRIBUTE_SYSTEM* | 为部分或全部被OS独占使用的文件或目录 |
FILE_ATTRIBUTE_TEMPORARY* | 为一个临时存储文件。文件系统尽量将数据保持在内存中以快速访问。应用程序在使用完毕后应该立即将临时文件删除。 |
通过将函数的返回值与表7-7中的属性标记相与(&)来判断文件或目录是否具有该属性,下面的例子演示如何获取文件的属性信息:
DWORD dwRes = GetFileAttributes (szFileName);
If (dwRes == 0xFFFFFFFF)
{
//获取文件属性出错,进行错误处理
}
Else
{ //获取具体属性
if (dwRes & FILE_ATTRIBUTE_DIRECTORY)
{
//为一个目录
}
if (dwRes & FILE_ATTRIBUTE_READONLY)
{
//为只读
}
…
}
对应的设置文件或目录属性使用SetFileAttributes()函数,它的原型如下:
BOOL SetFileAttributes(
LPCTSTR lpFileName,
DWORD dwFileAttributes
);
l 参数lpFileName指定文件或目录的名字
l 参数dwFileAttributes为一个DWORD值,可以为表7-7中右上角带’*’号标记的一个或多个属性值的组合(使用’|’组合)。
如果设置文件属性成功,返回TRUE(非0);否则返回FALSE。
8. 获取及设置文件时间
在Win CE中,文件时间包括3种:文件的创建时间,文件最后一次被访问(包括读写)的时间以及文件最后一次被修改的时间。函数GetFileTime()和SetFileTime()可以一次性获取及设置这3个时间,这两个函数都是通过文件句柄进行操作(这意味着必须首先打开指定文件),函数调用成功返回TRUE,失败则返回FALSE:
BOOL GetFileTime(
HANDLE hFile,
LPFILETIME lpCreationTime,
LPFILETIME lpLastAccessTime,
LPFILETIME lpLastWriteTime
);
BOOL SetFileTime(
HANDLE hFile,
const FILETIME* lpCreationTime,
const FILETIME* lpLastAccessTime,
const FILETIME* lpLastWriteTime
);
l 参数hFile为之前打开的文件句柄。
l 参数lpCreationTime, lpLastAccessTime, lpLastWriteTime分别代表文件的创建时间、最后一次被访问时间和最后一次被修改时间,对于不想关注的时间信息,可以将其设置为NULL。它们都是指向FILETIME结构体的指针,这个结构体使用两个32位的成员来代表一个64位的时间值,具体为相对于1601年1月1日的时间间隔长度,单位为100纳秒:
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
注意这里返回的文件时间默认都使用UTC(格林威治时间),而不是读者所在时区的时间(比如北京时间)。为了得到本地时间信息,可以调用函数FileTimeToLocalFileTime()实现:
BOOL FileTimeToLocalFileTime(
const FILETIME* lpFileTime,
LPFILETIME lpLocalFileTime
);
l 参数lpFileTime为默认的UTC时间
l 参数lpLocalFileTime为输出参数,返回对应的本地时间,也是一个指向FILETIME结构体的指针。
尽管返回的FILETIME结构体能够有效地代表文件时间信息,但是它却不那么具有可读性,比如说不能直接看出日期信息(除非读者知道它的编码格式)。实际上,在Win CE中提供了一个更具可读性代表时间的数据结构SYSTEMTIME,称为系统时间:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME;
这个结构体一共提供了8个域,从它的名字读者可以很容易了解它所代表的涵义。函数FileTimeToSystemTime()实现将上面返回的文件时间信息转换为系统时间格式:
BOOL FileTimeToSystemTime(
const FILETIME* lpFileTime,
LPSYSTEMTIME lpSystemTime
);
l 参数lpFileTime为上面返回的文件时间。
l 参数lpSystemTime为输出参数,返回转换得到的系统时间信息。
9. 获取文件大小信息
很多时候,在打开文件句柄之后,需要获取文件的大小信息,这样才能更好地管理,比如分配读写缓冲区的大小。在Win CE中通过调用GetFileSize()函数可以实现:
DWORD GetFileSize(
HANDLE hFile,
LPDWORD lpFileSizeHigh
);
l 参数hFile指定文件句柄
l 参数lpFileSizeHigh为输出参数,返回大小的高32位部分,如果文件大小超过4GB的话。可以将这个参数设置为NULL,如果不需要高半部分大小信息的话。对于小文件,应该将这个参数设置为NULL。
如果参数lpFileSizeHigh被设置为NULL,那么在函数调用成功后,返回文件的大小,单位为字节;返回0xFFFFFFFF表示失败。如果参数lpFileSizeHigh没有被设置为NULL,那么返回0xFFFFFFFF是合法的,需要进一步调用GetLastError()函数来判断是否出错,NO_ERROR表示没有出错。下面的例子演示怎么合理地判断函数执行是否出错:
//
// 第一种情况,将参数lpFileSizeHigh设置为NULL
// 获取文件大小信息
dwSize = GetFileSize (hFile, NULL);
// 此时返回0xFFFFFFFF表示出错
if (dwSize == 0xFFFFFFFF)
{
// 获取错误信息,进行出错处理
dwError = GetLastError();
…
}
//
// 第二种情况,参数lpFileSizeHigh没有设置为NULL
dwSizeLow = GetFileSize (hFile, & dwSizeHigh);
// 这时返回0xFFFFFFFF是合法的,需进一步判断GetLastError()
if (dwSizeLow == 0xFFFFFFFF && (dwError = GetLastError()) != NO_ERROR )
{
// 不为NO_ERROR,出错,进行出错处理
…
}
10. 集大成者
上面分别介绍了如何获取文件的属性,时间及大小等信息。有些时候应用程序可能需要获取文件的全部信息,这时如果还一个一个地获取比较繁琐,所幸Win CE提供了一个集大成的GetFileInformationByHandle()函数,一次性返回这些信息,通过文件句柄操作:
BOOL GetFileInformationByHandle(
HANDLE hFile,
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
l 参数hFile指定文件句柄。
l 参数lpFileInformation为输出参数,返回有关文件的信息。这是一个BY_HANDLE_FILE_INFORMATION结构体,定义如下:
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;
DWORD dwOID;
} BY_HANDLE_FILE_INFORMATION;
这里就不再对这些域一一介绍,读者根据域的名字可以很容易理解它的涵义,只介绍几个新的信息:
l 域dwVolumeSerialNumber返回文件所在的卷的序列号
l 域nNumberOfLinks返回文件的链接数,对FAT文件系统,总是返回1。
l 域nFileIndexHigh和nFileIndexLow返回文件的全局标识,可用于判断两个文件句柄是否指向同一个文件。
l 域dwOID返回文件的全局对象标识,这个域只对对象存储文件有效。
11. 一个获取指定目录下所有文件的信息的例子
下面的例子实现列举指定目录下的所有文件信息,包括文件名字,大小,最后被访问时间,其实可以将文件的更多属性列举出来,这里作为示例就给出这三种属性信息。在介绍程序实现之前,先介绍如何列举访问指定目录下的文件,这里需要用到两个Win32 API函数FindFirstFile()和FindNextFile()。
函数FindFirstFile()搜索指定目录下具有特定名字的文件,这个函数原型如下:
HANDLE FindFirstFile(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData
);
l 参数lpFileName为一个字符串指针,指定一个合法的目录或文件路径,可以使用通配符,如’*’代表任意字符串,’?’代表任意一个字符。比如为了获取目录下所有扩展名为.jpg的文件,可以使用”目录/*.jpg”。
l 参数lpFindFileData为输出参数,返回找到的文件或子目录信息。
函数执行成功,将返回目录的搜索句柄,这个句柄将用于下面继续枚举文件。否则返回INVALID_HANDLE_VALUE,表示失败。
函数FindNextFile()用于继续枚举下一个文件,函数原型为:
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
l 参数hFindFile为之前FindFirstFile()或FindNextFile()函数返回的搜索句柄。
l 参数lpFindFileData用于接收返回的文件信息。
函数执行成功,返回TRUE;否则返回FALSE,调用GetLastError()获取出错信息。当搜索完所有的文件后,将返回ERROR_NO_MORE_FILES。
搜索所有文件的实现方式是:首先调用FindFirstFile()函数打开搜索句柄,并返回第一个文件的信息;然后使用前面返回的搜索句柄循环调用FindNextFile()函数直至搜索完所有文件。
使用完文件搜索句柄后,需要调用FindClose()函数将其关闭。
下面结合一个具体的例子来说明如何遍历所有的文件信息,这个程序的运行界面如下:
图7.1 搜索指定目录下所有文件信息的运行结果
程序运行,用户在上方的文本框内输入一个合法的目录,单机下面的”Extrace Files”按钮,应用程序会自动为路径后添加后缀”\*.*”表示搜索所有文件。”Extrace Files”按钮的单击事件处理函数实现如下:
void CFileExtractDlg::OnBnClickedButtonExtractfiles()
{
// TODO: Add your control notification handler code here
CString dirName;
WIN32_FIND_DATA fileData;
HANDLE hFindFile = INVALID_HANDLE_VALUE;
CString outFileAttris = _T("");
DWORD dwError;
m_ListOutFiles.ResetContent();
//从控件获取指定目录
m_EditDirectory.GetWindowTextW(dirName);
//搜索所有文件
dirName.Format(_T("%s\\*.*"),dirName);
MessageBox(dirName);
int fileCount = 0;
hFindFile = FindFirstFile(dirName,&fileData);
if( hFindFile != INVALID_HANDLE_VALUE )
{
outFileAttris.Format(_T("%-64s%-16s%-32s"),_T("FileName"),_T("Size"),_T("LastAccessTime"));
m_ListOutFiles.AddString(outFileAttris);
outFileAttris.Format(_T("-------------------------------------------------------"));
m_ListOutFiles.AddString(outFileAttris);
do{
//不为目录
if((fileData.dwFileAttributes | FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY )
{
fileCount++;
CTime ctime(fileData.ftLastAccessTime); //获取文件的最后访问时间
CString strTime;
strTime = ctime.Format(_T("%Y-%m-%d %H:%M:%S")); //转换成字符串
//在结果中,列出文件的名称,大小,最后访问时间信息
outFileAttris.Format(_T("%-64s%-16d%-32s"), fileData.cFileName, fileData.nFileSizeLow, strTime);
m_ListOutFiles.AddString(outFileAttris);
}
}while( FindNextFile(hFindFile, &fileData) );
CString strFCount;
strFCount.Format(_T("%d files"),fileCount);
AfxMessageBox(strFCount);
//查询错误信息
dwError = GetLastError();
//关闭搜索句柄
FindClose(hFindFile);
//如果为ERROR_NO_MORE_FILES,目录下没有其它文件,正常结束;否则输出错误信息
if (dwError != 0 && dwError != ERROR_NO_MORE_FILES)
{
CString errMsg;
errMsg.Format(_T("FindNextFile error. Error is %u."), dwError);
AfxMessageBox(errMsg);
}
}
else{
AfxMessageBox(_T("Invalid dir or it is an empty dir"));
}
}
转载于:https://blog.51cto.com/8382359/1342186