레지스트리의 32bit/64bit 리디렉션과 가상화의 함정 ── Wow6432Node와 「썼는데 값이 없다」 문제

· 업데이트: · · 레지스트리, WOW64, Wow6432Node, UAC, 64bit 마이그레이션, Windows, C#, 트러블슈팅

수정 이력(5건, 최종 수정 2026년 09월 03일)

이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.

Codex 리뷰에 따라 상담·문의 링크에 /ko/ 로케일 접두를 붙였습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
기사 앞부분에 「이 기사의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
외부 리뷰(1283건)에 대응하여 본문을 업데이트했습니다. 개별 변경 내용은 아래 이력을 참조하십시오.
bitness와 WOW64의 풀 스펠을 도입부에서 설명하고, 두 메커니즘의 전체 그림 다이어그램을 추가했습니다(리디렉션은 bitness에 관한 이야기로 읽기·쓰기 모두에 영향을 주고, 가상화는 권한에 관한 이야기로 쓰기에만 영향을 준다는 대비입니다). `reg flags` 출력과 세 플래그의 의미를 표로 정리하고, 작업 관리자의 「UAC 가상화」 열을 표시하는 조작 경로를 명시했습니다.
최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22174890)

아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.

Go Komura (2026). 「레지스트리의 32bit/64bit 리디렉션과 가상화의 함정 ── Wow6432Node와 「썼는데 값이 없다」 문제」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/windows-registry-redirection-virtualization/

DOI(등록된 아카이브)
10.5281/zenodo.22174890
DOI(마지막 등록 버전)
10.5281/zenodo.22174891

「설치 프로그램이 기록한 라이선스 키를 앱이 시작 시 읽지 못한다. regedit로 보면 값은 분명히 있다」──장치 연동 소프트웨어의 64bit 마이그레이션을 지원하던 때, 이런 보고를 받았습니다. 조사해 보니 값이 있던 곳은 HKLM\Software\Wow6432Node\회사명 아래였습니다. 새로 64bit로 다시 빌드한 앱은 HKLM\Software\회사명을 읽으러 갔고, 거기에는 아무것도 없었다는 결말입니다.

이런 「쓴 값이 없다」「regedit에서는 보이는데 앱에서는 안 보인다」는 두 메커니즘을 모르면, 눈으로 아무리 확인해도 수수께끼로 남습니다. 하나는 64bit Windows의 레지스트리가 프로세스의 bitness(해당 프로세스가 32bit로 동작 중인지 64bit로 동작 중인지를 구분하는 것)에 따라 다른 위치로 보인다는 점입니다. WOW64(Windows 32-bit on Windows 64-bit, 64bit Windows가 32bit 앱을 실행하기 위한 서브시스템)에 의한 레지스트리 리디렉션입니다. 다른 하나는 권한 부족 쓰기가 다른 위치로 조용히 전달된다는 점입니다. UAC의 레지스트리 가상화입니다. 게다가 둘 다 「오류 없이 성공」하기 때문에, 문제가 드러나는 것은 쓴 위치와 읽는 위치가 어긋난 순간, 즉 64bit 마이그레이션이나 설치 프로그램 변경 시점입니다.

이 글에서는 Windows 업무 앱 개발자를 대상으로, WOW64 레지스트리 리디렉션의 구조와 리디렉션 대상 키·공유 키 정리, UAC 레지스트리 가상화의 동작 조건, 설치 프로그램·COM 등록에서 일어나는 실무 피해, 그리고 C#/C++/reg.exe로 뷰를 명시하는 올바른 작성법까지를 공식 문서에 근거해 정리합니다.

1. 먼저 결론

  • 64bit Windows의 레지스트리에는 64bit 뷰와 32bit 뷰가 있으며, 32bit 프로세스의 HKLM\Software 접근은 레지스트리 리디렉터에 의해 물리 위치 HKLM\Software\Wow6432Node로 투명하게 유도됩니다. 앱에는 오류도 경고도 보이지 않습니다.1
  • Wow6432Node라는 물리 위치는 시스템 예약 영역입니다. 경로를 하드코딩해 직접 접근해서는 안 됩니다(Windows 10 on ARM에서는 32bit ARM용으로 WowAA32Node라는 다른 위치가 사용됩니다). 다른 뷰에는 공식 수단(후술하는 플래그나 RegistryView)으로 접근합니다.12
  • 리디렉션되는 것은 일부 키뿐이며, 공유되는 키도 있습니다. Windows 7 이후에는 HKLM\SOFTWARE는 「리디렉션」, HKLM\SOFTWARE\Classes는 「공유」, 다만 그 아래의 CLSIDInterface는 「리디렉션」이라는 중첩 구조입니다.3
  • 다른 뷰에 대한 접근은, Win32에서는 RegOpenKeyEx 등의 samDesiredKEY_WOW64_64KEY / KEY_WOW64_32KEY를 지정하고, .NET에서는 RegistryKey.OpenBaseKeyRegistryView.Registry64 / Registry32를 지정하며, 명령줄에서는 reg.exe/reg:64 / /reg:32입니다.245
  • 관리자 권한이 없는 32bit 대화형 프로세스가 HKLM\Software에 쓰면, UAC 레지스트리 가상화에 의해 HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software로 전달되는 경우가 있습니다. 쓰기는 「성공」하고, 본인이 다시 읽으면 병합 뷰로 보여 버리는 것이 함정입니다.6
  • 매니페스트에 requestedExecutionLevel을 지정하면 파일/레지스트리 가상화가 비활성화됩니다. 바꿔 말하면 매니페스트가 없는 레거시 EXE만 가상화 대상입니다.7
  • 레지스트리 가상화는 잠정적 호환 기술이며, Microsoft는 향후 Windows에서 제거할 의향을 명시하고 있습니다. 신규 앱에서 의존해서는 안 됩니다. 설계로는 「HKLM에 쓰지 않는 것」이 정답입니다.6
  • COM의 CLSID 등록(HKCR\CLSID = HKLM\Software\Classes\CLSID)은 bitness마다 나뉩니다. 32bit COM 서버의 등록은 64bit 클라이언트에서 보이지 않으며, 「클래스가 등록되어 있지 않습니다(0x80040154)」의 전형적인 원인이 됩니다.3

