Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Elevating your application for dummies: A step-by-step guide to elevate and write in the Registry

0.00/5 (No votes)
8 Apr 2009 1  
We are going to enable your application to write entries in HKEY_LOCAL_MACHINE, elevating your application when necessary.

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

  1. Create a new ATL project (I'll name it Elevator).
  2. Select Executable (EXE) as Server type in the ATL Project Wizard dialog box.
  3. Add a new class to your Elevator project.
  4. Choose ATL Simple object, and press Add in the Add Class dialog box.
  5. Name your class "LocalMachineWriter" in ATL Simple Object Wizard dialog box, and press Finish.
  6. Add a new method to the interface ILocalMachineWritter.
  7. Call your new method WriteString, and add three in parameters: path, var, and value, all of them BSTR, and press Finish.
  8. Repeat steps 6 and 7 for the WriteLong method (the value parameter must be LONG).
  9. Modify the code of CLocalMachineWritter::WriteString and CLocalMachineWritter::WriteLong like this:
  10. 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;
    }
  11. Now, we have to enable our COM object to be elevated, adding two entries to the Registry.
  12. 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.

  13. Now, compile the COM object.
  14. 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.

  15. Create a new Win32 console application. Call it Client, and press OK.
  16. New project

  17. Select Console Application in the Win32 Application Wizard dialog box, and press Finish.
  18. Modify the StdAfx.h file to include Windows.h and comutil.h.
  19. #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).

  20. 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).
  21. Add a new class to your client application:
  22. Select C++ Class in the Add Class dialog box:
  23. Call your new class CElevatedRegistryWritter, and press Finish in the Generic C++ Class Wizard dialog box.
  24. Replace ElevatedRegistryWritter.h with this:
  25. #pragma once
    
    #include "Elevator_i.h"
    
    ///    <summary>
    ///    Wrapper class to load the LocalMachineWritter object
    /// with elevated credentials.
    ///    </summary>
    ///    <creation date="08/04/2009" by="Jose Angel"/>
    class CElevatedRegistryWritter
    {
        HWND wndParent;
        ILocalMachineWriter* object;
    
    #pragma region Constructors and destructor
    public:
        ///    <summary>
        ///        Constructor of the class
        ///    </summary>
        ///    <param name="wndParent">
        ///        Handle to a window. 
        ///        Use <c>GetConsoleWindow</c>
        ///         or <c>AfxGetMainWnd</c> for instance
        ///    </param>
        CElevatedRegistryWritter(HWND wndParent);
    
        ///    <summary>
        ///        Destructor. It will unload the Com object if necessary.
        ///    </summary>
        ~CElevatedRegistryWritter();
    #pragma endregion
    
    #pragma region Public methods
        ///    <summary>
        ///        This method ininialize the Com object
        ///     if necessary and write a string in the registry.
        ///    </summary>
        ///    <param name="path">Path of the key to write.</param>
        ///    <param name="var">Name of the key to write.</param>
        ///    <param name="value">Value to write.</param>
        ///    <returns>True if success.</returns>
        bool WriteString(LPCTSTR path, LPCTSTR var, LPCTSTR value);
    
        ///    <summary>
        ///        This method ininialize the Com object
        ///     if necessary and write a long in the registry.
        ///    </summary>
        ///    <param name="path">Path of the key to write.</param>
        ///    <param name="var">Name of the key to write.</param>
        ///    <param name="value">Value to write.</param>
        ///    <returns>True if success.</returns>
        bool WriteLong(LPCTSTR path, LPCTSTR var, LONG value);
    #pragma endregion
    
    #pragma region Implementation
    private:
        ///    <summary>
        ///        Utility method to create the object as an administrator.
        ///    </summary>
        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);
    }
  26. And finally, update your main() function, first initializing COM and then using our class.
  27. #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;
    }

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here