2

我最近编写了一个模拟用户帐户的应用程序,获取 CURRENT_USER 注册表项的句柄(使用 PInvoke“LoadUserProfile”检索 ProfileInfo.hProfile 对象)并使用 RegistryKey.FromHandle 创建注册表项。

参考代码:

using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(hToken))
{
    using (SafeRegistryHandle safeHandle = new SafeRegistryHandle(hProfile, true))
    {
        using (RegistryKey impersonatedUserHkcu = RegistryKey.FromHandle(safeHandle, RegistryView.Default))
        {
            // Do something with registry
        }
    }
}

这段代码运行良好(在 Windows 7 中运行),但使用了仅受 .NET 4.0 及更高版本支持的对象/方法(SafeRegistryHandle、RegistryKey.FromHandle()、RegistryView 枚举)。

现在,我需要使这个应用程序与 .NET 3.5 兼容,以便在装有 Windows XP 且无法安装 .NET Framework 4.0 的机器上使用它。

是否有任何等效的对象可以与 .NET 3.5 一起使用来实现相同的结果?(即修改模拟用户的注册表项)。或者是否存在某种唯一的.NET 4 对象的源代码?

4

2 回答 2

5

经过几天的研究,以及 MSDN 社区对同一问题的帮助,我找到了满足我需求的方法。

最初的建议是使用 Win Api 函数RegOpenKeyEx(有关信息和示例,请参见P/Invoke 网站);但根据这篇 MSDN 文章,我发现

如果您的服务或应用程序模拟不同的用户,请不要将此函数与 HKEY_CURRENT_USER 一起使用。而是调用 RegOpenCurrentUser 函数。

最后,要走的路是RegOpenCurrentUser功能。(遗憾的是在 P/Invoke 网站上仍然没有这个功能的踪迹,但你可以在 MSDN 上找到一些信息)

这就是我目前定义它的方式:

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenCurrentUser(int samDesired, out IntPtr phkResult);    

public enum RegistrySecurity
{
    KEY_ALL_ACCESS = 0xF003F,
    KEY_CREATE_LINK = 0x0020,
    KEY_CREATE_SUB_KEY = 0x0004,
    KEY_ENUMERATE_SUB_KEYS = 0x0008,
    KEY_EXECUTE = 0x20019,
    KEY_NOTIFY = 0x0010,
    KEY_QUERY_VALUE = 0x0001,
    KEY_READ = 0x20019,
    KEY_SET_VALUE = 0x0002,
KEY_WOW64_32KEY = 0x0200,
    KEY_WOW64_64KEY = 0x0100,
    KEY_WRITE = 0x20006,
}

public IntPtr GetImpersonateUserRegistryHandle(RegistrySecurity _access)
{
    IntPtr safeHandle = new IntPtr();
    int result = RegOpenCurrentUser((int)_access, out safeHandle);

    return safeHandle;
}

/// <summary>
/// Get a registry key from a pointer.
/// </summary>
/// <param name="hKey">Pointer to the registry key</param>
/// <param name="writable">Whether or not the key is writable.</param>
/// <param name="ownsHandle">Whether or not we own the handle.</param>
/// <returns>Registry key pointed to by the given pointer.</returns>
public RegistryKey _pointerToRegistryKey(IntPtr hKey, bool writable, bool ownsHandle)
{
    //Get the BindingFlags for private contructors
    System.Reflection.BindingFlags privateConstructors = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;

    //Get the Type for the SafeRegistryHandle
    Type safeRegistryHandleType =
            typeof(Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");

    //Get the array of types matching the args of the ctor we want
    Type[] safeRegistryHandleCtorTypes = new Type[] { typeof(IntPtr), typeof(bool) };

    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo safeRegistryHandleCtorInfo = safeRegistryHandleType.GetConstructor(
            privateConstructors, null, safeRegistryHandleCtorTypes, null);

    //Invoke the constructor, getting us a SafeRegistryHandle
    Object safeHandle = safeRegistryHandleCtorInfo.Invoke(new Object[] { hKey, ownsHandle });

    //Get the type of a RegistryKey
    Type registryKeyType = typeof(RegistryKey);

    //Get the array of types matching the args of the ctor we want
    Type[] registryKeyConstructorTypes = new Type[] { safeRegistryHandleType, typeof(bool) };

    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo registryKeyCtorInfo = registryKeyType.GetConstructor(
            privateConstructors, null, registryKeyConstructorTypes, null);

    //Invoke the constructor, getting us a RegistryKey
    RegistryKey resultKey = (RegistryKey)registryKeyCtorInfo.Invoke(new Object[] { safeHandle, writable });

    //return the resulting key
    return resultKey;
}

这就是我使用它来获取注册表的方式:

IntPtr localRegistryHandle = GetImpersonateUserRegistryHandle(TestRegistryAccess.RegistrySecurity.KEY_ALL_ACCESS);

using(RegistryKey localRegistry = _pointerToRegistryKey(localRegistryHandle, true, true))
{
    // do something with local registry
}
于 2013-05-29T08:18:47.040 回答
0

写了一个我在这里发布的模拟类来回答同样的问题: 模拟管理员帐户以编辑注册表项不起作用(C#)

要写入密钥,您只需执行以下操作:

string userName = "domain\user";
string password = "whatever";
string KEY_STR = "SomeSubKey\\ASubKeyToThat";

WindowsImpersonationContext adminContext = Impersonation.getWic(userName, password);
if (adminContext != null)
{
    try
    {
       RegistryKey key = Registry.CurrentUser.OpenSubKey(KEY_STR, true);
       key.SetValue("State", 0x60001);
    }
    catch (Exception ex)
    {
        Console.Out.WriteLine("\nUnable to set registry value:\n\t" + ex.Message);
        Impersonation.endImpersonation();
        adminContext.Undo();
    }
    finally
    {
        Impersonation.endImpersonation();
        // The above line does this --            
        //if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
        adminContext.Undo();
    }
}

除了获得所需之外,没有句柄或其他花哨的功能WindowsImpersonationContext。没有重新发布那部分,因为看起来你已经知道如何获得 WIC。

于 2017-02-22T22:56:38.093 回答