在Windows操作系统中,对于需要管理员权限才能执行的操作,如写入HKEY_LOCAL_MACHINE注册表,可以通过创建一个COM对象来实现。本文将介绍如何创建这样一个COM对象,并使用它来在需要管理员权限的情况下写入注册表。
如果应用程序是一个混合应用程序,即既有需要管理员权限的操作,也有不需要管理员权限的操作,那么本文将对非常有用。将通过创建一个COM对象,使得应用程序在需要时能够提升权限。
在注册服务器之后,应用程序可以使用CElevatedRegistryWriter类来提升权限并随时写入注册表。
        C++
        CElevatedRegistryWriter writter(GetConsoleWindow());
        if
        (writter.WriteString(_T(
        "
        Software\\Microsoft"
        ), 
                        _T(
        "
        Message"
        ), _T(
        "
        Hello World"
        )) &&
            writter.WriteLong(_T(
        "
        Software\\Microsoft"
        ), 
                           _T(
        "
        MyValue"
        ),
        100
        ))
        {
            print(
            "
            All done!"
            );
        }
    
首先,创建一个新的ATL项目(将其命名为Elevator)。在ATL项目向导对话框中选择可执行文件(EXE)作为服务器类型。然后,向Elevator项目添加一个新类。选择ATL简单对象,并在添加类对话框中点击添加。
在ATL简单对象向导对话框中,将类命名为"LocalMachineWriter",然后点击完成。接下来,为接口ILocalMachineWriter添加一个新方法。将新方法命名为WriteString,并添加三个参数:path、var和value,它们都是BSTR类型,然后点击完成。
重复步骤6和7为WriteLong方法(value参数必须是LONG类型)。然后,修改CLocalMachineWriter::WriteString和CLocalMachineWriter::WriteLong的代码如下:
        STDMETHODIMP CLocalMachineWriter::WriteString(BSTR path, BSTR var, BSTR value)
        {
            HKEY hk = NULL;
            if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ | KEY_WRITE, &hk))
                return E_FAIL;
            bool nReturn = E_FAIL;
            if (ERROR_SUCCESS == RegSetValueEx(hk, var, 0, REG_SZ, (const BYTE*)value, (1 + (DWORD)_tcslen(value)) * sizeof(TCHAR)))
                nReturn = S_OK;
            RegCloseKey(hk);
            return nReturn;
        }
        STDMETHODIMP CLocalMachineWriter::WriteLong(BSTR path, BSTR var, LONG value)
        {
            HKEY hk = NULL;
            if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ | KEY_WRITE, &hk))
                return E_FAIL;
            bool nReturn = E_FAIL;
            if (ERROR_SUCCESS == RegSetValueEx(hk, var, 0, REG_DWORD, (const BYTE*)&value, sizeof(LONG)))
                nReturn = S_OK;
            RegCloseKey(hk);
            return nReturn;
        }
    
现在,需要使COM对象能够提升权限,通过向注册表添加两个条目。打开LocalMachineWriter.rgs并添加以下内容:
        HKCR
        {
            Elevator.LocalMachineWriter.1 = s 'LocalMachineWriter Class'
            {
                CLSID = s '{E89BECE0-84AC-4EE0-BC26-835C269C166F}'
            }
            Elevator.LocalMachineWriter = s 'LocalMachineWriter Class'
            {
                CLSID = s '{E89BECE0-84AC-4EE0-BC26-835C269C166F}'
                CurVer = s 'Elevator.LocalMachineWriter.1'
            }
            NoRemove CLSID
            {
                ForceRemove {E89BECE0-84AC-4EE0-BC26-835C269C166F} = s 'LocalMachineWriter Class'
                {
                    ProgID = s 'Elevator.LocalMachineWriter.1'
                    VersionIndependentProgID = s 'Elevator.LocalMachineWriter'
                    ForceRemove 'Programmable'
                    LocalServer32 = s '%MODULE%'
                    TypeLib' = s '{A38F2A11-AF97-4903-9DF3-F757291C9358}'
                    val LocalizedString = s '@%MODULE%,-100'
                    ForceRemove Elevation = s ''
                    {
                        val Enabled = d '1'
                    }
                }
            }
        }
    
有关.rgs文件的信息,请参考此链接:http://msdn.microsoft.com/en-us/library/k32htktc.aspx。有关为什么要添加这些条目的信息,请参考此链接:http://msdn.microsoft.com/en-us/library/ms679687.aspx。
现在,编译COM对象。最后,需要注册COM对象,以便其他应用程序可以使用它。打开一个提升权限的Cmd(在执行CMD.exe时按下Ctrl+Shift键),转到Debug或Release目录,并使用/RegServer参数注册服务器:
完成了!已经创建了一个COM对象,使其能够写入本地计算机。现在,需要创建客户端代码,以提升的权限运行COM对象。
创建一个新的Win32控制台应用程序。将其命名为Client,并按OK。在Win32应用程序向导对话框中选择控制台应用程序,然后按完成。
修改StdAfx.h文件,包括Windows.h和comutil.h。
        #include 
        #include 
        #ifdef _DEBUG
        #pragma comment(lib, "comsuppwd.lib")
        #else
        #pragma comment(lib, "comsuppw.lib")
        #endif
      
包括了ComUtil.h和库comsuppw,因为将使用_bstr_t类(http://msdn.microsoft.com/en-us/library/zthfhkd6(VS.71).aspx)。
从Elevator项目目录复制Elevator_i.c和Elevator_i.h到新客户端应用程序目录(这些文件是必要的,因为它们声明了接口和接口的GUID以及对象)。
向客户端应用程序添加一个新类:在添加类对话框中选择C++类。将新类命名为CElevatedRegistryWriter,并在通用C++类向导对话框中点击完成。
替换ElevatedRegistryWriter.h和ElevatedRegistryWriter.cpp的内容如下:
        #pragma once
        #include "Elevator_i.h"
        class CElevatedRegistryWriter
        {
            HWND wndParent;
            ILocalMachineWriter* object;
        public:
            CElevatedRegistryWriter(HWND wndParent);
            ~CElevatedRegistryWriter();
            bool WriteString(LPCTSTR path, LPCTSTR var, LPCTSTR value);
            bool WriteLong(LPCTSTR path, LPCTSTR var, LONG value);
        private:
            HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void** ppv);
        };
    
        #include "stdafx.h"
        #include "ElevatedRegistryWriter.h"
        int _tmain(int argc, _TCHAR* argv[])
        {
            CoInitialize(NULL);
            MessageBox(NULL, _T("Accept to elevate"), _T("Testing elevation"), MB_OK);
            CElevatedRegistryWriter writter(GetConsoleWindow());
            if (writter.WriteString(_T("Software\\Microsoft"), _T("Message"), _T("Hello World")) && 
                writter.WriteLong(_T("Software\\Microsoft"), _T("MyValue"), 100))
            {}
            CoUninitialize();
            return 0;
        }