---
description: 이 장에서는 UNI 변환을 시스템에 동적으로 추가하려 한다.
---

# 54. EFI\_HII\_STRING\_PROTOCOL의 NewString 및 SetString 함수를 사용하여 다른 언어에 대한 문자열 패키지를 동적으로 추가

&#x20;예를 들어, 영어 문자열만 있는 `PlatformDxe`가 있다. [https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.uni](https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.uni)

```
#langdef en-US "English"

#string STR_FORMSET_TITLE        #language en-US "OVMF Platform Configuration"
#string STR_FORMSET_HELP         #language en-US "Change various OVMF platform settings."
#string STR_MAIN_FORM_TITLE      #language en-US "OVMF Settings"
#string STR_RES_CUR              #language en-US "Preferred Resolution at Next Boot"
#string STR_RES_CUR_HELP         #language en-US "The preferred resolution of the Graphics Console at next boot. It might be unset, or even invalid (hence ignored) wrt. the video RAM size."
#string STR_RES_NEXT             #language en-US "Change Preferred Resolution for Next Boot"
#string STR_RES_NEXT_HELP        #language en-US "You can specify a new preference for the Graphics Console here. The list is filtered against the video RAM size."
#string STR_SAVE_EXIT            #language en-US "Commit Changes and Exit"
#string STR_DISCARD_EXIT         #language en-US "Discard Changes and Exit"
```



&#x20;BIOS 메뉴에서 언어 기본 설정을 프랑스어로 변경하더라도 이러한 문자열은 여전히 영어로 출력된다. 확인해볼 수 있다.



&#x20;`Select Language` 옵션을 사용하여 언어를 `Francais`로 변경한다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FdLpAmWgHCSpoN9jf1A7Q%2Fimage.png?alt=media&#x26;token=38453571-17db-4c61-a59d-fac61d7e1a27" alt=""><figcaption></figcaption></figure>



&#x20;그런 다음 `Device Manager -> OVMF Platform Configuration`을 살펴보도록 한다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fy2kCGjcrMgMUUS2XNMJV%2Fimage.png?alt=media&#x26;token=84f0f256-d785-4fd0-899d-a6c3a42218ef" alt=""><figcaption></figcaption></figure>

