它运行良好,除了在Windows安装结束时,BIOS已被修改为指向新的Windows启动管理器作为主启动设备.因此,如果不输入BIOS并更改设置,则无法再次成像.
我理解为什么引导顺序会因为wimboot / MDT进程涉及多次重启而改变.但我真的想要将PXE作为主要引导始终保持或者操作引导顺序,以便在完成后首先使用网络. (我的PXE服务器将传递网络引导机会以允许安装工作或在不需要映像时单独保留系统.)
更新 – 我看到两种可能性:
>弄清楚Windows安装程序如何告诉UEFI从目标安装盘启动,并在完成Windows安装以重新启动到PXE启动时执行相同的操作.
>在安装Windows之后使用Windows启动管理器和BCDEdit将PXE启动选项放在从本地磁盘启动之上(在superuser处发现的问题与此处的问题基本相同.最终结果讨论的并不是我真正想要的(在UEFI设置中首先是PXE)但可能会产生相同的行为(PXE启动总是有机会在Windows启动之前采取行动).
>在Linux上,这将是相当简单的,通过efibootmgr
> EasyUEFI会让我做我想做的事 – 命令行支持需要相当便宜的许可证;但我觉得很不错,取决于像它这样的利基工具,特别是如果有其他选择.
> bcdedit on a UEFI machine modifies UEFI settings.我认为它会起作用.
> The UEFI spec对于启动顺序并不太复杂. API实际上只是GetVariable / SetVariable,其变量名为BootOrder(按照它们将要尝试的顺序获取/设置引导选项列表)和Boot ####(获取/设置有关每个引导选项的信息).
>我不知道如何在Windows上针对UEFI API编写Windows应用程序(任何人?)
> Windows provides an API,除其他外,包装UEFI的GetVariable / SetVariable.
一旦我理解了启动顺序和Windows API的UEFI规范,代码(C,为我们正在使用的64位构建)并不是太糟糕.这需要构建到需要管理权限并静态链接Windows运行时的exe中,然后在重新启动之前安装操作系统后在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启动管理器”. Description是结构中偏移量为6的以null结尾的宽字符串.
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(),bootOptionInfoBuffer,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,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; }