之前,我们通过修改Shell的代码,能够实现在右上角不断显示当前时间的功能。但是这个方法过于复杂。于是我请教了一下 HZZZ ,看看他是否有什么好办法。
经过他的研究,还是使用定时器的方式不断触发,困难点在于如果退出时没有对应的 Handle,再次触发会导致问题。解决这个问题的方法是:让程序在内存中重新加载自己,跳进去执行之后,最开始的部分就可以丢弃掉了。
当然,如果你想读懂这个程序,要先读懂前面几篇 《Step to UEFI>> 系列的文章。
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <time.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> #include <Protocol/SimpleFileSystem.h> #include <Protocol/BlockIo.h> #include <Library/DevicePathLib.h> #include <Library/HandleParsingLib.h> #include <Library/SortLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/BaseMemoryLib.h> #include <Protocol/LoadedImage.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2; extern EFI_HANDLE gImageHandle; STATIC CONST UINTN SecondsToNanoSeconds = 500000; /** The callback function for the timer event used to get map. @param[in] Event The event this function is registered to. @param[in] Context The context registered to the event. **/ VOID EFIAPI Timeout ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_TIME ET; UINTN x; UINTN y; //Get cursor postion x = gST->ConOut->Mode->CursorColumn; y = gST->ConOut->Mode->CursorRow; //Move cursor to Up-Left gST -> ConOut -> SetCursorPosition(gST -> ConOut,70,0); //Output current time gRT->GetTime(&ET, NULL); Print(L"%2d:%2d:%2d",ET.Hour,ET.Minute,ET.Second); //Move cursor back gST -> ConOut -> SetCursorPosition(gST -> ConOut,x,y); return ; } typedef struct { UINTN Signature; /// Image handle EFI_HANDLE Handle; /// Image type UINTN Type; /// If entrypoint has been called BOOLEAN Started; /// The image's entry point EFI_IMAGE_ENTRY_POINT EntryPoint; /// loaded image protocol EFI_LOADED_IMAGE_PROTOCOL Info; } LOADED_IMAGE_PRIVATE_DATA_TEMP; #define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))) #define LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(a) \ _CR(a, LOADED_IMAGE_PRIVATE_DATA_TEMP, Info) typedef void (*Fun)(); void function() { EFI_STATUS Status; EFI_HANDLE TimerOne = NULL; printf("function called\n"); Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL | EVT_TIMER, TPL_CALLBACK, Timeout, NULL, &TimerOne ); if (EFI_ERROR (Status)) { Print(L"Create Event Error! \r\n"); return ; } Status = gBS->SetTimer ( TimerOne, TimerPeriodic, MultU64x32 (SecondsToNanoSeconds, 1) ); if (EFI_ERROR (Status)) { Print(L"Set Timer Error! \r\n"); return ; } } int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_STATUS Status = EFI_SUCCESS; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo = NULL; EFI_HANDLE Handle = 0; EFI_GUID gEfiLoadedImageProtocolGuid = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; LOADED_IMAGE_PRIVATE_DATA_TEMP *private = NULL; Fun fun; UINTN FunOffset; UINTN FunAddr; Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo); // function offset in the old image FunOffset = (UINTN)function - (UINTN)ImageInfo->ImageBase; // load the image in memory again Status = gBS->LoadImage(FALSE, gImageHandle, NULL, ImageInfo->ImageBase, (UINTN)ImageInfo->ImageSize, &Handle); // get the newer imageinfo Status = gBS->HandleProtocol (Handle, &gEfiLoadedImageProtocolGuid, &ImageInfo); private = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(ImageInfo); FunAddr = (UINTN)FunOffset + (UINTN)ImageInfo->ImageBase; fun = (Fun)((UINTN)FunOffset + (UINTN)ImageInfo->ImageBase); // called the newer function in new image,the new image will be always in memory because it will not be free fun(); return EFI_SUCCESS; }
工作的视频,看得出来这样的方法适应性更好
完整代码下载