[c#] 32 비트 응용 프로그램에서 64 비트 레지스트리 읽기

AnyCPU 용으로 컴파일 된 AC # 단위 테스트 프로젝트가 있습니다. 빌드 서버는 64 비트 머신이며 64 비트 SQL Express 인스턴스가 설치되어 있습니다.

테스트 프로젝트는 다음과 유사한 코드를 사용하여 .MDF 파일의 경로를 식별합니다.

    private string GetExpressPath()
    {
        RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" );
        string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" );
        RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + @"\Setup" );
        return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString();
    }

이 코드는 32 비트 워크 스테이션에서 잘 작동하며 최근에 NCover로 코드 검사 분석을 활성화 할 때까지 빌드 서버에서 정상적으로 작동했습니다. NCover는 32 비트 COM 구성 요소를 사용하기 때문에 테스트 실행기 (Gallio)는 32 비트 프로세스로 실행됩니다.

레지스트리를 확인하면 아래에 “Instance Names”키가 없습니다.

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

32 비트 모드에서 실행되는 응용 프로그램이 Wow6432Node 외부의 레지스트리에 액세스하는 방법이 있습니까?



답변

레지스트리 키를 만들거나 열 때 KEY_WOW64_64KEY 매개 변수를 사용해야합니다. 하지만 AFAIK는 레지스트리 클래스로는 불가능하지만 API를 직접 사용할 때만 가능합니다.

시작하는 데 도움 될 수 있습니다.


답변

.NET Framework 4.x를 사용하는 64 비트 Windows에서 레지스트리 액세스에 대한 기본 지원이 여전히 있습니다 . 다음 코드는 Windows 7, 64 비트   및   Windows 10, 64 비트에서 테스트되었습니다   .

대신 사용하는 "Wow6432Node"것이 사실상 거기에 표시하고 다른에 매핑 한 레지스트리 트리로 노드를 에뮬레이트하는, 당신은 follwing을 할 수 있습니다 :

64 비트 또는 32 비트 레지스트리에 액세스해야하는지 여부를 결정하고 아래 설명 된대로 사용하십시오. 나중에 언급 한 코드 (추가 정보 섹션)를 사용할 수도 있습니다.이 코드는 하나의 쿼리로 두 노드에서 레지스트리 키를 가져 오는 통합 쿼리를 생성하므로 실제 경로를 사용하여 쿼리 할 수 ​​있습니다.

64 비트 레지스트리

64 비트 레지스트리에 액세스하려면 RegistryView.Registry64다음과 같이 사용할 수 있습니다 .

string value64 = string.Empty; 
RegistryKey localKey = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry64); 
localKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); 
if (localKey != null) 
{ 
    value64 = localKey.GetValue("RegisteredOrganization").ToString(); 
    localKey.Close();
} 
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64));

32 비트 레지스트리

32 비트 레지스트리 에 액세스하려면 RegistryView.Registry32다음과 같이 사용하십시오 .

string value32 = string.Empty; 
RegistryKey localKey32 = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry32); 
localKey32 = localKey32.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); 
if (localKey32 != null) 
{ 
    value32 = localKey32.GetValue("RegisteredOrganization").ToString(); 
    localKey32.Close();
} 
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32));

혼동하지 마십시오. 두 버전 모두 Microsoft.Win32.RegistryHive.LocalMachine첫 번째 매개 변수로 사용 됩니다. 두 번째 매개 변수 ( 대 )로 64 비트 또는 32 비트 를 사용할 것인지 구분합니다 .RegistryView.Registry64RegistryView.Registry32

참고

  • 64 비트 Windows HKEY_LOCAL_MACHINE\Software\Wow6432Node에서 64 비트 시스템에서 실행되는 32 비트 응용 프로그램에서 사용하는 값을 포함합니다. 진정한 64 비트 응용 프로그램 만이 값을 HKEY_LOCAL_MACHINE\Software직접 저장합니다 . 하위 트리는 Wow6432Node32 비트 응용 프로그램에 대해 완전히 투명하며 32 비트 응용 프로그램은 여전히 HKEY_LOCAL_MACHINE\Software예상대로 표시 됩니다 (일종의 리디렉션). 이전 버전의 Windows와 32 비트 Windows 7 (및 Vista 32 비트)에서는 하위 트리가 Wow6432Node분명히 존재 하지 않습니다 .

  • Windows 7 (64 비트)의 버그로 인해 32 비트 소스 코드 버전은 등록한 조직에 관계없이 항상 “Microsoft”를 반환하고 64 비트 소스 코드 버전은 올바른 조직을 반환합니다.

제공 한 예제로 돌아가서 다음과 같은 방법으로 64 비트 브랜치에 액세스합니다.