두 메커니즘의 전체 그림

이 글에서 다루는 두 메커니즘은 누가·무엇을·어디로 전달하는가가 서로 다릅니다. 리디렉션은 「bitness 이야기」로 읽기·쓰기 모두에 영향을 주고, 가상화는 「권한 이야기」로 쓰기에만 영향을 줍니다. 혼동하기 쉬우므로 세부 내용에 들어가기 전에 전체 그림을 한 장으로 파악하십시오.

32bit64bit매니페스트 없는 32bit 대화형 프로세스64bit 프로세스·서비스·매니페스트 있음프로세스가 HKLM의 Software 하위를 조작한다프로세스는 32bit인가WOW64 레지스트리 리디렉션bitness 이야기·읽기·쓰기 모두에 영향을 줌리디렉션 없음리디렉션 없는 HKLM Software를 읽고 쓴다실제로 읽고 쓰는 곳은Wow6432Node 하위 = 32bit 뷰쓰기인데, 해당 키에 대한쓰기 권한이 없다UAC 레지스트리 가상화권한 이야기·쓰기에만 영향을 줌사용자별 VirtualStore로 전달읽기는 원래 위치와의 병합 뷰액세스 거부로 그대로 실패한다

이 그림의 왼쪽 분기(누가 32bit인가)가 2장·3장, 오른쪽 아래 분기(권한과 매니페스트)가 5장의 내용입니다.

그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 19건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle

2. WOW64의 레지스트리 리디렉션 ── 32bit 프로세스는 어디를 보고 있는가

64bit Windows는 32bit 앱을 WOW64라는 서브시스템 위에서 실행합니다. 이때 레지스트리 리디렉터가 32bit 프로세스와 64bit 프로세스에 각각 서로 다른 논리 뷰를 보여 줍니다. 둘 다 같은 API로 같은 키 이름(HKEY_LOCAL_MACHINE\Software\...)을 지정하는데, 실제로 읽고 쓰는 물리 위치가 다르다는 것이 이 구조의 핵심입니다.1

리디렉션된 키의 물리 위치가 Wow6432Node입니다. 예를 들어 32bit 프로세스의 HKEY_LOCAL_MACHINE\Software는 물리적으로 HKEY_LOCAL_MACHINE\Software\Wow6432Node에 대응합니다. 이 대응은 앱 쪽에서는 완전히 투명하며, 32bit 앱은 「32bit Windows 위에서 동작하는 것과 같다」고 가정하고 레지스트리를 조작할 수 있습니다.1

누가 어디를 보는지를 표로 정리하면 다음과 같습니다.

접근 주체 코드에서 지정하는 경로 실제로 읽고 쓰는 물리 위치
64bit 프로세스 HKLM\Software\MyApp HKLM\Software\MyApp
32bit 프로세스 HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
32bit 프로세스 + KEY_WOW64_64KEY(RegistryView.Registry64) HKLM\Software\MyApp HKLM\Software\MyApp
64bit 프로세스 + KEY_WOW64_32KEY(RegistryView.Registry32) HKLM\Software\MyApp HKLM\Software\Wow6432Node\MyApp
32bit 대화형 프로세스·표준 권한·매니페스트 없는 쓰기 HKLM\Software\MyApp HKCU\Software\Classes\VirtualStore\Machine\Software\Wow6432Node\MyApp(WOW64 리디렉션 뒤에 가상화. 5장 참조)
regedit(64bit 프로세스) 64bit 뷰 기준으로 표시. Wow6432Node도 물리 키로 그대로 보임

도입부의 실패담이 바로 이 표입니다. 32bit 설치 프로그램이 쓴 값은 물리적으로 Wow6432Node 하위에 있고, 64bit화한 앱은 리디렉션 없는 HKLM\Software를 읽습니다. regedit는 양쪽이 보이므로 「regedit에는 있다」는 현장 보고와 「앱에서는 없다」는 현상이 모순 없이 동시에 성립합니다.

여기서 Wow6432Node 경로를 코드에 직접 써서 맞추고 싶어지지만, 이는 공식 문서가 명확히 금지하는 안티패턴입니다. 리디렉션 대상의 물리 위치는 시스템 예약이며 변경될 수 있다고 되어 있습니다. 실제로 Windows 10 on ARM에서 32bit ARM 앱의 리디렉션 대상은 WowAA32Node라는 다른 키입니다.12 다른 뷰를 읽으려면 4장의 공식 수단을 사용하십시오.

