Đọc đăng ký 64 bit từ ứng dụng 32 bit


98

Tôi có dự án thử nghiệm đơn vị ac # được biên dịch cho AnyCPU. Máy chủ xây dựng của chúng tôi là máy 64 bit và đã cài đặt phiên bản SQL Express 64 bit.

Dự án thử nghiệm sử dụng mã tương tự như sau để xác định đường dẫn đến tệp .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();
    }

Mã này hoạt động tốt trên các máy trạm 32 bit của chúng tôi và đã hoạt động tốt trên máy chủ xây dựng cho đến khi gần đây tôi đã bật phân tích phạm vi mã với NCover. Bởi vì NCover sử dụng thành phần COM 32 bit, trình chạy thử nghiệm (Gallio) chạy như một quy trình 32 bit.

Kiểm tra sổ đăng ký, không có khóa "Tên phiên bản" dưới

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Microsoft SQL Server

Có cách nào để ứng dụng đang chạy ở chế độ 32bit truy cập vào sổ đăng ký bên ngoài Wow6432Node không?

Câu trả lời:


21

bạn phải sử dụng tham số KEY_WOW64_64KEY khi tạo / mở khóa đăng ký. Nhưng AFAIK không thể thực hiện được với lớp Registry mà chỉ khi sử dụng trực tiếp API.

Điều này có thể giúp bạn bắt đầu.


151

Hiện vẫn còn hỗ trợ cho truy xuất registry dưới Windows 64 bit sử dụng .NET Framework 4.x . Đoạn mã sau được thử nghiệm với   Windows 7, 64 bit   và cả   Windows 10, 64 bit .

Thay vì sử dụng "Wow6432Node", mô phỏng một nút bằng cách ánh xạ một cây đăng ký này vào một cây đăng ký khác làm cho nó xuất hiện ở đó hầu như, bạn có thể thực hiện theo cách sau:

Quyết định xem bạn cần truy cập sổ đăng ký 64 bit hay 32 bit và sử dụng nó như được mô tả bên dưới. Bạn cũng có thể sử dụng mã tôi đã đề cập sau (phần Thông tin bổ sung), mã này tạo truy vấn liên hợp để lấy khóa đăng ký từ cả hai nút trong một truy vấn - vì vậy bạn vẫn có thể truy vấn chúng bằng cách sử dụng đường dẫn thực của chúng.

Đăng ký 64 bit

Để truy cập sổ đăng ký 64 bit , bạn có thể sử dụng RegistryView.Registry64như sau:

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

Đăng ký 32 bit

Nếu bạn muốn truy cập sổ đăng ký 32 bit , hãy sử dụng RegistryView.Registry32như sau:

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

Đừng nhầm lẫn, cả hai phiên bản đều đang sử dụng Microsoft.Win32.RegistryHive.LocalMachinelàm tham số đầu tiên, bạn phân biệt xem nên sử dụng 64 bit hay 32 bit bằng tham số thứ 2 ( RegistryView.Registry64so với RegistryView.Registry32).

Lưu ý rằng

  • Trên Windows 64 bit, HKEY_LOCAL_MACHINE\Software\Wow6432Nodechứa các giá trị được sử dụng bởi các ứng dụng 32 bit chạy trên hệ thống 64 bit. Chỉ các ứng dụng 64 bit thực sự mới lưu trữ HKEY_LOCAL_MACHINE\Softwaretrực tiếp các giá trị của chúng . Cây con Wow6432Nodehoàn toàn trong suốt đối với các ứng dụng 32 bit, các ứng dụng 32 bit vẫn nhìn thấy HKEY_LOCAL_MACHINE\Softwarenhư họ mong đợi (nó là một loại chuyển hướng). Trong các phiên bản Windows cũ hơn cũng như Windows 7 32 bit (và Vista 32 bit), cây con Wow6432Noderõ ràng là không tồn tại.

  • Do một lỗi trong Windows 7 (64 bit), phiên bản mã nguồn 32 bit luôn trả về "Microsoft" bất kể bạn đã đăng ký tổ chức nào trong khi phiên bản mã nguồn 64 bit trả về đúng tổ chức.

