32ビットアプリケーションから64ビットレジストリを読み取る


97

AnyCPU用にコンパイルされたc#ユニットテストプロジェクトがあります。ビルドサーバーは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ビットプロセスとして実行されます。

レジストリをチェックすると、下に「インスタンス名」キーがありません

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

32ビットモードで実行されているアプリケーションがWow6432Node外のレジストリにアクセスする方法はありますか?

回答:


21

レジストリキーを作成/開くときは、KEY_WOW64_64KEYパラメータを使用する必要があります。ただし、レジストリクラスでは不可能ですが、APIを直接使用する場合にのみ可能です。

これはあなたが始めるのに役立つかもしれません。


151

.NET Framework 4.xを使用した64ビットWindowsでのレジストリアクセスのネイティブサポートはまだあります。次のコードは、Windows 7、64ビット   、および   Windows 10、64ビットでテストされてい   ます

"Wow6432Node"1つのレジストリツリーを別のレジストリツリーにマッピングしてノードを仮想的にそこに表示することでノードをエミュレートするを使用する代わりに、次のように実行できます。

64ビットレジストリと32ビットレジストリのどちらにアクセスする必要があるかを決定し、以下の説明に従って使用します。後で説明するコード(追加情報セクション)を使用して、ユニオンクエリを作成し、1つのクエリで両方のノードからレジストリキーを取得することもできます。そのため、実際のパスを使用してクエリを実行できます。

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\Wow6432Node64ビットシステムで実行されている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)?.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

リストを取得するにはサブキーをレジストリキー内、関数を使用しますGetRegKeyNamesGetAllRegKeyNames。このリストを使用して、レジストリ内の他のキーをトラバースできます。

例2:インストールされているソフトウェアのアンインストール情報を取得する

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

32ビットと64ビットのすべてのアンインストールキーを取得します。

SQLサーバーは32ビットまたは64ビットとしてインストールできるため(上​​記の例1)、関数で必要なnull処理に注意してください。関数はオーバーロードされているため、必要に応じて32ビットまたは64ビットのパラメーターを渡すことができます。ただし、省略した場合、64ビットの読み取りが試行され、失敗した場合(null値)、32ビットの値が読み取られます。

ここには1つの特別な点があります。これGetAllRegValueNamesは通常ループコンテキストで使用されるため(上記の例1を参照)、ループnullを単純化foreachするのではなく、空の列挙型を返します。そのように処理されない場合、ループの前にこれifをチェックnullするのが面倒なステートメントをチェックするステートメント-関数内で一度処理されます。

なぜnullを気にするのですか?気にしないと、コード内でnull参照例外がスローされた理由を突き止めるのに多くの頭痛が発生するため、どこで、なぜ発生したかを突き止めるのに多くの時間を費やすことになります。また、本番環境で発生した場合は、ログファイルやイベントログの調査で忙しくなります(ロギングが実装されているといいのですが)...防御的な方法でできるnullの問題を回避することをお勧めします。演算子?.?[...は]??あなたを大いに助けることができます(上記のコードを参照してください)。C#の新しいnull可能参照型について説明しているすばらしい関連記事があります。これは、私が読むことをお勧めします。また、Elvisオペレーターに関するこの記事もお勧めします。


ヒント:Linqpadの無料版を使用して、Windowsですべての例をテストできます。インストールは必要ありません。名前空間インポートタブを押しF4て入力することを忘れないでくださいMicrosoft.Win32。Visual Studioではusing Microsoft.Win32;、コードの先頭にが必要です。

ヒント:新しいを理解するには、ヌル取り扱い事業者 LinqPadに次のコードを試してみる(およびデバッグ):

例3: null処理演算子のデモ

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フィドルでお試しください

あなたが興味を持っているなら、ここに私がまとめたいくつかの例があります。このツールで他に何ができるかを示しています。


2
その包括的な答えをありがとう。メモリから、質問を投稿したときは.NET 3.5を使用していたと思いますが、.NET 4によって状況が改善されたことを確認できます
David Gardiner

2
どういたしまして。最近64ビットレジストリで同様の問題がありましたが、既に解決済みなので、解決策を共有する価値があると思いました。
マット

2
これはまさに私が探していたものです。私はこれをWindows 9.1で実行しています。
Michiel Bugher、2015

1
@AZ_-編集ありがとうございます。そうです、キーを閉じる必要があります!
Matt

1
@JohnySkovdal-見出しを変更して、追加の(オプションの)情報を提供していることを明確にしました-問題をさらに掘り下げたい人のために。
Matt

6

コメントするのに十分な担当者がいませんが、OpenRemoteBaseKeyを使用してリモートレジストリを開くときに機能することを指摘する価値があります。RegistryView.Registry64パラメーターを追加すると、マシンAの32ビットプログラムがマシンBの64ビットレジストリにアクセスできるようになります。そのパラメーターを渡す前に、プログラムはOpenRemoteBaseKeyの後に32ビットを読み取っていて、キーIが見つかりませんでした。後だった。

注:私のテストでは、リモートマシンは実際には自分のマシンでしたが、別のマシンと同じように、OpenRemoteBaseKeyを介してアクセスしました。


4

これを試してください(32ビットプロセスから):

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

ここで見つかりまし)。


1
素敵なヒント、それはバッチでレジストリを操作することを可能にします。reg.exe /?詳細情報の取得に使用します...
Matt

4

.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);
    }
}

0

私が読んだものと自分のテストから、このパス「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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.