세부 사항으로, WOW64는 32bit 앱이 쓰는 %ProgramFiles%로 시작하는 REG_SZ/REG_EXPAND_SZ 문자열을 %ProgramFiles(x86)%로 바꾸는 보정도 합니다(대소문자까지 일치한 경우에만).1 또한 Vista/XP 시대에는 32bit/64bit 뷰 간에 키를 복사해 동기화하는 「레지스트리 리플렉션」이 있었지만, Windows 7 / Windows Server 2008 R2에서 폐지되었습니다. 오래된 해설을 읽을 때는 이 전제 차이에 주의하십시오.1

3. 리디렉션되는 키·공유되는 키

모든 키가 리디렉션되는 것은 아닙니다. 일부 키는 양쪽 뷰에서 하나의 물리 복사본을 공유합니다. 공식 문서 「Registry Keys Affected by WOW64」 목록에서 업무 앱 개발과 관련 깊은 항목을 발췌합니다(Windows 7 / Server 2008 R2 이후 열. 서브키는 원칙적으로 부모의 동작을 상속합니다).3

Windows 7 이후의 취급
HKLM\SOFTWARE 리디렉션
HKLM\SOFTWARE\Classes 공유
HKLM\SOFTWARE\Classes\CLSID 리디렉션
HKLM\SOFTWARE\Classes\Interface 리디렉션
HKLM\SOFTWARE\Classes\DirectShow / Media Type / MediaFoundation 리디렉션
HKLM\SOFTWARE\Clients 공유
HKLM\SOFTWARE\Microsoft\COM3 / EventSystem / OLE / RPC 공유
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths 공유
HKLM\SOFTWARE\Policies 공유
HKCU\SOFTWARE 공유
HKCU\SOFTWARE\Classes 공유
HKCU\SOFTWARE\Classes\CLSID / Interface 리디렉션

주목할 점은 중첩 구조입니다. HKLM\SOFTWARE는 리디렉션되는데 그 아래 Classes는 공유로 돌아가고, 다시 그 아래 CLSIDInterface는 다시 리디렉션됩니다. 「Software 하위는 전부 Wow6432Node로 간다」는 거친 이해로는 파일 확장자 연결(Classes 바로 아래, 공유)과 COM 클래스 등록(Classes\CLSID, 리디렉션)의 동작 차이를 설명할 수 없습니다. HKCU는 기본적으로 공유이므로, 사용자 단위 설정을 HKCU에 두는 한 bitness 문제는 거의 일어나지 않는다는 점도 실무상 중요한 결과입니다.3

또한 KEY_WOW64_64KEY 같은 플래그는 공유 키에는 효과가 없습니다. 공유 키는 원래 하나뿐이므로 뷰를 전환할 의미가 없기 때문입니다.2

4. 다른 뷰를 명시적으로 읽기 ── reg.exe·C#·C++의 올바른 작성법

「어느 뷰에 값이 있는지」를 확인하는 가장 빠른 방법은 reg.exe의 /reg:64 / /reg:32 옵션입니다. 각각 64bit 뷰·32bit 뷰를 명시해 접근합니다.5

:: 64bit 뷰(리디렉션 없는 HKLM\Software)를 읽기
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:64

:: 32bit 뷰(물리적으로는 Wow6432Node 하위)를 읽기
reg query "HKLM\SOFTWARE\KomuraSoft\DeviceLink" /v LicenseKey /reg:32

이 두 줄을 실행해 한쪽에만 값이 있으면 「쓴 쪽과 읽는 쪽의 bitness가 어긋나 있다」는 것이 확정됩니다. 경로에 Wow6432Node를 손으로 쓰지 않고, 같은 논리 경로에 대해 뷰만 전환하는 것이 핵심입니다.

C#(.NET)에서는 RegistryKey.OpenBaseKeyRegistryView를 전달합니다. Registry64(값 256), Registry32(값 512), Default(값 0)의 세 가지가 있으며, OpenBaseKey / OpenRemoteBaseKey / FromHandle에서 지정할 수 있습니다.4

using Microsoft.Win32;

// 32bit 프로세스에서도 64bit 뷰를 읽기
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
    var license = key?.GetValue("LicenseKey") as string;
}

// 64bit 프로세스에서 32bit 뷰(Wow6432Node 쪽)를 읽기
// ── 32bit 시절 설치 프로그램이 쓴 값의 마이그레이션 읽기 등에 사용
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
using (var key = baseKey.OpenSubKey(@"SOFTWARE\KomuraSoft\DeviceLink"))
{
    var legacy = key?.GetValue("LicenseKey") as string;
}

RegistryView.Default는 프로세스 bitness에 맡깁니다. AnyCPU 빌드의 .NET 앱은 실행 환경에 따라 32bit/64bit가 바뀌므로, HKLM 하위의 기기 공통 데이터를 다룬다면 어느 뷰를 읽을지를 코드에서 명시해 두면 빌드 설정 변경(Prefer 32-bit 전환이나 64bit 마이그레이션)으로 레지스트리 보이는 방식이 갑자기 바뀌는 사고를 막을 수 있습니다. 32bit OS에서 Registry64를 요청한 경우에는 32bit 뷰의 키가 반환되는 사양이므로, 32bit OS 지원이 남아 있어도 같은 코드로 안전하게 동작합니다.4