Quay lại ví dụ bạn đã cung cấp, hãy thực hiện theo cách sau để truy cập nhánh 64 bit:

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

Thông tin bổ sung - để sử dụng thực tế:

Tôi muốn thêm một cách tiếp cận thú vị mà Johny Skovdal đã đề xuất trong các nhận xét, mà tôi đã chọn để phát triển một số chức năng hữu ích bằng cách sử dụng cách tiếp cận của anh ấy: Trong một số tình huống, bạn muốn lấy lại tất cả các khóa bất kể nó là 32 bit hay 64 bit. Các tên phiên bản SQL là một ví dụ. Bạn có thể sử dụng truy vấn liên hợp trong trường hợp đó như sau (C # 6 trở lên):

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

Bây giờ bạn có thể chỉ cần sử dụng các chức năng trên như sau:

Ví dụ 1: Lấy tên phiên bản 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}");
}

sẽ cung cấp cho bạn danh sách tên giá trị và giá trị trong sqlRegPath.

Lưu ý: Bạn có thể truy cập giá trị mặc định của một khóa (được hiển thị bởi công cụ dòng lệnh REGEDT32.EXEdưới dạng (Default)) nếu bạn bỏ qua ValueNametham số trong các chức năng tương ứng ở trên.

Để nhận danh sách các SubKeys trong khóa đăng ký, hãy sử dụng hàm GetRegKeyNameshoặc GetAllRegKeyNames. Bạn có thể sử dụng danh sách này để duyệt qua các khóa khác trong sổ đăng ký.

Ví dụ 2: Lấy thông tin gỡ cài đặt của phần mềm đã cài đặt

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

sẽ nhận được tất cả các khóa gỡ cài đặt 32 bit và 64 bit.

Lưu ý xử lý null được yêu cầu trong các hàm vì máy chủ SQL có thể được cài đặt dưới dạng 32 bit hoặc 64 bit (Ví dụ 1 ở trên). Các hàm bị quá tải nên bạn vẫn có thể truyền tham số 32 bit hoặc 64 bit nếu được yêu cầu - tuy nhiên, nếu bạn bỏ qua nó thì nó sẽ cố đọc 64 bit, nếu không thành công (giá trị null), nó sẽ đọc các giá trị 32 bit.

Có một điểm đặc biệt ở đây: Vì GetAllRegValueNamesthường được sử dụng trong ngữ cảnh vòng lặp (xem Ví dụ 1 ở trên), nó trả về một kiểu liệt kê trống thay vì nullđơn giản hóa foreachcác vòng lặp: nếu nó không được xử lý theo cách đó, vòng lặp sẽ phải có tiền tố là một ifcâu lệnh kiểm tra nullcái nào sẽ rất phức tạp khi phải làm điều đó - để điều đó được xử lý một lần trong hàm.

Tại sao phải bận tâm về null? Bởi vì nếu bạn không quan tâm, bạn sẽ phải đau đầu hơn rất nhiều khi tìm ra lý do tại sao ngoại lệ tham chiếu rỗng đó lại được đưa vào mã của bạn - bạn sẽ mất rất nhiều thời gian để tìm ra vị trí và lý do nó xảy ra. Và nếu nó xảy ra trong quá trình sản xuất, bạn sẽ rất bận rộn với việc nghiên cứu các tệp nhật ký hoặc nhật ký sự kiện (tôi hy vọng bạn đã triển khai ghi nhật ký) ... tốt hơn hãy tránh các vấn đề vô hiệu khi bạn có thể theo cách phòng thủ. Các nhà khai thác ?., ?[... ]??có thể giúp bạn rất nhiều (xem đoạn mã được cung cấp ở trên). Có một bài viết liên quan rất hay thảo luận về các kiểu tham chiếu nullable mới trong C # mà tôi khuyên bạn nên đọc và cả bài viết này về toán tử Elvis.


