在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;
}