C++(Win32 API)에서는 RegOpenKeyEx / RegCreateKeyEx / RegDeleteKeyExsamDesiredKEY_WOW64_64KEY(0x0100) 또는 KEY_WOW64_32KEY(0x0200)를 OR로 지정합니다. 둘을 동시에 지정하면 ERROR_INVALID_PARAMETER로 실패합니다.2

HKEY hKey = nullptr;
// 32bit 프로세스에서 64bit 뷰의 키를 열기
LSTATUS st = RegOpenKeyExW(
    HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\KomuraSoft\\DeviceLink",
    0,
    KEY_READ | KEY_WOW64_64KEY,   // 여기서 뷰를 명시
    &hKey);
if (st == ERROR_SUCCESS)
{
    wchar_t buf[256]; DWORD cb = sizeof(buf); DWORD type = 0;
    RegQueryValueExW(hKey, L"LicenseKey", nullptr, &type,
                     reinterpret_cast<LPBYTE>(buf), &cb);
    RegCloseKey(hKey);
}

공식 문서가 드는 주의점은 두 가지입니다. 일단 플래그를 붙여 다른 뷰를 열었으면, 그 하위 자식 키 조작(생성·삭제·열기)에도 같은 플래그를 계속 명시할 것. 섞으면 예상하지 못한 동작이 됩니다. 또한 양쪽 뷰의 키를 빠짐없이 열거하려면 KEY_WOW64_64KEY로 연 핸들과 KEY_WOW64_32KEY로 연 핸들의 2패스로 열거해야 합니다. RegDeleteKey(Ex 없음)는 다른 뷰에 접근하지 못한다는 점도 주의해야 합니다.2

5. UAC 레지스트리 가상화 ── HKLM에 쓴 값이 VirtualStore에 있다

리디렉션과 혼동하기 쉬운 또 하나의 메커니즘이 UAC의 레지스트리 가상화입니다. 이는 bitness 이야기가 아니라 권한 이야기이며, Vista 이후 관리자 권한을 전제로 작성된 레거시 앱을 구제하기 위한 호환 기술입니다.6

동작은 이렇습니다. 쓰기 권한이 없는 프로세스가 HKLM\Software 하위에 값 쓰기나 서브키 생성을 시도하면, 액세스 거부로 실패시키는 대신 쓰기가 사용자별 가상 스토어 HKEY_USERS\<사용자SID>_Classes\VirtualStore\Machine\Software로 전달됩니다(regedit에서는 HKCU\Software\Classes\VirtualStore\Machine\Software 하위로 보입니다). 나아가 읽을 때는 가상 스토어 값과 원래 글로벌 스토어 값의 병합 뷰가 반환되며, 같은 이름의 값은 가상 스토어 쪽이 우선됩니다.6 64bit OS의 32bit 프로세스에서는 3장의 WOW64 리디렉션이 먼저 적용되므로, HKLM\Software\MyApp에 대한 쓰기의 실제 전달 대상은 VirtualStore\Machine\Software\Wow6432Node\MyApp이 됩니다. VirtualStore 하위를 조사할 때는 리디렉션 없는 Software 쪽뿐 아니라 Wow6432Node 쪽도 반드시 확인하십시오.

즉 쓴 본인의 프로세스에서는 아무 일도 없었던 것처럼 읽고 쓸 수 있습니다. 이것이 「개발 기기(관리자로 실행)에서는 문제없고, 고객 표준 사용자 환경에서만 설정이 이상하다」「A씨의 로그온에서는 동작하는데 B씨에서는 초기값으로 돌아간다」는, 사용자에 따라 달라지는 이상한 장애의 정체입니다. 가상 스토어는 사용자 프로필(NTUSER.DAT 등)의 일부이므로 사용자마다 내용이 달라집니다(프로필 구조는 「Windows 사용자 프로필 입문 - AppData와 NTUSER.DAT」를 참조하십시오).

가상화가 동작하는 조건은 한정적이며, 공식 문서에 명시되어 있습니다.6

  • 대상이 되는 것은 32bit 대화형 프로세스에 의한, HKLM\Software 하위의, 관리자라면 쓸 수 있는 키에 대한 조작뿐입니다
  • 다음은 대상 외입니다. 64bit 프로세스, 서비스 등의 비대화형 프로세스, 사용자를 가장(impersonation) 중인 조작, 드라이버, 그리고 매니페스트에 requestedExecutionLevel을 지정한 프로세스
  • HKLM\Software\Classes, HKLM\Software\Microsoft\Windows, HKLM\Software\Microsoft\Windows NT 하위도 대상 외입니다

