MFC에서 ARM64 환경인지 제대로 확인하는 법
현재의 ARM64 윈도우 환경에서는 x86/x64 어플리케이션도 대부분 실행이 가능하다.
따라서, 대부분의 어플리케이션은 그냥 컴파일만 하면 사용할 수 있다.
그런데, 가끔씩은 자신이 어떤 환경에서 동작되는 어떤 프로그램인지 알아야 할 때가 있다.
ARM64 윈도우가 등장하기 전까지는 이 조건이 그렇게 복잡하지 않았다.
다음 중 하나인지만 확인하면 충분했다.
- x86 윈도우에서 실행되는 x86 어플리케이션 (x86 native)
- x64 윈도우에서 실행되는 x86 어플리케이션 (WOW64)
- x64 윈도우에서 실행되는 x64 어플리케이션 (x64 native)
이 중 3번은 확인하기가 어렵지도 않았다.
x64 어플리케이션이면 윈도우는 x64 환경일 수 밖에 없기 때문이었다.
하지만 ARM64 윈도우가 등장한 이후로는 많은 것이 달라졌다.
(물론 ARM 32bit 윈도우도 있지만, 이건 사용자가 워낙 없으니 무시…)
위의 조건에 추가해서 다음의 조건들을 더 확인할 수 있어야 한다.
- ARM64 윈도우에서 실행되는 ARM64 어플리케이션 (ARM64 native)
- ARM64 윈도우에서 실행되는 x86 어플리케이션
- ARM64 윈도우에서 실행되는 x64 어플리케이션
이 정도로 복잡한 결과를 얻으려면 함수를 제대로 하나 만드는 게 좋다.
이를 위해 결과로 받을 타입을 선언한다.
enum class RuntimeKind {
Arm64_Native,
X86_on_X64,
X86_Native,
X64_Native,
X86_on_Arm64,
X64_on_Arm64,
Unknown
};
이 기능은 kernel32.dll의 IsWow64Process2() 및 GetProcessInformation() 를 이용해 구현한다.
이를 위해선 다음과 같이 선언해야 한다.
#include <windows.h>
#include <processthreadsapi.h>
// Windows 11 SDK 미만에서도 빌드되도록 값만 정의
#ifndef ProcessMachineTypeInfo
#define ProcessMachineTypeInfo static_cast<PROCESS_INFORMATION_CLASS>(9)
#endif
// Windows 11 SDK에 있는 구조체를 최소 필드로 재선언
#ifndef _PROCESS_MACHINE_INFORMATION_DEFINED
#define _PROCESS_MACHINE_INFORMATION_DEFINED
typedef struct _PROCESS_MACHINE_INFORMATION {
USHORT ProcessMachine; // IMAGE_FILE_MACHINE_*
USHORT Res0;
ULONG MachineAttributes; // (여기선 미사용)
} PROCESS_MACHINE_INFORMATION, *PPROCESS_MACHINE_INFORMATION;
#endif
필요한 선언을 다 했으면, 마지막으로 해당 함수를 구현한다.
template<typename T>
static T GetK32Proc(LPCSTR name) {
HMODULE h = GetModuleHandleW(L"kernel32.dll");
return h ? reinterpret_cast<T>(GetProcAddress(h, name)) : nullptr;
}
RuntimeKind DetectRuntimeKind() {
using IsWow64Process2_t = BOOL (WINAPI*)(HANDLE, USHORT*, USHORT*);
using GetProcessInformation_t = BOOL (WINAPI*)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
auto pIsWow64Process2 = GetK32Proc<IsWow64Process2_t>("IsWow64Process2");
auto pGetProcessInformation = GetK32Proc<GetProcessInformation_t>("GetProcessInformation");
USHORT proc = IMAGE_FILE_MACHINE_UNKNOWN;
USHORT host = IMAGE_FILE_MACHINE_UNKNOWN;
if (pIsWow64Process2 && pIsWow64Process2(GetCurrentProcess(), &proc, &host)) {
// --- ARM64 호스트 ---
if (host == IMAGE_FILE_MACHINE_ARM64) {
if (proc == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_on_Arm64;
// WOW64 아님 → x64 네이티브 또는 ARM64 네이티브
if (proc == IMAGE_FILE_MACHINE_UNKNOWN) {
if (pGetProcessInformation) {
PROCESS_MACHINE_INFORMATION mi{};
if (pGetProcessInformation(GetCurrentProcess(), ProcessMachineTypeInfo, &mi, sizeof(mi))) {
if (mi.ProcessMachine == IMAGE_FILE_MACHINE_ARM64)
return RuntimeKind::Arm64_Native;
if (mi.ProcessMachine == IMAGE_FILE_MACHINE_AMD64)
return RuntimeKind::X64_on_Arm64;
}
}
// 보수적 추정 (구형 OS/SDK)
#if defined(_M_ARM64)
return RuntimeKind::Arm64_Native;
#elif defined(_M_X64)
return RuntimeKind::X64_on_Arm64;
#else
return RuntimeKind::Unknown;
#endif
}
}
// --- x64 호스트 ---
if (host == IMAGE_FILE_MACHINE_AMD64) {
if (proc == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_on_X64;
if (proc == IMAGE_FILE_MACHINE_UNKNOWN)
return RuntimeKind::X64_Native;
}
// --- 32비트 호스트 ---
if (host == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_Native;
} else {
// 레거시 대비: IsWow64Process2 불가 시
BOOL isWow64 = FALSE;
using IsWow64Process_t = BOOL (WINAPI*)(HANDLE, PBOOL);
if (auto pIsWow64Process = GetK32Proc<IsWow64Process_t>("IsWow64Process")) {
if (pIsWow64Process(GetCurrentProcess(), &isWow64) && isWow64)
return RuntimeKind::X86_on_X64;
}
SYSTEM_INFO si{};
GetNativeSystemInfo(&si);
switch (si.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64: return RuntimeKind::X64_Native;
case PROCESSOR_ARCHITECTURE_INTEL: return RuntimeKind::X86_Native;
case PROCESSOR_ARCHITECTURE_ARM64: return RuntimeKind::Unknown; // 구형 조합: 정보 부족
default: return RuntimeKind::Unknown;
}
}
return RuntimeKind::Unknown;
}
참고로, 이 포스팅은 구라제거기가 ARM64를 지원하는 과정에서 구현한 내용이다.
좀 더 생각해보니 ARM64 프로세스인 경우는 굳이 API를 호출할 필요가 없다.
컴파일 타임에서 확정이 가능하기 때문.
프로세스가 x86/x64인 경우만 API를 호출하면 된다.
이를 반영해서 살짝 수정하면 아래와 같다.
RuntimeKind DetectRuntimeKind() {
// --- 컴파일 타임에서 확정 가능한 경우 ---
#if defined(_M_ARM64)
return RuntimeKind::Arm64_Native; // ARM64 빌드 → 무조건 ARM64 네이티브
#else
using IsWow64Process2_t = BOOL(WINAPI*)(HANDLE, USHORT*, USHORT*);
using GetProcessInformation_t = BOOL(WINAPI*)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
auto pIsWow64Process2{ GetK32Proc<IsWow64Process2_t>("IsWow64Process2") };
auto pGetProcessInformation{ GetK32Proc<GetProcessInformation_t>("GetProcessInformation") };
USHORT proc{ IMAGE_FILE_MACHINE_UNKNOWN };
USHORT host{ IMAGE_FILE_MACHINE_UNKNOWN };
if (pIsWow64Process2 && pIsWow64Process2(GetCurrentProcess(), &proc, &host))
{
// --- ARM64 호스트 ---
if (host == IMAGE_FILE_MACHINE_ARM64)
{
if (proc == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_on_Arm64;
if (proc == IMAGE_FILE_MACHINE_AMD64)
return RuntimeKind::X64_on_Arm64;
if (proc == IMAGE_FILE_MACHINE_UNKNOWN)
{
// ProcessMachine이 UNKNOWN이면 ARM64 네이티브 또는 X64 on ARM64
if (pGetProcessInformation)
{
PROCESS_MACHINE_INFORMATION mi{};
if (pGetProcessInformation(GetCurrentProcess(), ProcessMachineTypeInfo, &mi, sizeof(mi)))
{
if (mi.ProcessMachine == IMAGE_FILE_MACHINE_ARM64)
return RuntimeKind::Arm64_Native;
if (mi.ProcessMachine == IMAGE_FILE_MACHINE_AMD64)
return RuntimeKind::X64_on_Arm64;
}
}
return RuntimeKind::Arm64_Native; // 보수적 추정
}
}
// --- x64 호스트 ---
if (host == IMAGE_FILE_MACHINE_AMD64)
{
if (proc == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_on_X64;
if (proc == IMAGE_FILE_MACHINE_UNKNOWN)
return RuntimeKind::X64_Native;
}
// --- x86 호스트 ---
if (host == IMAGE_FILE_MACHINE_I386)
return RuntimeKind::X86_Native;
}
else
{
// 레거시 호환
BOOL isWow64{ FALSE };
using IsWow64Process_t = BOOL(WINAPI*)(HANDLE, PBOOL);
if (auto pIsWow64Process = GetK32Proc<IsWow64Process_t>("IsWow64Process"))
{
if (pIsWow64Process(GetCurrentProcess(), &isWow64) && isWow64)
return RuntimeKind::X86_on_X64;
}
SYSTEM_INFO si{};
GetNativeSystemInfo(&si);
switch (si.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_AMD64:
return RuntimeKind::X64_Native;
case PROCESSOR_ARCHITECTURE_INTEL:
return RuntimeKind::X86_Native;
case PROCESSOR_ARCHITECTURE_ARM64:
return RuntimeKind::Arm64_Native; // 구형 환경 추정
default:
return RuntimeKind::Unknown;
}
}
return RuntimeKind::Unknown;
#endif