&#x20;하나의 문자열 패키지에는 하나의 언어에 대한 번역된 문자열만 있는 것을 기억하고 있다. HII 요소가 여러 언어를 지원하는 경우, 패키지 목록에 여러 STRING 패키지가 있다. `PlatformDxe`의 경우 패키지 목록에 하나의 문자열 패키지만 있다.\
[https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.inf](https://github.com/tianocore/edk2/blob/master/OvmfPkg/PlatformDxe/Platform.inf)

```
PackageList[9]: GUID=D9DCC5DF-4007-435E-9098-8970935504B2; size=0x855
        Package[0]: type=FORMS; size=0x1F6
        Package[1]: type=STRINGS; size=0x62B
        Package[2]: type=DEVICE_PATH; size=0x1C
        Package[3]: type=END; size=0x4
```



&#x20;먼저 패키지 목록에서 가능한 모든 문자열을 출력해보겠다. 우리는 UNI 소스 코드를 보았지만, 일부 문자열은 시스템에서 동적으로 추가될 수 있으므로 이를 확인하는 것이 좋다.



&#x20;패키지 목록에서 문자열을 출력하려면, 패키지 목록의 `EFI_HII_HANDLE`이 필요하다. 이를 위해 `EFI_HII_DATABASE_PROTOCOL`에서 `ListPackageLists` 함수를 활용할 수 있다.

```
EFI_HII_DATABASE_PROTOCOL.ListPackageLists()

Summary:
Determines the handles that are currently active in the database.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_DATABASE_LIST_PACKS) (
 IN CONST EFI_HII_DATABASE_PROTOCOL *This,
 IN UINT8 PackageType,
 IN CONST EFI_GUID *PackageGuid,
 IN OUT UINTN *HandleBufferLength,
 OUT EFI_HII_HANDLE *Handle
 );

Parameters:
This			A pointer to the EFI_HII_DATABASE_PROTOCOL instance.
PackageType		Specifies the package type of the packages to list or EFI_HII_PACKAGE_TYPE_ALL
			for all packages to be listed.
PackageGuid		If PackageType is EFI_HII_PACKAGE_TYPE_GUID, then this is the pointer to the
			GUID which must match the Guid field of EFI_HII_GUID_PACKAGE_HDR. Otherwise, it must be NULL.
HandleBufferLength	On input, a pointer to the length of the handle buffer. On output, the length of the
			handle buffer that is required for the handles found.
Handle			An array of EFI_HII_HANDLE instances returned. Type EFI_HII_HANDLE is
			defined in EFI_HII_DATABASE_PROTOCOL.NewPackageList() in the Packages section.

Description:
This function returns a list of the package handles of the specified type that are currently active in the
database. The pseudo-type EFI_HII_PACKAGE_TYPE_ALL will cause all package handles to be listed
```



&#x20;이 함수를 사용하면 `HandleBufferLength=0`으로 한 번 호출하고, `EFI_BUFFER_TOO_SMALL` 오류를 수신하지만, `HandleBufferLength`에 대한 값을 가져와야 한다. 필요한 크기만을 할당하고, 이 함수를 다시 호출한다.

&#x20;말만 들었는데도 조금 지루한 과정이다.



&#x20;이 작업을 더 쉽게 하기 위해서 `HiiLib` 에서 `HiiGetHiiHandles` 함수를 활용해 보겠다.

```
/**
  Retrieves the array of all the HII Handles or the HII handles of a specific
  package list GUID in the HII Database.
  This array is terminated with a NULL HII Handle.
  This function allocates the returned array using AllocatePool().
  The caller is responsible for freeing the array with FreePool().
  @param[in]  PackageListGuid  An optional parameter that is used to request
                               HII Handles associated with a specific
                               Package List GUID.  If this parameter is NULL,
                               then all the HII Handles in the HII Database
                               are returned.  If this parameter is not NULL,
                               then zero or more HII Handles associated with
                               PackageListGuid are returned.
  @retval NULL   No HII handles were found in the HII database
  @retval NULL   The array of HII Handles could not be retrieved
  @retval Other  A pointer to the NULL terminated array of HII Handles
**/
EFI_HII_HANDLE *
EFIAPI
HiiGetHiiHandles (
  IN CONST EFI_GUID  *PackageListGuid  OPTIONAL
  )
```



&#x20;이것으로 우리의 애플리케이션은 다음과 같이 간단화할 수 있다.

```c
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

#include <Library/HiiLib.h>
#include <Library/MemoryAllocationLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_GUID PackageGuid = {0xD9DCC5DF, 0x4007, 0x435E, {0x90, 0x98, 0x89, 0x70, 0x93, 0x55, 0x04, 0xB2 }};
  EFI_HII_HANDLE* Handle = HiiGetHiiHandles(&PackageGuid);

  for (UINTN i=0; i<0xFFFF; i++) {
    EFI_STRING String = HiiGetString(*Handle, i, "en-US");
    if (String != NULL) {
      Print(L"ID=%d, %s\n", i, String);
    }
    FreePool(String);
  }
  return EFI_SUCCESS;
}
```



&#x20;여기 문자열을 얻기 위해 가능한 모든 문자열 토큰을 반복하여 `HiiGetString` 함수를 호출하는 것을 볼 수 있다.



&#x20;이 애플리케이션을 빌드하고 실행하면 다음과 같은 결과를 얻게 된다.

```
FS0:\> HIIAddLocalization.efi
ID=1, English
ID=2, OVMF Platform Configuration
ID=3, Change various OVMF platform settings.
ID=4, OVMF Settings
ID=5, Preferred Resolution at Next Boot
ID=6, The preferred resolution of the Graphics Console at next boot. It might be unset, or even invalid (hence ignored) wrt. the video RAM size.
ID=7, Change Preferred Resolution for Next Boot
ID=8, You can specify a new preference for the Graphics Console here. The list is filtered against the video RAM size.
ID=9, Commit Changes and Exit
ID=10, Discard Changes and Exit
ID=11, 640x480
ID=12, 800x480
ID=13, 800x600
ID=14, 832x624
ID=15, 960x640
ID=16, 1024x600
ID=17, 1024x768
ID=18, 1152x864
ID=19, 1152x870
ID=20, 1280x720
ID=21, 1280x760
ID=22, 1280x768
ID=23, 1280x800
ID=24, 1280x960
ID=25, 1280x1024
ID=26, 1360x768
ID=27, 1366x768
ID=28, 1400x1050
ID=29, 1440x900
ID=30, 1600x900
ID=31, 1600x1200
ID=32, 1680x1050
ID=33, 1920x1080
ID=34, 1920x1200
ID=35, 1920x1440
ID=36, 2000x2000
ID=37, 2048x1536
ID=38, 2048x2048
ID=39, 2560x1440
ID=40, 2560x1600
```



&#x20;프랑스어로 번역된 배열을 만들어 보자.

```
  CHAR16* FrenchStrings[] = {
    L"Configuration de la OVMF plateforme",
    L"Modifier divers paramètres de la plateforme OVMF",
    L"Paramètres OVMF",
    L"Résolution préférée au prochain démarrage",
    L"La résolution préférée de la console graphique au prochain démarrage. Il peut être non défini, ou même invalide (donc ignoré) wrt. la taille de la RAM vidéo.",
    L"Modifier la résolution préférée pour le prochain démarrage",
    L"Vous pouvez spécifier ici une nouvelle préférence pour la console graphique. La liste est filtrée en fonction de la taille de la RAM vidéo.",
    L"Valider les modifications et quitter",
    L"Annuler les changements et quitter",
    L"640x480",
    L"800x480",
    L"800x600",
    L"832x624",
    L"960x640",
    L"1024x600",
    L"1024x768",
    L"1152x864",
    L"1152x870",
    L"1280x720",
    L"1280x760",
    L"1280x768",
    L"1280x800",
    L"1280x960",
    L"1280x1024",
    L"1360x768",
    L"1366x768",
    L"1400x1050",
    L"1440x900",
    L"1600x900",
    L"1600x1200",
    L"1680x1050",
    L"1920x1080",
    L"1920x1200",
    L"1920x1440",
    L"2000x2000",
    L"2048x1536",
    L"2048x2048",
    L"2560x1440",
    L"2560x1600",
  };
```



&#x20;이제 문자열 출력 반복문에 대한 설명을 해보자면, 문자열 패키지를 생성하고 동일한 패키지 목록 아래 HII 데이터베이스에 채워야 한다. 여기서 우리는 한 가지 방법을 사용할 수 있다.

&#x20;manual 문자열 패키지를 생성하는 대신, 새 언어의 dummy인 `L""` 문자열을 사용하여 `gHiiString->NewString` 함수를 호출할 수 있다.

&#x20;`en-US` 문자열 패키지에 40개의 문자열이 있는 경우, ID=41인 문자열 `L""` 하나만 있는 `fr-FR` 문자열 패키지가 생성된다. 그리고 이 패키지는 자동으로 패키지 목록에 추가된다.



&#x20;`fr-FR` 문자열 패키지가 패키지 목록 있으면, 모든 데이터 문자열에 대해 `gHiiString->SetString` 함수를 간단히 호출할 수 있다.

```
  EFI_STATUS Status;
  for (UINTN i=0; i<(sizeof(FrenchStrings)/sizeof(FrenchStrings[0])); i++) {
    EFI_STRING_ID StringId;
    if (i==0) {
      Status = gHiiString->NewString(gHiiString, *Handle, &StringId, "fr-FR", L"French", L"", NULL);
      if (EFI_ERROR(Status)) {
        Print(L"Error! NewString fail\n");
      }
    }
    StringId = i+2;
    Status = gHiiString->SetString(gHiiString, *Handle, StringId, "fr-FR", FrenchStrings[i], NULL);
    if (EFI_ERROR(Status)) {
      Print(L"Error! SetString fail for ID=%d\n", StringId);
    }
  }
```



&#x20;다음은 `EFI_HII_STRING_PROTOCOL`의 `NewString` 및 `SetString` 함수에 대한 API 설명이다.

```
EFI_HII_STRING_PROTOCOL.SetString()

Summary:
Change information about the string.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_SET_STRING) (
 IN CONST EFI_HII_STRING_PROTOCOL *This,
 IN EFI_HII_HANDLE PackageList,
 IN EFI_STRING_ID StringId,
 IN CONST CHAR8 *Language,
 IN CONST EFI_STRING String,
 IN CONST EFI_FONT_INFO *StringFontInfo OPTIONAL
 );

Parameters:
This		A pointer to the EFI_HII_STRING_PROTOCOL instance.
PackageList	The package list containing the strings.
Language	Points to the language for the updated string.
StringId	The string id, which is unique within PackageList.
String		Points to the new null-terminated string.
StringFontInfo	Points to the string’s font information or NULL if the string font information is not changed.

Description:
This function updates the string specified by StringId in the specified PackageList to the text specified by
String and, optionally, the font information specified by StringFontInfo
```

```
EFI_HII_STRING_PROTOCOL.NewString()

Summary:
Creates a new string in a specific language and add it to strings from a specific package list.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_NEW_STRING) (
 IN CONST EFI_HII_STRING_PROTOCOL *This,
 IN EFI_HII_HANDLE PackageList,
 OUT EFI_STRING_ID *StringId
 IN CONST CHAR8 *Language,
 IN CONST CHAR16 *LanguageName OPTIONAL,
 IN CONST EFI_STRING String,
 IN CONST EFI_FONT_INFO *StringFontInfo
 );

Parameters:
This		A pointer to the EFI_HII_STRING_PROTOCOL instance.
PackageList	Handle of the package list where this string will be added.
Language	Points to the language for the new string.
LanguageName	Points to the printable language name to associate with the passed in Language
		field.
String		Points to the new null-terminated string.
StringFontInfo	Points to the new string’s font information or NULL if the string should have the
		default system font, size and style.
StringId	On return, contains the new strings id, which is unique within PackageList.

DescriptionL
This function adds the string String to the group of strings owned by PackageList, with the specified font information StringFontInfo and returns a new string id
```



&#x20;`gHiiString` 프로토콜을 직접 사용하기 위해 `UefiHiiServicesLib` 라이브러리 클래스와 해당 헤더 `Library/UefiHiiServicesLib.h`를 추가하는 것을 잊지 않도록 하자.



애플리케이션을 빌드하고 실행한다.

```
FS0:\> HIIAddLocalization.efi
FS0:\>
```



&#x20;그런 다음 `exit`으로 BIOS 메뉴로 이동한다.



&#x20;`Select Language` 옵션으로 언어를 `Francais`로 변경한다.

&#x20;그 다음, `DeviceManager -> OVMF Platform Configuration`으로 이동한다. 이미 번역이 완료되어 있음을 알 수 있다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FU4rMnOrSNPWLyfreaIo0%2Fimage.png?alt=media&#x26;token=affa7f1b-45fe-41a7-ab06-9a8e560fd73a" alt=""><figcaption></figcaption></figure>

&#x20;메뉴를 종료한 다음 다시 이동하면 `Device manager`의 문자열도 "Configuration de la OVMF plateforme"로 변경된 것을 볼 수 있다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Ff0sqI7pFEh3GwIWDcVTj%2Fimage.png?alt=media&#x26;token=dfd12646-577b-476c-9bbd-707d9b488f1d" alt=""><figcaption></figcaption></figure>
