很多时候我们编写的一些工具需要支持暂停的功能,比如:ls 列出的文件名时最好能够响应用户的按键,暂停一下以便用户查看结果。查看了一下Shell方面的代码,可以通过 Shell Environment 2 提供的函数来实现。
当然,我不愿意使用庞大的 Shell Library,选择性的提取一些代码就OK
// // PauseTest.c // #include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/UefiApplicationEntryPoint.h> #include <Library/BaseMemoryLib.h> #define SHELL_INTERFACE_PROTOCOL \ { \ 0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \ } EFI_GUID ShellInterfaceProtocol = SHELL_INTERFACE_PROTOCOL; EFI_GUID SEGuid = EFI_SE_EXT_SIGNATURE_GUID; // // The shell environment is provided by a driver. The shell links to the // shell environment for services. In addition, other drivers may connect // to the shell environment and add new internal command handlers, or // internal protocol handlers. // #define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \ { \ 0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \ } EFI_GUID ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL; #define EFI_OUTPUT_PAUSE 0x00000002 typedef struct { SHELLENV_EXECUTE Execute; // Execute a command line SHELLENV_GET_ENV GetEnv; // Get an environment variable SHELLENV_GET_MAP GetMap; // Get mapping tables SHELLENV_ADD_CMD AddCmd; // Add an internal command handler SHELLENV_ADD_PROT AddProt; // Add protocol info handler SHELLENV_GET_PROT GetProt; // Get the protocol ID SHELLENV_CUR_DIR CurDir; SHELLENV_FILE_META_ARG FileMetaArg; SHELLENV_FREE_FILE_LIST FreeFileList; // // The following services are only used by the shell itself // SHELLENV_NEW_SHELL NewShell; SHELLENV_BATCH_IS_ACTIVE BatchIsActive; SHELLENV_FREE_RESOURCES FreeResources; } EFI_SHELL_ENVIRONMENT; EFI_SHELL_INTERFACE *SI; EFI_SHELL_ENVIRONMENT *SE; EFI_SHELL_ENVIRONMENT2 *SE2; EFI_BOOT_SERVICES *gBS; EFI_RUNTIME_SERVICES *gRT; EFI_SYSTEM_TABLE *gST; //Copy from \Shell\Library\Misc.c BOOLEAN GrowBuffer ( IN OUT EFI_STATUS *Status, IN OUT VOID **Buffer, IN UINTN BufferSize ) /*++ Routine Description: Helper function called as part of the code needed to allocate the proper sized buffer for various EFI interfaces. Arguments: Status - Current status Buffer - Current allocated buffer, or NULL BufferSize - Current buffer size needed Returns: TRUE - if the buffer was reallocated and the caller should try the API again. --*/ { BOOLEAN TryAgain; // // If this is an initial request, buffer will be null with a new buffer size // if (NULL == *Buffer && BufferSize) { *Status = EFI_BUFFER_TOO_SMALL; } // // If the status code is "buffer too small", resize the buffer // TryAgain = FALSE; if (*Status == EFI_BUFFER_TOO_SMALL) { if (*Buffer) { FreePool (*Buffer); } *Buffer = AllocateZeroPool (BufferSize); if (*Buffer) { TryAgain = TRUE; } else { *Status = EFI_OUT_OF_RESOURCES; } } // // If there's an error, free the buffer // if (!TryAgain && EFI_ERROR (*Status) && *Buffer) { FreePool (*Buffer); *Buffer = NULL; } return TryAgain; } //Copy from \Shell\Library\Handle.c EFI_STATUS LocateHandle ( IN EFI_LOCATE_SEARCH_TYPE SearchType, IN EFI_GUID * Protocol OPTIONAL, IN VOID *SearchKey OPTIONAL, IN OUT UINTN *NoHandles, OUT EFI_HANDLE **Buffer ) /*++ Routine Description: Function returns an array of handles that support the requested protocol in a buffer allocated from pool. Arguments: SearchType - Specifies which handle(s) are to be returned. Protocol - Provides the protocol to search by. This parameter is only valid for SearchType ByProtocol. SearchKey - Supplies the search key depending on the SearchType. NoHandles - The number of handles returned in Buffer. Buffer - A pointer to the buffer to return the requested array of handles that support Protocol. Returns: EFI_SUCCESS - The result array of handles was returned. EFI_NOT_FOUND - No handles match the search. EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results. --*/ { EFI_STATUS Status; UINTN BufferSize; // // Initialize for GrowBuffer loop // Status = EFI_SUCCESS; *Buffer = NULL; BufferSize = 50 * sizeof (EFI_HANDLE); // // Call the real function // while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) { Status = gBS->LocateHandle ( SearchType, Protocol, SearchKey, &BufferSize, *Buffer ); } *NoHandles = BufferSize / sizeof (EFI_HANDLE); if (EFI_ERROR (Status)) { *NoHandles = 0; } return Status; } INTN CompareGuidx ( IN EFI_GUID *Guid1, IN EFI_GUID *Guid2 ) /*++ Routine Description: Compares to GUIDs Arguments: Guid1 - guid to compare Guid2 - guid to compare Returns: = 0 if Guid1 == Guid2 != 0 if Guid1 != Guid2 --*/ { INT32 *g1; INT32 *g2; INT32 r; // // Compare 32 bits at a time // g1 = (INT32 *) Guid1; g2 = (INT32 *) Guid2; r = g1[0] - g2[0]; r |= g1[1] - g2[1]; r |= g1[2] - g2[2]; r |= g1[3] - g2[3]; return r; } // Copy from \Shell\Library\Init.c EFI_STATUS LibInitializeShellApplication ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer; UINTN HandleNum; UINTN HandleIndex; EFI_GUID SESGuid = EFI_SE_EXT_SIGNATURE_GUID; // // Connect to the shell interface // Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI); if (EFI_ERROR (Status)) { Print (L"InitShellApp: Application not started from Shell\n"); gBS->Exit (ImageHandle, Status, 0, NULL); } // // Connect to the shell environment // Status = gBS->HandleProtocol ( ImageHandle, &ShellEnvProtocol, (VOID *) &SE2 ); if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 && (SE2->MajorVersion > EFI_SHELL_MAJOR_VER || (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= EFI_SHELL_MINOR_VER)) ) ) { Status = LocateHandle ( ByProtocol, &ShellEnvProtocol, NULL, &HandleNum, &HandleBuffer ); if (EFI_ERROR (Status)) { Print (L"InitShellApp: 1Shell environment interfaces not found\n"); gBS->Exit (ImageHandle, Status, 0, NULL); } Status = EFI_NOT_FOUND; for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { gBS->HandleProtocol ( HandleBuffer[HandleIndex], &ShellEnvProtocol, (VOID *) &SE2 ); if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0) { Status = EFI_SUCCESS; break; } } FreePool (HandleBuffer); if (EFI_ERROR (Status)) { Print (L"InitShellApp: 2Shell environment interfaces not found\n"); gBS->Exit (ImageHandle, Status, Status, NULL); } } SE = (EFI_SHELL_ENVIRONMENT *) SE2; // // Done with init // return Status; } // // Entry point function // EFI_STATUS UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { INTN i; Print(L"You can't Pause by Tab key\n"); for (i=0;i<1000;i++) { Print(L"."); } Print(L".\n"); gBS = SystemTable -> BootServices; LibInitializeShellApplication (ImageHandle,SystemTable); SE2->SetKeyFilter(SE2->GetKeyFilter() | EFI_OUTPUT_PAUSE); Print(L"You can Pause by Tab key"); for (i=0;i<1000;i++) { Print(L"."); } return EFI_SUCCESS; }
上面的代码演示了使用 Pause Break键来暂停输出的功能。
代码在这里下载: