Я обнаружил, что столкнулся с той же проблемой и спросил об этом при сбое сервера. Прошел хороший день гугл-спотыкания, прежде чем я собрал достаточно много, чтобы решить эту проблему. Здесь это идет:
- В Linux это было бы довольно просто, через efibootmgr
- EasyUEFI позволил бы мне делать то, что я тоже хочу - поддержка командной строки требует довольно дешевой лицензии; но я не чувствую себя прекрасно в зависимости от нишевого инструмента, особенно, если есть другие варианты.
- bcdedit на машине UEFI изменяет настройки UEFI. Я думаю, что это будет работать.
- Спецификация UEFI для порядка загрузки не слишком сложна. API - это просто GetVariable/SetVariable с переменными с именем BootOrder (для получения / установки списка параметров загрузки в порядке, в котором они будут опробованы) и Boot #### (для получения / установки информации о каждом параметре загрузки).
- Я понятия не имею, как я написал бы приложение Windows против API UEFI на окнах (любой?)
- Windows предоставляет API, который, помимо прочего, оборачивает UEFI GetVariable/SetVariable.
Как только я понял спецификацию UEFI для порядка загрузки и Windows API, код (C++, построенный для 64-битной системы, поскольку это все, что мы используем) был не так уж плох. Это должно быть встроено в исполняемый файл, который требует административных привилегий и статически связывает среду выполнения Windows, а затем я запускаю его в MDT после установки ОС перед перезагрузкой.
Во-первых, вы должны требовать привилегии для вызова API. Используйте маленький помощник:
struct CloseHandleHelper
{
void operator()(void *p) const
{
CloseHandle(p);
}
};
BOOL SetPrivilege(HANDLE process, LPCWSTR name, BOOL on)
{
HANDLE token;
if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &token))
return FALSE;
std::unique_ptr<void, CloseHandleHelper> tokenLifetime(token);
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
if (!LookupPrivilegeValueW(NULL, name, &tp.Privileges[0].Luid))
return FALSE;
tp.Privileges[0].Attributes = on ? SE_PRIVILEGE_ENABLED : 0;
return AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL);
}
затем позвоните
SetPrivilege(GetCurrentProcess(), SE_SYSTEM_ENVIRONMENT_NAME, TRUE));
Далее, получите список параметров загрузки (объединение значений uint16_t):
const int BUFFER_SIZE = 4096;
BYTE bootOrderBuffer[BUFFER_SIZE];
DWORD bootOrderLength = 0;
const TCHAR bootOrderName[] = TEXT("BootOrder");
const TCHAR globalGuid[] = TEXT("{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}");
DWORD bootOrderAttributes;
bootOrderLength = GetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, BUFFER_SIZE, &bootOrderAttributes);
if (bootOrderLength == 0)
{
std::cout << "Failed getting BootOrder with error " << GetLastError() << std::endl;
return 1;
}
Затем вы можете перебрать каждый параметр загрузки, сформировать для него имя переменной Boot #### и затем использовать его для получения структуры с информацией о параметре. Вы захотите узнать, имеет ли первая активная опция "Описание", равную "Диспетчеру загрузки Windows". Описание - строка широких символов с нулевым символом в конце по смещению 6 в структуре.
for (DWORD i = 0; i < bootOrderLength; i += 2)
{
std::wstringstream bootOptionNameBuilder;
bootOptionNameBuilder << "Boot" << std::uppercase << std::setfill(L'0') << std::setw(4) << std::hex << *reinterpret_cast<uint16_t*>(bootOrderBuffer + i);
std::wstring bootOptionName(bootOptionNameBuilder.str());
BYTE bootOptionInfoBuffer[BUFFER_SIZE];
DWORD bootOptionInfoLength = GetFirmwareEnvironmentVariableEx(bootOptionName.c_str(), globalGuid, bootOptionInfoBuffer, BUFFER_SIZE, nullptr);
if (bootOptionInfoLength == 0)
{
std::cout << "Failed getting option info for option at offset " << i << std::endl;
return 1;
}
uint32_t* bootOptionInfoAttributes = reinterpret_cast<uint32_t*>(bootOptionInfoBuffer);
//First 4 bytes make a uint32_t comprised of flags. 0x1 means the boot option is active (not disabled)
if (((*bootOptionInfoAttributes) & 0x1) != 0)
{
std::wstring description(reinterpret_cast<wchar_t*>(bootOptionInfoBuffer + sizeof(uint32_t) + sizeof(uint16_t)));
bool isWBM = boost::algorithm::to_upper_copy<std::wstring>(description) == L"WINDOWS BOOT MANAGER";
// details - keep track of the value of i for the first WBM and non-WBM options you find, and the fact that you found them
}
}
Теперь, если вы нашли активные параметры загрузки WBM и не WBM, а первая опция WBM находится в wbmOffset, а первая опция не WBM - nonWBMOffset, с wbmOffset <nonWBMOffset, поменяйте местами записи в переменной BootOrder следующим образом:
uint16_t *wbmBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + wbmOffset);
uint16_t *nonWBMBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + nonWBMOffset);
std::swap(*wbmBootOrderEntry, *nonWBMBootOrderEntry);
if (SetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, bootOrderLength, bootOrderAttributes))
{
std::cout << "Swapped WBM boot entry at offset " << wbmOffset << " with non-WBM boot entry at offset " << nonWBMOffset << std::endl;
}
else
{
std::cout << "Failed to swap WBM boot entry with non-WBM boot entry, error " << GetLastError() << std::endl;
return 1;
}