之前有网友询问过一个问题:他的程序调用了另外一个 Application,希望能够获得另外那个 Application的运行结果。最近,偶然看到了 Shell 下记录历史信息的功能,具体头文件在\ShellPkg\Application\Shell\ConsoleLogger.h 中有定义。
typedef struct _CONSOLE_LOGGER_PRIVATE_DATA{ UINTN Signature; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut; ///< the protocol we installed onto the system table EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut; ///< old protocol to reinstall upon exiting EFI_HANDLE OldConHandle; ///< old protocol handle UINTN ScreenCount; ///< How many screens worth of data to save CHAR16 *Buffer; ///< Buffer to save data UINTN BufferSize; ///< size of buffer in bytes // start row is the top of the screen UINTN OriginalStartRow; ///< What the originally visible start row was UINTN CurrentStartRow; ///< what the currently visible start row is UINTN RowsPerScreen; ///< how many rows the screen can display UINTN ColsPerScreen; ///< how many columns the screen can display INT32 *Attributes; ///< Buffer for Attribute to be saved for each character UINTN AttribSize; ///< Size of Attributes in bytes EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode; ///< mode of the history log BOOLEAN Enabled; ///< Set to FALSE when a break is requested. UINTN RowCounter; ///< Initial row of each print job. } CONSOLE_LOGGER_PRIVATE_DATA;
这个功能实现的方法是,在 Shell 启动的时,将系统中的EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 题材换成重新定义的,这样每次Application的输出都会被Shell截获存放在一个Buffer中,有需要的时候,再从 Buffer中取出(例如:使用 PageUp/PageDown 查看)。
对于我们来说,首先找到 Shell 的 Handle,之后枚举这个 Handle 上的全部protocol,找到SimpleTextOutProtocol,再根据找到的这个Protocol的偏移,反推算出CONSOLE_LOGGER_PRIVATE_DATA,最后就能访问到我们需要的CONSOLE_LOGGER_PRIVATE_DATA->buffer了。
代码如下,为了简单明了,我只显示Buffer最前面4行
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/BaseMemoryLib.h> #include <Library/MemoryAllocationLib.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_HANDLE gImageHandle; #define CR(Record, TYPE, Field, TestSignature) \ BASE_CR (Record, TYPE, Field) typedef struct _CONSOLE_LOGGER_PRIVATE_DATA{ UINTN Signature; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut; ///< the protocol we installed onto the system table EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut; ///< old protocol to reinstall upon exiting EFI_HANDLE OldConHandle; ///< old protocol handle UINTN ScreenCount; ///< How many screens worth of data to save CHAR16 *Buffer; ///< Buffer to save data UINTN BufferSize; ///< size of buffer in bytes // start row is the top of the screen UINTN OriginalStartRow; ///< What the originally visible start row was UINTN CurrentStartRow; ///< what the currently visible start row is UINTN RowsPerScreen; ///< how many rows the screen can display UINTN ColsPerScreen; ///< how many columns the screen can display INT32 *Attributes; ///< Buffer for Attribute to be saved for each character UINTN AttribSize; ///< Size of Attributes in bytes EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode; ///< mode of the history log BOOLEAN Enabled; ///< Set to FALSE when a break is requested. UINTN RowCounter; ///< Initial row of each print job. } CONSOLE_LOGGER_PRIVATE_DATA; #define CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(a) CR (a, CONSOLE_LOGGER_PRIVATE_DATA, OurConOut, CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE) int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer=NULL; UINTN BufferSize=0,i; EFI_HANDLE TheHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Dev; CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; // Look up all gEfiShellProtocolGuid in the platform Status = gBS->LocateHandle( ByProtocol, &gEfiShellProtocolGuid, NULL, &BufferSize, HandleBuffer); if (Status == EFI_BUFFER_TOO_SMALL) { HandleBuffer = AllocateZeroPool(BufferSize); if (HandleBuffer == NULL) { return (EFI_BUFFER_TOO_SMALL);} //Get all the gEfiShellProtocolGuid Protocol in the system Status = gBS->LocateHandle( ByProtocol, &gEfiShellProtocolGuid, NULL, &BufferSize, HandleBuffer); } //Print(L"%d handles have been found!\n",(BufferSize / sizeof(EFI_HANDLE))); if (BufferSize==0) { Print(L"No gEfiShellProtocolGuid found!\n"); return EFI_SUCCESS; } //In fact there will be only one in system (in the Shell image) TheHandle=HandleBuffer[0]; FreePool(HandleBuffer); //Get SimpleTextOutProtocol used by Shell Status = gBS->HandleProtocol ( TheHandle, &gEfiSimpleTextOutProtocolGuid, &Dev); if (EFI_ERROR(Status)) { Print(L"Error when opening SimpleTextOutProtocol\n"); return EFI_SUCCESS; } ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(Dev); for (i=0;i<(ConsoleInfo->ColsPerScreen+2)*4;i++) { if (ConsoleInfo->Buffer[i] != 0) {Print(L"%c",ConsoleInfo->Buffer[i]);} } Print(L"\n"); return EFI_SUCCESS; }
运行结果,这是NT32模拟环境中的运行结果。同样我还在 Intel ApolloLake 上进行了实验,结果相同。
完整的代码下载:
编译后的Application下载(IA32和X64)
Hi LABz
your attached zip file can not be extracted.
thx
Hi, yes, there is something wrong. I will fix it. Thanks a lot.