실무에서 특히 중요한 것은 「매니페스트 유무에 따라 동작이 바뀐다」는 점입니다. Visual Studio의 C++ 링커는 기본으로 asInvoker의 UAC 프래그먼트를 매니페스트에 넣으므로7, 요즘 툴체인으로 빌드한 EXE는 처음부터 가상화 대상 외입니다. 가상화를 만나는 것은 매니페스트가 없는 VB6/오래된 Delphi/오래된 VC++로 만든 레거시 EXE를 64bit Windows에서 돌리는 현장입니다. 반대로 레거시 EXE에 「일단 매니페스트를 추가」「다시 빌드해 64bit화」한 순간 가상화가 꺼지고, 이번에는 정말로 액세스 거부로 실패하는(또는 조용히 쓰기에 실패하는) 마이그레이션 함정도 있습니다. requestedExecutionLevel의 세 값(asInvoker / highestAvailable / requireAdministrator)과 권한 설계의 사고방식은 「Windows에서 관리자 권한이 필요해지는 때는 언제인가」에서 자세히 다룹니다.

강조하고 싶은 점은, 공식 문서가 가상화는 잠정적(interim) 호환 기술이며 향후 Windows에서 제거할 의향이라고 명시한 것입니다. 신규 개발에서 이 동작에 의존하는 것은 논외이며, 설계 원칙은 「앱은 민감한 시스템 영역(HKLM)에 쓰지 않는다. 데이터는 사용자 단위 위치나 적절한 ACL을 붙인 공통 위치에 둔다」입니다.6 어디에 무엇을 저장해야 하는지는 「Windows 앱의 데이터 저장 위치 고르는 법」에 정리되어 있습니다.

키 단위로 가상화를 제어하는 플래그(REG_KEY_DONT_VIRTUALIZE / REG_KEY_DONT_SILENT_FAIL / REG_KEY_RECURSE_FLAG)도 있으며, reg.exe의 flags 옵션으로 조회·설정할 수 있습니다. 공식 문서에 실린 조회 예와 그 출력은 다음과 같습니다.6

C:\>reg flags HKLM\Software\AppKey1 QUERY
HKEY_LOCAL_MACHINE\Software\AppKey1
        REG_KEY_DONT_VIRTUALIZE: CLEAR
        REG_KEY_DONT_SILENT_FAIL: CLEAR
        REG_KEY_RECURSE_FLAG: CLEAR
The operation completed successfully.

세 가지 모두 CLEAR는 「플래그가 설정되어 있지 않다」= 이 키에서는 가상화가 기본대로 동작한다는 뜻입니다. 각 플래그를 설정한(SET) 때의 효과는 다음과 같습니다.6

플래그 설정했을 때의 효과
REG_KEY_DONT_VIRTUALIZE 쓰기의 가상화를 비활성화. 권한 부족의 키 생성·값 설정은 VirtualStore로 전달되지 않고 그대로 실패합니다
REG_KEY_DONT_SILENT_FAIL 열기의 가상화를 비활성화. 권한 부족 열기를 MAXIMUM_ALLOWED로 다시 여는 구제를 그만두고 실패시킵니다
REG_KEY_RECURSE_FLAG 가상화 플래그를 부모 키에서 자식으로 전파합니다. 적용되는 것은 변경 후에 만들어지는 자식 키뿐이며, 기존 자식 키에는 적용되지 않습니다

즉 「이 키만 가상화를 멈추고 권한 부족을 드러내고 싶다」면 REG_KEY_DONT_VIRTUALIZE를 설정한다는 쓰임이 됩니다. 플래그를 설정하는 쪽의 형식은 QUERY 대신 SET을 쓰지만, 정확한 옵션 순서는 reg flags /?로 확인하십시오.

장애 조사에서는 작업 관리자의 「세부 정보」 탭에서 열 제목을 마우스 오른쪽 단추로 클릭하고 「열 선택」에서 「UAC 가상화」를 추가해 프로세스 단위 가상화 상태를 보는 것과, HKCU\Software\Classes\VirtualStore 하위를 살펴 전달된 잔해가 없는지 보는 것, 두 가지가 빠른 확인 수단입니다.

6. 설치 프로그램과 COM 등록에서 일어나는 실무 피해

이 두 메커니즘이 실무 피해로 터지기 쉬운 곳이 설치 프로그램과 COM 등록입니다.

설치 프로그램의 bitness 문제. 32bit 설치 프로그램(32bit MSI나 32bit 설치 EXE)이 HKLM\Software\회사명에 쓴 설정은 물리적으로 Wow6432Node 하위로 들어갑니다. 앱 본체를 64bit화했는데 설치 프로그램을 32bit 그대로 재사용하면, 도입부 실패담처럼 「설치 프로그램은 썼고, 앱은 읽지 못한다」가 완성됩니다. 반대 패턴(64bit 설치 프로그램+32bit 앱)도 마찬가지입니다. 설치 프로그램과 앱 본체에서 「어느 뷰에 쓰고, 어느 뷰를 읽을지」를 맞추는 것, 64bit 마이그레이션 과도기에는 4장의 RegistryView.Registry32로 이전 위치에서 마이그레이션 읽기를 구현하는 것이 대책이 됩니다.

