2009-06-10 42 views
78

AnyCPU用にコンパイルされたC#ユニットテストプロジェクトがあります。私たちのビルドサーバーは64ビットマシンで、64ビットSQL Expressインスタンスがインストールされています。32ビットアプリケーションから64ビットレジストリを読み取る

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ビットのワークステーション上で正常に動作し、最近Iまでビルドサーバー上で正常に動作んでした:

テストプロジェクトは。MDFファイルへのパスを識別するために、次のようなコードを使用していますNCoverによるコードカバレッジ解析が可能になりました。 NCoverは32ビットCOMコンポーネントを使用するため、テストランナー(Gallio)は32ビットプロセスとして動作します。レジストリをチェック

は、何の "インスタンス名" キーがない

HKEY_LOCAL_MACHINE \ SOFTWARE \ WOW6432NODE \マイクロソフト\のMicrosoft SQL Serverの

は32ビットで実行中のアプリケーションのための方法はありますモードはWow6432Node外のレジストリにアクセスするには?

答えて

16

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

Thisは、あなたを始めてくれるかもしれません。

3

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

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

hereことがわかりました)。

108

.NET Framework 4.xを使用している64ビットWindowsでは、レジストリへのアクセスがネイティブでサポートされています。次のコードは、  Windows 7,64ビット  および  Windows 10,64ビットでテストされています。 64ビットレジストリにアクセスするには、使用することができます:

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(); 
} 
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64)); 

をあなたは32ビットのレジストリにアクセスする場合は、使用:

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(); 
} 
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32)); 

を混同しないでください、両方のバージョンがあります最初のパラメータとしてMicrosoft.Win32.RegistryHive.LocalMachineを使用する場合は、の64ビットまたはの32ビットのの2番目のパラメータRegistryView.Registry64RegistryView.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ビットのソースコードバージョンは、64ビットのソースコードバージョンが正しい組織を返す一方、登録した組織に関係なく常に「Microsoft」を返します。

    バックあなたが提供してきた例に来

、それを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"); 

更新:

私は思います興味深いアプローチを追加したいのですが、私は彼のアプローチを使っていくつかの便利な機能を開発するために取り上げたコメントに示唆しています。状況によっては、 32ビットか64ビットかにかかわらずすべてのキーを元に戻すことができます。 SQLインスタンス名はそのような例です。次のように(C#6以上)その場合のユニオンクエリを使用することができます。

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

今、あなたは、単に次のように上記の機能を使用することができます。

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

あなたのリストを与えるであろうsqlRegPathの値の名前と値の

SQLサーバーは32ビットまたは64ビットとしてインストールできるため、関数にはNULLの処理が必要であることに注意してください。関数はオーバーロードされているため、必要に応じて32ビットまたは64ビットのパラメータを渡すことができます。ただし、省略した場合は64ビットを読み取ろうとしますが、失敗した場合(null値)、32ビットの値を読み取ります。 GetAllRegValueNamesは通常(上記の例を参照)、ループコンテキストで使用されるため、それはforeachループを簡単にするために、空の列挙というnullよりを戻す:

ここで1軒あり、それはそのように処理されない場合には、ループは接頭辞としてnullifステートメントをチェックする必要があります。これは処理が面倒です。そのため、関数内で一度処理されます。

nullについてどうして迷惑ですか?気にしない場合は、なぜあなたのコードにnull参照例外がスローされたのかを見つけ出すことが、より多くの頭を悩ますことになります。そして、それが実際に生産された場合は、ログファイルやイベントログを勉強することが非常に忙しいでしょう(私はあなたがログを実装していることを願っています)。演算子?.,?[ ... ]および??は、多くのお手伝いをいたします(上記のコードを参照してください)。


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

ヒント:は試してみる(およびデバッグ)、新しいヌル取扱事業者に慣れるために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>" 
} 

あなたが興味を持っている場合、hereはいくつかありますあなたがツールで何ができるのかをまとめた例を示します。

+2

感謝を。メモリから、私が質問を投稿したときに.NET 3.5を使用していたと思うが、.NET 4が状況を改善していることを知ってよかった。 –

+2

よろしくお願いします。最近私が解決した64ビットレジストリにも同様の問題があったので、解決策を共有する価値があると思いました。 – Matt

+2

これはまさに私が探していたものです。私はWindows 9.1でこれをやっているし、それは素晴らしい作品です。 –

4

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

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

3

.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); 
    } 
} 
関連する問題