我的应用程序需要在运行时设置SQL别名,如果它检测到别名未设置。现在我有它生成一个临时Reg文件并通过regedit.exe运行它,但是因为我的应用程序是32位(它必须是因为我正在使用一些32位dll,我无法获得64位版本)windows当我将regedit运行到版本%windir%\SysWow64\regedit.exe
而不是%windir%\regedit.exe
时,我正在进行重定向。
这会导致我尝试写入[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文件夹而不是根窗口文件夹,这是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"
答案 0 :(得分:1)
我会考虑使用SMO ServerAlias class创建服务器别名,然后您不必自己处理注册表访问。
答案 1 :(得分:1)
为什么不直接使用.Net Framework Registry类?
如果使用RegistryKey.OpenBaseKey并将64位RegistryView作为目标,它将在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);
}
}
}
这将导致代码在提供的用户的上下文中执行。