Gợi ý: Bạn có thể sử dụng phiên bản Linqpad miễn phí để kiểm tra tất cả các ví dụ trong Windows. Nó không yêu cầu cài đặt. Đừng quên nhấn F4và nhập Microsoft.Win32vào tab nhập Không gian tên. Trong Visual Studio, bạn yêu cầu using Microsoft.Win32;ở đầu mã của mình.

Mẹo: Để tự làm quen với các toán tử xử lý null mới , hãy thử (và gỡ lỗi) mã sau trong LinqPad:

Ví dụ 3: Trình bày các toán tử xử lý 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>"
}

Hãy thử nó với .Net fiddle

Nếu bạn quan tâm, đây là một số ví dụ tôi tổng hợp cho thấy bạn có thể làm gì khác với công cụ này.


2
Cảm ơn vì câu trả lời toàn diện. Từ bộ nhớ Tôi nghĩ rằng tôi đã sử dụng .NET 3.5 khi tôi đăng các câu hỏi, nhưng tốt để xem NET 4 đã được cải thiện tình hình
David Gardiner

2
Không có gì. Tôi đã gặp sự cố tương tự với sổ đăng ký 64 bit gần đây mà tôi đã giải quyết được vì vậy tôi nghĩ rằng rất đáng để chia sẻ giải pháp.
Matt

2
Đây chính xác là những gì tôi đang tìm kiếm. Tôi đang làm điều này trong Windows 9.1 và nó hoạt động rất tốt.
Michiel Bugher,

1
@AZ_ - cảm ơn bạn đã chỉnh sửa, bạn nói đúng, cần đóng chìa khóa!
Matt

1
@JohnySkovdal - Tôi đã thay đổi tiêu đề để nói rõ rằng tôi chỉ cung cấp thông tin bổ sung (tùy chọn) - cho những ai muốn tìm hiểu sâu hơn về vấn đề này.
Matt

6

Tôi không có đủ đại diện để nhận xét, nhưng điều đáng chú ý là nó hoạt động khi mở sổ đăng ký từ xa bằng OpenRemoteBaseKey. Việc thêm tham số RegistryView.Registry64 cho phép chương trình 32-bit trên Máy A truy cập vào sổ đăng ký 64-bit trên Máy B. Trước khi tôi chuyển tham số đó, chương trình của tôi đang đọc 32-bit sau OpenRemoteBaseKey và không tìm thấy khóa tôi. là sau khi.

Lưu ý: Trong thử nghiệm của tôi, máy từ xa thực sự là máy của tôi, nhưng tôi đã truy cập nó qua OpenRemoteBaseKey, giống như cách tôi làm với một máy khác.


4

hãy thử điều này (từ quy trình 32 bit):

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

(tìm thấy ở đây ).


1
Gợi ý hay, nó cho phép thao tác đăng ký hàng loạt. Sử dụng reg.exe /?để biết thêm thông tin ...
Matt

4

Nếu bạn không thể sử dụng .NET 4 với nó RegistryKey.OpenBaseKey(..., RegistryView.Registry64), bạn cần phải sử dụng Windows API trực tiếp.

Tương tác tối thiểu giống như:

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

Sử dụng nó như:

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

Từ những gì tôi đã đọc và từ các thử nghiệm của riêng mình, đối với tôi, dường như sổ đăng ký nên được kiểm tra trong đường dẫn này "SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Uninstall". Bởi vì trong các đường dẫn khác, các thanh ghi không bị xóa sau khi gỡ cài đặt chương trình.

Bằng cách này, tôi nhận được 64 thanh ghi với cấu hình 32 bit.

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

Đối với 32 thanh ghi là:

registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.