之前有网友询问过一个问题:他的程序调用了另外一个 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.