之前,我们通过修改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;
}
工作的视频,看得出来这样的方法适应性更好
完整代码下载