COM 등록의 bitness 문제. 3장의 표대로 HKLM\Software\Classes\CLSID(= HKCR\CLSID의 HKLM 쪽)는 리디렉션 대상입니다. 즉 32bit COM 서버의 CLSID 등록은 32bit 뷰에, 64bit 등록은 64bit 뷰에 들어가며 서로 보이지 않습니다.3 64bit 프로세스에서 32bit DLL의 인프로세스 COM 서버는 원래 로드할 수 없으므로 이 분리 자체는 합리적이지만, 현장에서는 「regsvr32로 등록했는데 클라이언트에서는 0x80040154(클래스가 등록되어 있지 않습니다)」라는 형태로 닥칩니다. regsvr32 자체에도 32bit 버전(SysWOW64 쪽)과 64bit 버전(System32 쪽)이 있으며, 어느 쪽으로 등록했는지에 따라 쓰이는 뷰가 결정됩니다. COM 등록과 bitness 조합에서 일어나는 함정의 전체 그림은 「COM/OCX/ActiveX 개발에서 빠지는 등록과 bitness의 함정」에, 레지스트리 등록 자체를 불필요하게 만드는 선택지는 「Reg-Free COM이란」에 정리되어 있습니다.

COM 세계에는 또 하나의 역사적 사정이 있습니다. Vista/XP 시대에는 CLSID 등이 「리디렉션+리플렉션(양쪽 뷰 간 동기화)」으로 다루어졌지만, Windows 7에서 리플렉션이 폐지되어 현재는 순수하게 뷰마다 분리됩니다.3 또한 HKLM\SOFTWARE\Wow6432Node\ClassesHKLM\SOFTWARE\Classes\Wow6432Node와 같은 호환용 심볼릭 링크가 몇 개 정의되어 있지만, 이는 Wow6432Node를 하드코딩해 버린 기존 앱 구제용이며 신규 앱이 써도 되는 것이 아닙니다.3

레지스트리 분리를 넘어도 DLL 자체의 로드에는 별도의 이름 해석 규칙이 기다립니다. 「찾을 수 없다」류의 조사에서는 「Windows DLL 이름 해석의 구조 - 검색 순서와 SxS」도 함께 보십시오.

7. 트러블슈팅 ── Procmon으로 「실제로 어디를 읽었는지」 보기

구조를 알아도, 눈앞의 장애에서 「이 프로세스는 결국 어느 물리 키를 읽었는가」를 확정하려면 관측이 필요합니다. 여기서 가장 강력한 것이 Sysinternals의 Process Monitor(Procmon)입니다.

절차는 단순합니다.

  1. Procmon을 시작하고 필터에 Process Name is <대상앱>.exe를 추가
  2. 도구 모음에서 레지스트리 조작만 표시하도록 좁힘(Operation begins with Reg 필터도 가능)
  3. 앱의 문제 조작을 재현하고 RegOpenKey / RegQueryValue / RegSetValue 행을 확인

핵심은 Procmon의 Path 열에는 리디렉션 해결 후의 물리 경로가 표시된다는 점입니다. 32bit 앱이 HKLM\Software\MyApp을 연 셈이어도 Procmon에는 HKLM\SOFTWARE\WOW6432Node\MyApp으로 나옵니다. 여기에 NAME NOT FOUND가 늘어서 있으면 「어느 뷰의, 어느 키가 없는지」가 한눈에 보이고, 쓰기가 HKCU\Software\Classes\VirtualStore\...로 흐르고 있으면 가상화 동작도 관측할 수 있습니다. COM의 0x80040154 조사라면 CLSID\{...} 열기 실패가 어느 뷰에서 일어났는가까지 추적할 수 있습니다. Procmon 필터 설계와 읽는 법의 세부 사항은 「Process Monitor(ProcMon) 실전 가이드」를 참조하십시오.

8. 실무의 정석(판단표)

상황 할 일 이유·보충
「regedit에서는 보이는데 앱에서는 안 보인다」 reg query ... /reg:64/reg:32로 양쪽 뷰를 비교해 읽기 어느 뷰에 값이 있는지를 먼저 확정한다5
32bit/64bit 양쪽 자사 프로세스가 같은 HKLM 설정을 읽음 쓰는 쪽에서 뷰를 고정하고(예: 64bit 뷰), 읽는 쪽 전원이 같은 뷰를 명시 RegistryView.Registry64 / KEY_WOW64_64KEY로 통일42
코드에 Wow6432Node를 직접 쓰고 싶어짐 쓰지 않는다. 뷰 지정 API로 교체 물리 위치는 시스템 예약. ARM에서는 WowAA32Node가 된다12
사용자 단위 설정의 위치 HKCU(또는 AppData)에 둔다 HKCU는 공유 키라 bitness 문제가 없고 권한 문제도 없다3
레거시 32bit 앱 설정이 「사용자마다 다르다」 HKCU\Software\Classes\VirtualStore를 확인 가상화로 전달된 값이 사용자마다 쌓이는 전형 패턴6
레거시 EXE에 매니페스트 추가/64bit화 HKLM 쓰기 지점을 모두 찾은 뒤에 실시 가상화가 꺼져, 지금까지 「동작하던」 쓰기가 실패하기 시작한다67
COM의 0x80040154 클라이언트와 서버의 bitness를 확인하고, 대응하는 regsvr32/뷰로 등록을 확인 CLSID 등록은 뷰마다 분리되어 있다3
어디를 읽었는지 확정할 수 없다 Procmon으로 물리 경로와 결과(NAME NOT FOUND 등)를 관측 추측을 멈추고 사실을 보는 것이 가장 빠르다