RegistryKey localKey = 
    RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 
        RegistryView.Registry64); 
RegistryKey sqlServerKey = localKey.OpenSubKey(
    @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL");
string sqlExpressKeyName = (string) sqlServerKey.GetValue("SQLEXPRESS");

추가 정보-실제 사용 :

Johny Skovdal 이 주석에서 제안한 흥미로운 접근 방식을 추가하고 싶습니다. 그의 접근 방식을 사용하여 몇 가지 유용한 기능을 개발하기 위해 선택했습니다. 어떤 상황에서는 32 비트 또는 64 비트. SQL 인스턴스 이름이 그러한 예입니다. 이 경우 다음과 같이 통합 쿼리를 사용할 수 있습니다 (C # 6 이상).

// using Microsoft.Win32;
public static IEnumerable<string> GetRegValueNames(RegistryView view, string regPath,
                                  RegistryHive hive = RegistryHive.LocalMachine) 
{ 
    return RegistryKey.OpenBaseKey(hive, view)
                     ?.OpenSubKey(regPath)?.G‌​etValueNames();
}

public static IEnumerable<string> GetAllRegValueNames(string RegPath,
                                  RegistryHive hive = RegistryHive.LocalMachine) 
{
    var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive);
    var reg32 = GetRegValueNames(RegistryView.Re‌​gistry32, RegPath, hive);
    var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
    return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}

public static object GetRegValue(RegistryView view, string regPath, string ValueName="",
                                 RegistryHive hive = RegistryHive.LocalMachine)
{
    return RegistryKey.OpenBaseKey(hive, view)
                       ?.OpenSubKey(regPath)?.G‌​etValue(ValueName);
}

public static object GetRegValue(string RegPath, string ValueName="",
                                 RegistryHive hive = RegistryHive.LocalMachine)
{   
    return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive) 
                     ?? GetRegValue(RegistryView.Re‌​gistry32, RegPath, ValueName, hive);
}

public static IEnumerable<string> GetRegKeyNames(RegistryView view, string regPath,
                   RegistryHive hive = RegistryHive.LocalMachine)
{
    return RegistryKey.OpenBaseKey(hive, view)
        ?.OpenSubKey(regPath)?.GetSubKeyNames(); 
}

public static IEnumerable<string> GetAllRegKeyNames(string RegPath,
                                  RegistryHive hive = RegistryHive.LocalMachine)
{
    var reg64 = GetRegKeyNames(RegistryView.Registry64, RegPath, hive);
    var reg32 = GetRegKeyNames(RegistryView.Re‌​gistry32, RegPath, hive);
    var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
    return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}

이제 위의 기능을 다음과 같이 간단히 사용할 수 있습니다.

예 1 : SQL 인스턴스 이름 가져 오기

var sqlRegPath=@"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
foreach (var valueName in GetAllRegValueNames(sqlRegPath))
{
    var value=GetRegValue(sqlRegPath, valueName);
    Console.WriteLine($"{valueName}={value}");
}

sqlRegPath의 값 이름 및 값 목록을 제공합니다.

참고 : 위의 해당 함수에서 매개 변수 를 생략하면 키 의 기본값 (명령 줄 도구에서 REGEDT32.EXE로 표시됨)에 액세스 할 수 있습니다 .(Default)ValueName

레지스트리 키 내의 하위 키 목록을 가져 오려면 GetRegKeyNames또는 함수를 사용하십시오 GetAllRegKeyNames. 이 목록을 사용하여 레지스트리의 추가 키를 탐색 할 수 있습니다.

예 2 : 설치된 소프트웨어의 제거 정보 가져 오기

var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);

32 비트 및 64 비트 제거 키를 모두 가져옵니다.

SQL 서버를 32 비트 또는 64 비트로 설치할 수 있으므로 함수에 널 처리가 필요합니다 (위의 예 1). 함수가 오버로드되어 필요한 경우 32 비트 또는 64 비트 매개 변수를 계속 전달할 수 있습니다. 그러나이를 생략하면 64 비트를 읽으려고 시도하고 실패하면 (널 값) 32 비트 값을 읽습니다.

여기에는 한 가지 전문성이 있습니다. GetAllRegValueNames일반적으로 루프 컨텍스트에서 사용 되기 때문에 (위의 예제 1 참조) 루프 null를 단순화 하는 대신 빈 열거 형을 반환합니다 foreach. 이러한 방식으로 처리되지 않으면 루프에 접두사를 붙여야합니다. if문 검사를 위해 null그 함수에 한 번 처리되도록 – 어떤 성가신 그렇게 할 필요가있을 것이다.

