从32位程序启动64位版本的regedit以创建sql别名

本文关键字:regedit 创建 sql 别名 版本 32位 程序 启动 64位 | 更新日期: 2023-09-27 18:16:46

我的应用程序需要设置一个SQL别名,当它运行时,如果它检测到别名没有设置。现在我让它生成一个临时Reg文件,并通过regedit.exe运行它,但是,因为我的应用程序是32位的(它必须是因为我正在与一些32位的dll交互,我无法获得64位版本),当我运行regedit到版本%windir%'SysWow64'regedit.exe而不是%windir%'regedit.exe时,windows正在做重定向。

这导致我试图写入[HKEY_LOCAL_MACHINE'SOFTWARE'Microsoft'MSSQLServer'Client'ConnectTo]的密钥被重定向到32位子文件夹,并且我显式地写入32位子文件夹,[HKEY_LOCAL_MACHINE'SOFTWARE'Wow6432Node'Microsoft'MSSQLServer'Client'ConnectTo]我不知道它们要去哪里。

通常要解决这个问题,您只需使用%windir%'sysnative'xxxx.exe,但sysnative重定向到System32文件夹而不是根windows文件夹,这是regedit所在的位置。

有没有一种方法可以解决这个问题,而不需要编写自定义程序来提升和执行它自己?


这是我当前的代码,这是失败的

static void CreateAliases()
{
    using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
    {
        using (var key = baseKey.OpenSubKey(@"SOFTWARE'Microsoft'MSSQLServer'Client'ConnectTo"))
        {
            CheckKeys(key);
        }
    }
    try
    {
        using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
        {
            using (var key = baseKey.OpenSubKey(@"SOFTWARE'Microsoft'MSSQLServer'Client'ConnectTo"))
            {
                CheckKeys(key);
            }
        }
    }
    catch
    {
        //Catch failues if it is 32 bit only.
    }
}
private static void CheckKeys(RegistryKey key)
{
    //check to see if the key exists.
    if (key == null)
    {
        AddKeys();
        return;
    }
    var value = key.GetValue(@"wi'sql2008");
    if (value == null || value.ToString() != String.Concat("DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2008Port))
    {
        AddKeys();
        return;
    }
    value = key.GetValue(@"wi'sql2005");
    if (value == null || value.ToString() != String.Concat("DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2005Port))
    {
        AddKeys();
        return;
    }
}
static private void AddKeys()
{
    string file = System.IO.Path.GetTempFileName();
    using(StreamWriter sw = new StreamWriter(file))
    {
        sw.WriteLine("Windows Registry Editor Version 5.00");
        sw.WriteLine();
        sw.WriteLine(@"[HKEY_LOCAL_MACHINE'SOFTWARE'Wow6432Node'Microsoft'MSSQLServer'Client'ConnectTo]");
        sw.WriteLine(String.Concat("'"wi''''sql2005'"='"DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2005Port,'"'));
        sw.WriteLine(String.Concat("'"wi''''sql2008'"='"DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2008Port,'"'));
        sw.WriteLine();
        sw.WriteLine(@"[HKEY_LOCAL_MACHINE'SOFTWARE'Microsoft'MSSQLServer'Client'ConnectTo]");
        sw.WriteLine(String.Concat("'"wi''''sql2005'"='"DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2005Port, '"'));
        sw.WriteLine(String.Concat("'"wi''''sql2008'"='"DBMSSOCN,wi,", Properties.Settings.Default.wi_sql2008Port, '"'));
    }
    WindowsIdentity identity = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(identity);
    bool IsAdmin = principal.IsInRole("BUILTIN''Administrators");
    string regedit;
    if (Environment.Is64BitProcess)
    {
        regedit = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "regedit");
    }
    else
    {
        regedit = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "sysnative", "regedit"); //regedit.exe does not exist in sysnative.
    }
    if (IsAdmin)
    {
        var proc = Process.Start(new ProcessStartInfo(regedit, String.Concat("/s ", file)));
        proc.WaitForExit();
    }
    else
    {
        MessageBox.Show("Updating registry keys for WI alias, this must be run as administrator");
        var proc = Process.Start(new ProcessStartInfo(regedit, String.Concat("/s ", file)) { Verb = "runas", UseShellExecute = true });
        proc.WaitForExit();
    }
    File.Delete(file);
}

这是正在生成的临时文件。

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE'SOFTWARE'Wow6432Node'Microsoft'MSSQLServer'Client'ConnectTo]
"wi''sql2005"="DBMSSOCN,wi,49224"
"wi''sql2008"="DBMSSOCN,wi,49681"
[HKEY_LOCAL_MACHINE'SOFTWARE'Microsoft'MSSQLServer'Client'ConnectTo]
"wi''sql2005"="DBMSSOCN,wi,49224"
"wi''sql2008"="DBMSSOCN,wi,49681"

从32位程序启动64位版本的regedit以创建sql别名

我会考虑使用SMO ServerAlias类创建服务器别名,这样您就不必自己处理注册表访问了。

为什么不直接使用。net Framework注册表类呢?

如果您使用RegistryKey。OpenBaseKey并针对64位注册视图,它将在32位机器上打开32位部分,在64位机器上打开64位部分。

为了处理作为管理员组的成员更新注册表项的需要,您可以简单地提示用户输入管理员用户名和密码,在执行工作时模拟该用户。

例如,下面是我们用来登录用户的类:
public class SecurityGeneral
{
    [System.Runtime.InteropServices.DllImport("advapi32.dll", EntryPoint = "LogonUser", ExactSpelling = false, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
    private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    [System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint = "CloseHandle", ExactSpelling = false, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);
    private const int LOGON32_PROVIDER_DEFAULT = 0;
    //This parameter causes LogonUser to create a primary token.
    private const int LOGON32_LOGON_INTERACTIVE = 2;
    public static IntPtr LogonAsUser(string sUserName, string sPassword)
    {
        IntPtr tokenHandle = new IntPtr(0);
        tokenHandle = IntPtr.Zero;
        string[] asNameParts = null;
        string sName = null;
        string sDomain = "";
        asNameParts = sUserName.Split('''');
        if (asNameParts.Length == 2)
        {
            sDomain = asNameParts[0];
            sName = asNameParts[1];
        }
        else
        {
            sName = asNameParts[0];
        }
        // Call LogonUser to obtain a handle to an access token.
        if (LogonUser(sName, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle))
        {
            return tokenHandle;
        }
        else
        {
            return IntPtr.Zero;
        }
    }
    public static void LogonAsUserEnd(IntPtr oToken)
    {
        try
        {
            if (!((oToken == IntPtr.Zero)))
            {
                CloseHandle(oToken);
            }
        }
        catch
        {
        }
    }
}

一旦从用户那里获得了用户名和密码,就可以调用这个方法,在todo位置插入注册表更新代码:

    public void UpdateRegistryAsUser(string sUser, string sPassword)
    {
        IntPtr tokenHandle = default(IntPtr);
        tokenHandle = SecurityGeneral.LogonAsUser(sUser, sPassword);
        if (!((tokenHandle == IntPtr.Zero)))
        {
            WindowsImpersonationContext oImpersonatedUser = null;
            try
            {
                // Use the token handle returned by LogonUser.
                WindowsIdentity oNewIdentity = new WindowsIdentity(tokenHandle);
                oImpersonatedUser = oNewIdentity.Impersonate();
                // ToDo: add your registry updates here
            }
            finally
            {
                // Stop impersonating the user.
                if (oImpersonatedUser != null)
                {
                    oImpersonatedUser.Undo();
                }
                SecurityGeneral.LogonAsUserEnd(tokenHandle);
            }
        }
    }

这将导致代码在提供的用户的上下文中执行。