9. 정리

  • 64bit Windows의 레지스트리는 뷰가 두 개이며, 32bit 프로세스의 HKLM\Software는 Wow6432Node로 투명하게 리디렉션됩니다. 「쓴 값이 없다」의 첫 번째 용의자입니다.
  • 리디렉션은 모든 키가 아닙니다. Classes는 공유, 그 아래 CLSID / Interface는 리디렉션, HKCU는 거의 공유라는 중첩 구조를 파악하십시오.
  • Wow6432Node를 직접 쓰는 것은 금지입니다. reg.exe의 /reg:64 /reg:32, .NET의 RegistryView, Win32의 KEY_WOW64_64KEY / KEY_WOW64_32KEY로 뷰를 명시합니다.
  • UAC 레지스트리 가상화는 매니페스트 없는 32bit 대화형 프로세스의 권한 부족 HKLM 쓰기를 VirtualStore로 조용히 전달합니다. 레거시 구제용 잠정 기술이며 신규 앱에서 의존해서는 안 됩니다.
  • 설치 프로그램과 앱, COM 서버와 클라이언트는 「어느 뷰를 쓸지」를 맞춥니다. 64bit 마이그레이션 시에는 이전 뷰에서의 마이그레이션 읽기를 설계에 넣습니다.
  • 헷갈리면 Procmon으로 물리 경로를 관측합니다. 추측보다 관측이 빠르고 확실합니다.

관련 기사

관련 상담 영역

合同会社小村ソフト에서는 「레지스트리에 쓴 값을 읽지 못한다」「COM 등록을 찾지 못한다」와 같은 장애 조사, 32bit 앱·COM 컴포넌트 자산의 64bit 마이그레이션 설계, 장치 연동 소프트웨어를 포함한 Windows 업무 앱 수주 개발을 다룹니다.

참고 링크

  1. Microsoft Learn, Registry Redirector. 레지스트리 리디렉터가 32bit/64bit 앱에 서로 다른 논리 뷰를 제공하며 앱에 투명하다는 점, HKEY_LOCAL_MACHINE\Software가 HKEY_LOCAL_MACHINE\Software\Wow6432Node로 리디렉션된다는 점, 물리 위치는 시스템 예약이며 앱이 직접 접근해서는 안 된다는 점, Windows 10 on ARM의 32bit ARM 키는 WowAA32Node로 매핑된다는 점, %ProgramFiles% 문자열 치환, 리플렉션이 Windows 7 / Windows Server 2008 R2에서 폐지된 점에 대해.  2 3 4 5 6 7 8

  2. Microsoft Learn, Accessing an Alternate Registry View. KEY_WOW64_64KEY(0x0100)와 KEY_WOW64_32KEY(0x0200)의 의미, RegCreateKeyEx·RegDeleteKeyEx·RegOpenKeyEx의 samDesired로 지정한다는 점, 두 플래그 동시 지정은 ERROR_INVALID_PARAMETER가 된다는 점, 공유 키에는 효과가 없다는 점, 자식 키 조작에서 같은 플래그를 계속 써야 한다는 점, 전체 키 열거는 2패스로 한다는 점, Wow6432Node/WowAA32Node가 예약 키라는 점에 대해.  2 3 4 5 6 7 8

  3. Microsoft Learn, Registry Keys Affected by WOW64. 리디렉션되는 키와 공유되는 키 목록(Windows 7 이후 HKLM\SOFTWARE는 리디렉션, HKLM\SOFTWARE\Classes는 공유, Classes\CLSID·Interface·DirectShow 등은 리디렉션, Clients·COM3·OLE·RPC·App Paths·Policies·HKCU\SOFTWARE 등은 공유), 서브키가 부모 동작을 상속한다는 점, HKCR이 HKLM과 HKCU의 Classes 병합 뷰라는 점, Wow6432Node를 포함한 호환용 심볼릭 링크는 기존 앱 구제용이며 신규 앱은 쓰면 안 된다는 점에 대해.  2 3 4 5 6 7 8 9

  4. Microsoft Learn, RegistryView Enum (Microsoft.Win32). RegistryView 열거형의 Default(0)·Registry64(256)·Registry32(512), OpenBaseKey·OpenRemoteBaseKey·FromHandle로 뷰를 지정할 수 있다는 점, 32bit OS에서 64bit 뷰를 요청한 경우 32bit 뷰의 키가 반환된다는 점에 대해.  2 3 4

  5. Microsoft Learn, reg query. reg query 명령의 /reg:32가 32bit 레지스트리 뷰로, /reg:64가 64bit 레지스트리 뷰로 키에 접근하는 옵션이라는 점에 대해.  2 3

  6. Microsoft Learn, Registry Virtualization. HKLM\Software에 대한 쓰기가 HKEY_USERS<User SID>_Classes\VirtualStore\Machine\Software로 리디렉션된다는 점, 읽을 때 가상 스토어 우선의 병합 뷰가 반환된다는 점, 가상화 대상이 32bit 대화형 프로세스이면서 HKLM\Software 하위이면서 관리자가 쓸 수 있는 키에 한정된다는 점, 64bit 프로세스·서비스·가장 중인 조작·requestedExecutionLevel 지정 프로세스·Classes 등 서브키에서는 무효라는 점, 잠정적 호환 기술로 향후 삭제 의향이 있으며 앱이 의존해서는 안 된다는 점, reg flags에 의한 REG_KEY_DONT_VIRTUALIZE 등의 제어에 대해.  2 3 4 5 6 7 8 9 10

  7. Microsoft Learn, Application manifests. requestedExecutionLevel 요소의 asInvoker·requireAdministrator·highestAvailable의 의미, requestedExecutionLevel 노드를 지정하면 파일 및 레지스트리 가상화가 비활성화된다는 점, 하위 호환을 위해 가상화를 쓰고 싶으면 이 노드를 생략한다는 점, Visual C++ 링커가 기본으로 asInvoker의 UAC 프래그먼트를 매니페스트에 넣는다는 점에 대해.  2 3

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