왜 null을 괴롭 히나요? 신경 쓰지 않으면 널 참조 예외가 코드에 던져진 이유를 알아내는 데 훨씬 더 많은 골칫거리가 될 것이기 때문입니다. 어디서, 왜 발생했는지 알아내는 데 많은 시간을 할애 할 것입니다. 그리고 프로덕션에서 발생했다면 로그 파일이나 이벤트 로그를 연구하는 데 매우 바쁠 것입니다 (로깅이 구현 되었기를 바랍니다). 방어적인 방식으로 가능한 경우 null 문제를 피하는 것이 좋습니다. 연산자 ?., ?[]??많은 도움이 될 수 있습니다 (위에 제공된 코드 참조). 새로운 논의 편안한 관련 기사가 C #에서 널 (NULL) 참조 형식 I 읽고도하는 것이 좋습니다, 이 하나의 엘비스 연산자에 대한이.


힌트 : Linqpad 무료 버전을 사용하여 Windows에서 모든 예제를 테스트 할 수 있습니다 . 설치가 필요하지 않습니다. 네임 스페이스 가져 오기 탭 을 누르고 F4입력하는 것을 잊지 마십시오 Microsoft.Win32. Visual Studio에서는 using Microsoft.Win32;코드 맨 위에 있어야 합니다.

팁 : 새에 익숙해 널 취급 사업자 , LinqPad에 다음 코드를 사용해 (디버그) :

예제 3 : 널 처리 연산자 시연

static string[] test { get { return null;} } // property used to return null
static void Main()
{
    test.Dump();                    // output: null
    // "elvis" operator:
    test?.Dump();                   // output: 
    // "elvis" operator for arrays
    test?[0].Dump();                // output: 
    (test?[0]).Dump();              // output: null
    // combined with null coalescing operator (brackets required):
    (test?[0]??"<null>").Dump();    // output: "<null>"
}

.Net 바이올린으로 사용해보십시오

당신이 관심이 있다면, 여기 당신이 도구로 할 수있는 그 밖의 무엇을 보여주는 함께 넣어 몇 가지 예입니다.


답변

댓글을 작성할 담당자가 충분하지 않지만 OpenRemoteBaseKey를 사용하여 원격 레지스트리를 열 때 작동한다는 점을 지적 할 가치가 있습니다. RegistryView.Registry64 매개 변수를 추가하면 머신 A의 32 비트 프로그램이 머신 B의 64 비트 레지스트리에 액세스 할 수 있습니다.이 매개 변수를 전달하기 전에 내 프로그램은 OpenRemoteBaseKey 이후 32 비트를 읽었고 키 I를 찾지 못했습니다. 이후였다.

참고 : 테스트에서 원격 시스템은 실제로 내 시스템 이었지만 다른 시스템에서와 마찬가지로 OpenRemoteBaseKey를 통해 액세스했습니다.


답변

이것을 시도하십시오 (32 비트 프로세스에서) :

> %WINDIR%\sysnative\reg.exe query ...

( 여기 에서 찾았 습니다 ).


답변

.NET 4와 함께 사용할 수없는 RegistryKey.OpenBaseKey(..., RegistryView.Registry64)경우 Windows API를 직접 사용해야합니다.

최소한의 상호 운용성은 다음과 같습니다.

internal enum RegistryFlags
{
    ...
    RegSz = 0x02,
    ...
    SubKeyWow6464Key = 0x00010000,
    ...
}

internal enum RegistryType
{
    RegNone = 0,
    ...
}

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int RegGetValue(
    UIntPtr hkey, string lpSubKey, string lpValue, RegistryFlags dwFlags, 
    out RegistryType pdwType, IntPtr pvData, ref uint pcbData);

다음과 같이 사용하십시오.

IntPtr data = IntPtr.Zero;
RegistryType type;
uint len = 0;
RegistryFlags flags = RegistryFlags.RegSz | RegistryFlags.SubKeyWow6464Key;
UIntPtr key = (UIntPtr)((uint)RegistryHive.LocalMachine);

const string subkey= @"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
const string value = "SQLEXPRESS";

if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
{
    data = Marshal.AllocHGlobal((int)len);
    if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
    {
        string sqlExpressKeyName = Marshal.PtrToStringUni(data);
    }
}


답변

내가 읽은 내용과 자체 테스트에서 레지스트리를 “SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall”경로에서 확인해야하는 것 같습니다. 다른 경로에서는 프로그램을 제거한 후에도 레지스터가 삭제되지 않기 때문입니다.

이런 식으로 32 비트 구성으로 64 개의 레지스터를 얻었습니다.

string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
    var list = key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName).GetValue("DisplayName")).ToList();

    key.Close();
}

32 개 레지스터의 경우 :

registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);


답변