研究一下如何枚举目录下的全部文件,查到了2个函数 ShellFindFirstFile 和 ShellFindNextFile。下面就研究如何使用这两个函数。
这两个函数在 \ShellPkg\Include\Library\ShellLib.h 中有定义
/** Retrieve first entry from a directory. This function takes an open directory handle and gets information from the first entry in the directory. A buffer is allocated to contain the information and a pointer to the buffer is returned in *Buffer. The caller can use ShellFindNextFile() to get subsequent directory entries. The buffer will be freed by ShellFindNextFile() when the last directory entry is read. Otherwise, the caller must free the buffer, using FreePool, when finished with it. @param[in] DirHandle The file handle of the directory to search. @param[out] Buffer The pointer to the buffer for the file's information. @retval EFI_SUCCESS Found the first file. @retval EFI_NOT_FOUND Cannot find the directory. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @return Others Status of ShellGetFileInfo, ShellSetFilePosition, or ShellReadFile. @sa ShellReadFile **/ EFI_STATUS EFIAPI ShellFindFirstFile ( IN SHELL_FILE_HANDLE DirHandle, OUT EFI_FILE_INFO **Buffer ); /** Retrieve next entries from a directory. To use this function, the caller must first call the ShellFindFirstFile() function to get the first directory entry. Subsequent directory entries are retrieved by using the ShellFindNextFile() function. This function can be called several times to get each entry from the directory. If the call of ShellFindNextFile() retrieved the last directory entry, the next call of this function will set *NoFile to TRUE and free the buffer. @param[in] DirHandle The file handle of the directory. @param[out] Buffer The pointer to buffer for file's information. @param[out] NoFile The pointer to boolean when last file is found. @retval EFI_SUCCESS Found the next file, or reached last file @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. **/ EFI_STATUS EFIAPI ShellFindNextFile( IN SHELL_FILE_HANDLE DirHandle, OUT EFI_FILE_INFO *Buffer, OUT BOOLEAN *NoFile ) { // // pass to file handle lib // return (FileHandleFindNextFile(DirHandle, Buffer, NoFile)); }
阅读说明可以发现,调用之后会分配一个Buffer,然后由调用者来负责释放。
开始写程序之后很快问题就来了,下面这样很简单的程序会让我的模拟器一次次崩溃。
int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_FILE_HANDLE DirHandle; RETURN_STATUS Status; EFI_FILE_INFO *FileInfo = NULL; BOOLEAN NoFile=FALSE; Status = ShellOpenFileByName(L"fsnt0:\\testz", (SHELL_FILE_HANDLE *)&DirHandle, EFI_FILE_MODE_READ , 0); if(Status != RETURN_SUCCESS) { Print(L"OpenFile failed!\n"); return EFI_SUCCESS; } ShellFindFirstFile(DirHandle,&FileInfo); PrintFileInfo(FileInfo); free(FileInfo); while (FALSE==NoFile) { Status=ShellFindNextFile(DirHandle,FileInfo,&NoFile); if (EFI_SUCCESS == Status) { } Print(L"No File[%d]\n",NoFile); } return EFI_SUCCESS; }
百思不得其解,在程序中加入输出函数,跟踪一下。出错的部分在ShellFindNextFile(DirHandle,FileInfo,&NoFile); 中。追踪进入 \ShellPkg\Library\UefiFileHandleLib\UefiFileHandleLib.c 的 FileHandleFindNextFile。
/** Retrieve next entries from a directory. To use this function, the caller must first call the FileHandleFindFirstFile() function to get the first directory entry. Subsequent directory entries are retrieved by using the FileHandleFindNextFile() function. This function can be called several times to get each entry from the directory. If the call of FileHandleFindNextFile() retrieved the last directory entry, the next call of this function will set *NoFile to TRUE and free the buffer. @param[in] DirHandle The file handle of the directory. @param[out] Buffer The pointer to buffer for file's information. @param[out] NoFile The pointer to boolean when last file is found. @retval EFI_SUCCESS Found the next file, or reached last file @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. **/ EFI_STATUS EFIAPI FileHandleFindNextFile( IN EFI_FILE_HANDLE DirHandle, OUT EFI_FILE_INFO *Buffer, OUT BOOLEAN *NoFile ) { EFI_STATUS Status; UINTN BufferSize; // // ASSERTs for DirHandle or Buffer or NoFile poitners being NULL // ASSERT (DirHandle != NULL); ASSERT (Buffer != NULL); ASSERT (NoFile != NULL); // // This BufferSize MUST stay equal to the originally allocated one in GetFirstFile // BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE; // // read in the info about the next file // Status = FileHandleRead (DirHandle, &BufferSize, Buffer); ASSERT(Status != EFI_BUFFER_TOO_SMALL); if (EFI_ERROR(Status)) { return (Status); } // // If we read 0 bytes (but did not have erros) we already read in the last file. // if (BufferSize == 0) { FreePool(Buffer); *NoFile = TRUE; } return (EFI_SUCCESS); }
确定是 FreePool(Buffer); 导致的。看到这个代码感觉很奇怪,明明是让 caller 负责释放,为什么他在里面要干这事情?再阅读函数说明“ If the call of
FileHandleFindNextFile() retrieved the last directory entry, the next call of
this function will set *NoFile to TRUE and free the buffer.” 原来这个函数设计的意思是:调用FileHandleFindNextFile 不断取得文件时,如果你中间停止了,那么请自行释放Buffer;但是如果你一直调用到了最后一个文件,那么函数本身会帮你释放掉而不需要你来做。
最终的程序不复杂
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_SHELL_PROTOCOL *gEfiShellProtocol; void PrintFileInfo(EFI_FILE_INFO *FileInfo) { Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize); Print(L"PhysicalSize [%ld] bytes\n",FileInfo-> PhysicalSize); Print(L"File Create date [%d-%d-%d %d-%d-%d]\n", FileInfo-> CreateTime.Year, FileInfo-> CreateTime.Month, FileInfo-> CreateTime.Day, FileInfo-> CreateTime.Hour, FileInfo-> CreateTime.Minute, FileInfo-> CreateTime.Second); Print(L"File last accessed date [%d-%d-%d %d-%d-%d]\n", FileInfo-> LastAccessTime.Year, FileInfo-> LastAccessTime.Month, FileInfo-> LastAccessTime.Day, FileInfo-> LastAccessTime.Hour, FileInfo-> LastAccessTime.Minute, FileInfo-> LastAccessTime.Second); Print(L"File last modification date [%d-%d-%d %d-%d-%d]\n", FileInfo-> ModificationTime.Year, FileInfo-> ModificationTime.Month, FileInfo-> ModificationTime.Day, FileInfo-> ModificationTime.Hour, FileInfo-> ModificationTime.Minute, FileInfo-> ModificationTime.Second); Print(L"File Name [%s]\n",&FileInfo->FileName[0]); } int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_FILE_HANDLE DirHandle; RETURN_STATUS Status; EFI_FILE_INFO *FileInfo = NULL; BOOLEAN NoFile=FALSE; Status = ShellOpenFileByName(L"fsnt0:", (SHELL_FILE_HANDLE *)&DirHandle, EFI_FILE_MODE_READ , 0); if(Status != RETURN_SUCCESS) { Print(L"OpenFile failed!\n"); return EFI_SUCCESS; } ShellFindFirstFile(DirHandle,&FileInfo); while (TRUE!=NoFile) { PrintFileInfo(FileInfo); Status=ShellFindNextFile(DirHandle,FileInfo,&NoFile); } return EFI_SUCCESS; }
运行结果
完整代码下载
请问你的efi文件是怎么编译出来的,《UEFI原理与编程》中说使用main 函数的应用程序工程模块在AppPkg 环境下才能成功编译,但我没找到AppPkg
看一下 http://www.lab-z.com/how-to-use-eadk/ 这篇文章
AppPkg 是 EADK的一部分。
Question
HOW TO OPEN VOLUME FS2: \ ??
Question
HOW TO OPEN VOLUME FS2: \ ??
You can try "Status = ShellOpenFileByName(L"fs2:", (SHELL_FILE_HANDLE *)&DirHandle, EFI_FILE_MODE_READ , 0);"
thank so much
i'll try Another method
uefiman entry point .....
EFI FILE PROTOCOL * ROOT
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, Root);
Status = Root ->Open(
Root,
& SystemFile,
(CHAR16*)L"TEST.TXT",
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0
);
fs0:\ file only accessible
fs2:\test.txt file access method please......
So your queston is "how to use OpenVolume function to access the file on FS2?"
Right!!
how to OpenVolume fs2:\\ XXXX.txt
I have written you a email. Please check your mail box.