Odczyt rejestru 64-bitowego z aplikacji 32-bitowej
Mam projekt C# unit test, który jest kompilowany dla AnyCPU. Nasz serwer kompilacji jest 64-bitową maszyną i ma zainstalowaną 64-bitową instancję SQL Express.
Projekt testowy wykorzystuje kod podobny do poniższego, aby zidentyfikować ścieżkę do .Pliki 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();
}
Ten kod działa dobrze na naszych 32-bitowych stacjach roboczych i działał dobrze na serwerze kompilacji, dopóki nie włączyłem ostatnio analizy pokrycia kodu za pomocą NCover. Ponieważ NCover używa 32-bitowego komponentu COM, Test runner (Gallio) działa jako Proces 32-bitowy.
Sprawdzanie rejestru, nie ma klucza "nazwy instancji" pod
HKEY_LOCAL_MACHINE\SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server
Czy istnieje sposób dla aplikacji działającej w trybie 32bit, aby uzyskać dostęp do rejestru poza Wow6432Node?
5 answers
Musisz użyć param KEY_WOW64_64KEY podczas tworzenia / otwierania klucza rejestru. Ale AFAIK to nie jest możliwe z klasą rejestru, ale tylko przy użyciu API bezpośrednio.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-10 07:13:18
Nadal istnieje natywne wsparcie dla dostępu do rejestru pod 64-bitowym systemem Windows przy użyciu . NET Framework 4.x . Poniższy kod jest testowany z Windows 7, 64 bit, a także z Windows 10, 64 bit. Aby uzyskać dostęp do rejestru 64-bitowego, możesz użyć:
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));
Jeśli chcesz uzyskać dostęp do rejestru 32bit, użyj:
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));
Nie myl się, obie wersje używają Microsoft.Win32.RegistryHive.LocalMachine
jako pierwszego parametru, rozróżniasz, czy używać 64-bitowy lub 32-bitowy przez drugi parametr (RegistryView.Registry64
versus RegistryView.Registry32
).
Uwaga, że
W 64-bitowym systemie Windows
HKEY_LOCAL_MACHINE\Software\Wow6432Node
zawiera wartości używane przez 32-bitowe aplikacje działające w systemie 64-bitowym. Tylko prawdziwe aplikacje 64-bitowe przechowują swoje wartości wHKEY_LOCAL_MACHINE\Software
bezpośrednio. SubtreeWow6432Node
jest całkowicie przezroczysty dla aplikacji 32-bitowych, aplikacje 32-bitowe nadal widząHKEY_LOCAL_MACHINE\Software
tak, jak tego oczekują (jest to rodzaj przekierowania). W starszych wersje systemu Windows, a także 32-bitowy Windows 7 (i Vista 32-bit) podtrójWow6432Node
oczywiście nie istnieje .Ze względu na błąd w systemie Windows 7 (64-bitowy), 32-bitowa wersja kodu źródłowego zawsze zwraca "Microsoft" niezależnie od zarejestrowanej organizacji, podczas gdy 64-bitowa wersja kodu źródłowego zwraca właściwą organizację.
Wracając do przykładu, który podałeś, zrób to w następujący sposób, aby uzyskać dostęp do 64 bitów oddział:
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");
Dodatkowe informacje do praktycznego zastosowania:
// 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);
}
Teraz możesz po prostu użyć powyższych funkcji w następujący sposób:
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}");
}
Który wyświetli listę nazw wartości i wartości w sqlRegPath.
Uwaga: możesz uzyskać dostęp do domyślnej wartości klucza (wyświetlanej przez narzędzie wiersza poleceń REGEDT32.EXE
jako (Default)
), jeśli pominiesz parametr ValueName
w odpowiednich funkcjach powyżej.
Aby uzyskać listę podkluczy wewnątrz klucza rejestru, użyj funkcji GetRegKeyNames
lub GetAllRegKeyNames
. Ty może użyć tej listy do przesuwania kolejnych kluczy w rejestrze.
Na przykład
var currentVersionRegPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $@"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);
Otrzyma wszystkie 32-bitowe i 64-bitowe klucze odinstalowujące.
Zwróć uwagę na obsługę null wymaganą w funkcjach, ponieważ SQL server może być zainstalowany jako 32-bitowy lub 64-bitowy. Funkcje są przeciążone, więc w razie potrzeby można przekazać parametr 32 - bitowy lub 64-bitowy-jednak jeśli go pominiemy, to spróbuje odczytać 64-bitowy, jeśli to się nie powiedzie (wartość null), odczytuje wartości 32-bitowe.
Jest tu jedna specjalność: ponieważ GetAllRegValueNames
zwykle jest używany w kontekście pętli (patrz przykład powyżej), zwraca pustą liczbę zamiast null
, aby uprościć pętle foreach
: gdyby nie była traktowana w ten sposób, pętla musiałaby być poprzedzona poleceniem if
sprawdzającym null
, co byłoby uciążliwe - więc jest to traktowane raz w funkcji.
Po co zawracać sobie głowę null? bo jeśli cię to nie obchodzi, będziesz miał dużo więcej bólów głowy dowiadując się, dlaczego ten wyjątek null reference został wrzucony do Twojego kodu - spędziłbyś dużo czasu, dowiadując się, gdzie i dlaczego to się stało. A jeśli to się stało w produkcji, będziesz bardzo zajęty studiowaniem plików dziennika lub dzienników zdarzeń (mam nadzieję, że masz logowanie zaimplementowane) ... lepiej unikać problemów null, gdzie można w sposób obronny. Operatorzy ?.
, ?[
...]
i ??
mogą ci bardzo pomóc(patrz kod podany powyżej). Jest ładny powiązany artykuł omawiający nowe nullable reference types in C#, which I recommend to read and also this one about the Elvis operator.
Podpowiedź: możesz skorzystać z darmowej edycji Linqpad aby przetestować wszystkie przykłady pod Windows. Nie wymaga instalacji. Nie zapomnij nacisnąć F4 i wpisać Microsoft.Win32
w zakładce import przestrzeni nazw. W Visual Studio potrzebujesz using Microsoft.Win32;
u góry kodu.
Wskazówka: aby zapoznać się siebie z nowym operatory obsługi null, wypróbuj (i debuguj) następujący kod w LinqPad:
string[] test { get { return null;} } // property used to return null
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>"
}
Jeśli jesteś zainteresowany, proszę. oto kilka przykładów, które zebrałem pokazując, co jeszcze można zrobić z narzędziem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-04 11:35:01
Nie mam dość rep do komentowania, ale warto zaznaczyć, że działa to przy otwieraniu zdalnego rejestru za pomocą OpenRemoteBaseKey. Dodawanie widoku rejestracji.Parametr Registry64 pozwala 32-bitowemu programowi na maszynie A na dostęp do 64-bitowego rejestru na maszynie B. zanim przekazałem ten parametr, mój program czytał 32-bitowy po OpenRemoteBaseKey i nie znalazł klucza, którego szukałem.
Uwaga: w moim teście zdalna maszyna była w rzeczywistości moją maszyną, ale uzyskałem do niej dostęp przez OpenRemoteBaseKey, tak jak ja bym dla innej maszyny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 17:31:59
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-10 12:04:06
Jeśli nie możesz używać. NET 4 z jego RegistryKey.OpenBaseKey(..., RegistryView.Registry64)
, musisz użyć Windows API bezpośrednio.
Minimalny interop to:
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);
Użyj go jak:
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 16:33:21