콘텐츠로 이동

C#으로 Windows의 VT / Hyper-V 활성화 여부 조회하기

처음에는 기존 네이티브 DLL에 COM(Component Object Model) 라이브러리를 추가해 VirtualizationFirmwareEnabled와 HypervisorPresent를 가져오려 했다. 아래 이유로 C#에서 처리하기로 했다.

  • COM 초기화: 같은 스레드에서 여러 번 초기화되거나 STA/MTA 모델이 뒤섞일 수 있다.
  • COM 보안: 보안 설정이 중복으로 초기화될 수 있다.
  • 스레드 관리: Unity의 기본 스레드 모델 때문에 문제가 생길 수 있다.
  • P/Invoke와 C++/CLI: DLL을 올바르게 호출하고 데이터를 마샬링하기가 복잡하다.

VT 활성화 조회:

wmic cpu get VirtualizationFirmwareEnabled

Hyper-V 활성화 조회:

wmic computersystem get HypervisorPresent

C# 코드 전문:

// C# 코드 전문
private bool OpenCMDtoGetVirtualizationFirmwareEnabled()
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c wmic cpu get VirtualizationFirmwareEnabled",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
Verb = "runas" // 관리자 권한으로 실행
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
Debug.LogWarning($"VirtualizationFirmwareEnabled: {output}"); // 출력 결과 확인
// "VirtualizationFirmwareEnabled"이라는 단어가 포함된 첫 번째 행을 제거
var lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length > 1)
{
return lines[1].Equals("TRUE", StringComparison.OrdinalIgnoreCase);
}
return false;
}
private bool OpenCMDtoGetHypervisorPresent()
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c wmic computersystem get HypervisorPresent",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
Verb = "runas" // 관리자 권한으로 실행
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
Debug.LogWarning($"VirtualizationFirmwareEnabled: {output}"); // 출력 결과 확인
// "VirtualizationFirmwareEnabled"이라는 단어가 포함된 첫 번째 행을 제거
var lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length > 1)
{
return lines[1].Equals("TRUE", StringComparison.OrdinalIgnoreCase);
}
return false;
}
// Win OS 조회 및 분기
public string IsVirtualizationEnabled()
{
OperatingSystem os = Environment.OSVersion;
Version version = os.Version;
Debug.LogWarning($"Operating System: {os.Platform}");
Debug.LogWarning($"Version: {version.Major}.{version.Minor}");
Debug.LogWarning($"Service Pack: {os.ServicePack}");
// 운영체제 버전에 따라 분기 처리
if (os.Platform == PlatformID.Win32NT)
{
switch (version.Major)
{
case 6 when version.Minor == 1: // Windows 7
{
Debug.LogWarning("Operating System: Windows 7");
return IsVirtualizationEnabledByDll();
}
case 6 when version.Minor == 2: // Windows 8
{
Debug.LogWarning("Operating System: Windows 8");
if (OpenCMDtoGetVirtualizationFirmwareEnabled())
return "On";
return "Off";
}
case 6 when version.Minor == 3: // Windows 8.1
{
Debug.LogWarning("Operating System: Windows 8.1");
if (OpenCMDtoGetVirtualizationFirmwareEnabled())
return "On";
return "Off";
}
case 10 when version.Build >= 22000: // Windows 11
{
Debug.LogWarning("Operating System: Windows 11");
if (OpenCMDtoGetVirtualizationFirmwareEnabled())
return "On";
return "Off";
}
case 10: Debug.LogWarning("Operating System: Windows 10"); // Windows 10
{
if (OpenCMDtoGetVirtualizationFirmwareEnabled())
return "On";
return "Off";
}
default: // Unknown Windows
Debug.LogWarning("Operating System: Unknown Windows Version");
return "NA";
}
}
Debug.LogWarning("Operating System: Non-Windows or Unsupported Version"); // Not Windows
return "NA";
}

Windows 7은 버전이 낮아 wmi로 이 값을 조회할 수 없다. 그래서 __cpuid로 직접 조회한다(Intel/AMD).

또한 Windows 7에서는 Hyper-V를 쓸 수 없다. COM 초기화가 필요 없으므로 DLL로 C++ 코드를 호출한다.

bool VirtualizationChecker::IsCpuSupportVirtualization()
{
uint32_t signature_reg[3] = { 0 };
if (!cpuid_ex(0, signature_reg, 1))
{
return false;
}
uint32_t features[4] = { 0 };
cpuid_ex(1, features, 2, 4);
if (signature_reg[0] == 0x756e6547 && signature_reg[1] == 0x6c65746e && signature_reg[2] == 0x49656e69)
return features[2] & (1 << 5);
if (signature_reg[0] == 0x68747541 && signature_reg[1] == 0x69746e65 && signature_reg[2] == 0x444d4163)
return features[2] & (1 << 2);
return false;
}
bool VirtualizationChecker::cpuid_ex(unsigned int leaf, uint32_t* regs, size_t start, size_t end)
{
#if (!defined(__x86_64__) && ! defined(__i386__) && ! defined(_M_IX86) && ! defined(_M_X64))
return false;
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
int x[4];
__cpuid((int*)x, leaf);
#elif defined(__clang__) || defined(__GNUC__)
unsigned int x[4];
__cpuid(leaf, x[0], x[1], x[2], x[3]);
#else
static_assert(false, "cpuid_ex: compiler is not supported");
#endif
for (; start < end; start++)
{
*regs++ = static_cast<uint32_t>(x[start]);
}
return true;
}