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.Registry64
RegistryView.Registry32
참고 그
-
64 비트 Windows
HKEY_LOCAL_MACHINE\Software\Wow6432Node
에서 64 비트 시스템에서 실행되는 32 비트 응용 프로그램에서 사용하는 값을 포함합니다. 진정한 64 비트 응용 프로그램 만이 값을HKEY_LOCAL_MACHINE\Software
직접 저장합니다 . 하위 트리는Wow6432Node
32 비트 응용 프로그램에 대해 완전히 투명하며 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)?.GetValueNames();
}
public static IEnumerable<string> GetAllRegValueNames(string RegPath,
RegistryHive hive = RegistryHive.LocalMachine)
{
var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive);
var reg32 = GetRegValueNames(RegistryView.Registry32, 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)?.GetValue(ValueName);
}
public static object GetRegValue(string RegPath, string ValueName="",
RegistryHive hive = RegistryHive.LocalMachine)
{
return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive)
?? GetRegValue(RegistryView.Registry32, 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.Registry32, 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>"
}
당신이 관심이 있다면, 여기 당신이 도구로 할 수있는 그 밖의 무엇을 보여주는 함께 넣어 몇 가지 예입니다.
답변
댓글을 작성할 담당자가 충분하지 않지만 OpenRemoteBaseKey를 사용하여 원격 레지스트리를 열 때 작동한다는 점을 지적 할 가치가 있습니다. RegistryView.Registry64 매개 변수를 추가하면 머신 A의 32 비트 프로그램이 머신 B의 64 비트 레지스트리에 액세스 할 수 있습니다.이 매개 변수를 전달하기 전에 내 프로그램은 OpenRemoteBaseKey 이후 32 비트를 읽었고 키 I를 찾지 못했습니다. 이후였다.
참고 : 테스트에서 원격 시스템은 실제로 내 시스템 이었지만 다른 시스템에서와 마찬가지로 OpenRemoteBaseKey를 통해 액세스했습니다.
답변
답변
.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);