Introduction
We're going to enable your application to write entries in HKEY_LOCAL_MACHINE, elevating your application when necessary using the COM method enumerated in http://msdn.microsoft.com/en-us/library/bb530410.aspx#vistauac_topic6c.
We'll create a new COM object with two methods: WriteString
and WriteLong
, and we'll use that COM object when writing entries to Local Machine. Then, we'll create a console application to show the use. It is really easy, and we'll finish everything in 5 minutes. So, let's go.
Background
If after reading this topic: http://msdn.microsoft.com/en-us/library/bb530410.aspx#vistauac_topic6b, you determine that your application is a Mixed application, this article is for you.
Using the code
After registering the server, your application can use the CElevatedRegistryWritter
class to elevate and write in the Registry anytime you want.
CElevatedRegistryWritter writter(GetConsoleWindow());
if(writter.WriteString(_T("Software\\Microsoft"),
_T("Message"), _T("Hello World")) &&
writter.WriteLong(_T("Software\\Microsoft"),
_T("MyValue"), 100))
{
print("All done!");
}
Phase 1: Creating a Registry COM object
- Create a new ATL project (I'll name it Elevator).
- Select Executable (EXE) as Server type in the ATL Project Wizard dialog box.
- Add a new class to your Elevator project.
- Choose ATL Simple object, and press Add in the Add Class dialog box.
- Name your class "LocalMachineWriter" in ATL Simple Object Wizard dialog box, and press Finish.
- Add a new method to the interface
ILocalMachineWritter
.
- Call your new method
WriteString
, and add three in
parameters: path
, var
, and value
, all of them BSTR
, and press Finish.
- Repeat steps 6 and 7 for the
WriteLong
method (the value
parameter must be LONG
).
- Modify the code of
CLocalMachineWritter::WriteString
and CLocalMachineWritter::WriteLong
like this:
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;
}
- Now, we have to enable our COM object to be elevated, adding two entries to the Registry.
Open LocalMachineWriter.rgs and add the bold lines:
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'
}
}
}
}
For information about .rgs files, refer this link: http://msdn.microsoft.com/en-us/library/k32htktc.aspx. For information about why to add these entries, refer this link: http://msdn.microsoft.com/en-us/library/ms679687.aspx.
- Now, compile the COM object.
- Finally, we have to register our COM object to enable other applications to use it. Open an elevated Cmd (execute CMD.exe while pressing the Ctrl+Shift keys), go to the Debug or Release directory, and register the server, executing it with the /RegServer parameter:
And, we're finished!!
We have created a COM object, enabled to write in the Local Machine. Now, we've to create the client side code to run our COM object with elevated privileges.
- Create a new Win32 console application. Call it Client, and press OK.
- Select Console Application in the Win32 Application Wizard dialog box, and press Finish.
- Modify the StdAfx.h file to include Windows.h and comutil.h.
#include <windows.h>
#include <comutil.h>
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
We've included ComUtil.h and the library comsuppw because we'll use the _bstr_t
class (http://msdn.microsoft.com/en-us/library/zthfhkd6(VS.71).aspx).
- Copy Elevator_i.c and Elevator_i.h from your Elevator project directory to your brand new client application directory (these files are necessary because they declare the interface and the GUIDs of the interface and the object).
- Add a new class to your client application:
- Select C++ Class in the Add Class dialog box:
- Call your new class
CElevatedRegistryWritter
, and press Finish in the Generic C++ Class Wizard dialog box.
- Replace ElevatedRegistryWritter.h with this:
#pragma once
#include "Elevator_i.h"
class CElevatedRegistryWritter
{
HWND wndParent;
ILocalMachineWriter* object;
#pragma region Constructors and destructor
public:
CElevatedRegistryWritter(HWND wndParent);
~CElevatedRegistryWritter();
#pragma endregion
#pragma region Public methods
bool WriteString(LPCTSTR path, LPCTSTR var, LPCTSTR value);
bool WriteLong(LPCTSTR path, LPCTSTR var, LONG value);
#pragma endregion
#pragma region Implementation
private:
HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid,
REFIID riid, __out void ** ppv);
#pragma endregion
};
and ElevatedRegistryWritter.cpp with this:
#include "StdAfx.h"
#include "ElevatedRegistryWritter.h"
#include "Elevator_i.c"
CElevatedRegistryWritter::CElevatedRegistryWritter(HWND _wndParent)
: object(NULL)
, wndParent(_wndParent)
{
}
CElevatedRegistryWritter::~CElevatedRegistryWritter(void)
{
if(NULL != object)
object->Release();
}
bool CElevatedRegistryWritter::WriteString(LPCTSTR path, LPCTSTR var, LPCTSTR value)
{
if( NULL == object && FAILED(CoCreateInstanceAsAdmin(wndParent,
CLSID_LocalMachineWriter, IID_ILocalMachineWriter, (void**)&object)))
return false;
SUCCEEDED(object->WriteString(_bstr_t(path), _bstr_t(var), _bstr_t(value)));
}
bool CElevatedRegistryWritter::WriteLong(LPCTSTR path, LPCTSTR var, LONG value)
{
if( NULL == object && FAILED(CoCreateInstanceAsAdmin(wndParent,
CLSID_LocalMachineWriter, IID_ILocalMachineWriter, (void**)&object)))
return false;
SUCCEEDED(object->WriteLong(_bstr_t(path), _bstr_t(var), value));
}
HRESULT CElevatedRegistryWritter::CoCreateInstanceAsAdmin(HWND hwnd,
REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
BIND_OPTS3 bo;
WCHAR wszCLSID[50];
WCHAR wszMonikerName[300];
StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]));
_stprintf_s(wszMonikerName, L"Elevation:Administrator!new:%s", wszCLSID);
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return CoGetObject(wszMonikerName, &bo, riid, ppv);
}
- And finally, update your
main()
function, first initializing COM and then using our class.
#include "stdafx.h"
#include "ElevatedRegistryWritter.h"
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
MessageBox(NULL, _T("Accept to elevate"),
_T("Testing elevation"), MB_OK);
CElevatedRegistryWritter writter(GetConsoleWindow());
if(writter.WriteString(_T("Software\\Microsoft"),
_T("Message"), _T("Hello World")) &&
writter.WriteLong(_T("Software\\Microsoft"),
_T("MyValue"), 100))
{}
CoUninitialize();
return 0;
}