regedit에서는 값이 보이는데, 앱에서 읽으면 존재하지 않는 이유는 무엇입니까?
앱과 regedit가 보고 있는 레지스트리 뷰가 다른 것이 전형적인 원인입니다. 64bit Windows의 regedit는 64bit 프로세스이며, 64bit 뷰를 기준으로 Wow6432Node(32bit 뷰의 물리 위치)까지 포함해 표시합니다. 반면 32bit 앱이 HKLM\Software를 열면 WOW64의 레지스트리 리디렉터에 의해 Wow6432Node 쪽으로 유도되므로, 64bit 뷰 쪽에만 있는 값은 「존재하지 않는」 상태가 됩니다. regedit에서 보이는 값의 경로에 Wow6432Node가 포함되어 있는지를 먼저 확인하고, reg query의 /reg:64와 /reg:32로 양쪽 뷰를 비교해 읽으면 원인을 빠르게 분리할 수 있습니다.
코드에서 Wow6432Node 경로를 직접 지정해 접근해도 됩니까?
피해야 합니다. Microsoft 공식 문서는 리디렉션 대상의 물리 위치가 시스템 예약 영역이며 향후 변경될 수 있으므로 애플리케이션이 직접 접근해서는 안 된다고 명시합니다. 실제로 Windows 10 on ARM에서는 32bit ARM 앱용으로 WowAA32Node라는 다른 물리 위치가 사용되며, Wow6432Node를 하드코딩하면 ARM 환경에서 깨집니다. 다른 뷰에 접근하려면 KEY_WOW64_64KEY/KEY_WOW64_32KEY 플래그나 .NET의 RegistryView와 같은 공식 수단을 사용하십시오.
HKLM에 쓴 값이 HKCU의 VirtualStore에 있는 이유는 무엇입니까?
UAC의 레지스트리 가상화가 동작한 결과입니다. 쓰기 권한이 없는 32bit 대화형 프로세스가 매니페스트에 requestedExecutionLevel을 두지 않은 채 HKLM\Software 하위에 쓰면, 실패하는 대신 사용자별 가상 스토어(HKEY_USERS\<SID>_Classes\VirtualStore\Machine\Software, regedit에서는 HKCU\Software\Classes\VirtualStore 하위로 보입니다)로 전달됩니다. 그 프로세스가 다시 읽으면 가상 스토어와 원래 위치의 병합 뷰가 보이므로 겉으로는 동작한 것처럼 보이지만, 64bit 프로세스나 서비스에서는 그 값이 보이지 않아 「사용자마다 설정이 다르다」는 이상한 장애가 됩니다. 가상화는 레거시 구제용 잠정 기술이므로 신규 앱에서 의존해서는 안 됩니다.
C#에서 레지스트리의 bitness(32bit/64bit 뷰)를 명시하려면 어떻게 합니까?
RegistryKey.OpenBaseKey에 RegistryView를 전달합니다. RegistryView.Registry64를 지정하면 32bit 프로세스에서도 64bit 뷰를, RegistryView.Registry32를 지정하면 64bit 프로세스에서도 32bit 뷰(Wow6432Node 쪽)를 읽고 쓸 수 있습니다. RegistryView.Default는 프로세스 bitness에 맡기므로, AnyCPU 빌드처럼 실행 환경에 따라 bitness가 바뀌는 구성에서는 어느 뷰를 읽을지를 명시하는 편이 안전합니다. 32bit OS에서 Registry64를 요청한 경우에는 32bit 뷰가 반환되는 사양이므로, 32bit OS 지원이 남아 있어도 같은 코드로 동작합니다.
레지스트리 가상화를 끄거나, 가상화되고 있는지 확인하려면 어떻게 합니까?
앱 쪽에서는 매니페스트에 requestedExecutionLevel(asInvoker여도 가능)을 지정하면 해당 프로세스의 파일/레지스트리 가상화가 비활성화됩니다. 관리자 쪽에서는 reg flags 명령으로 키 단위의 REG_KEY_DONT_VIRTUALIZE 플래그를 설정·확인할 수 있습니다. 이미 동작 중인 앱을 조사할 때는 작업 관리자의 「UAC 가상화」 열로 프로세스 단위 가상화 상태를 확인하고, HKCU\Software\Classes\VirtualStore 하위에 전달된 값이 쌓여 있지 않은지를 보는 것이 빠른 길입니다. 항구 대책으로는 애초에 HKLM에 쓰지 않는 설계(사용자 단위 설정은 HKCU나 AppData로)로 고치는 것을 권합니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기