none
UAC - Elevated Com Moniker RRS feed

  • Question

  • I'm trying to make a library that I write Vista compatible.  UAC is  proving a headache because my library writes and modifies data that needs to be shared across all users.  So my writes and modifications cannot be virtualized.  However, since my library is not a stand alone app itself, I can't specify that virtualization be disabled for the whole app.

    So . . . what I need is to request elevation credentials of the user on the fly when my library reaches the point of needing to do a file write.  From my reading, the cleanest way to do this seemed to be to isolate my writes into a COM object and then use the Elevated COM Moniker techinque.  My COM knowledge is spotty, but this didn't seem to complicated a project, so I've given it a go.

    With regard to just getting the writes moved and getting the object working, all is fine.  But I am stuck with how to get the elevation portion to run correctly.  Specifically, when I return from CoGetClassobject, i always get an error of "0x80080017 The class is not configured to support Elevated activation."  But I'm at a loss for how to do this configuration properly.

    I note the MSDN class indicates the class must be firutred to run as Activation as Activator.  I added a "RunAs" subkey to my object's CLSID key in the registry.  I set the value to "Launching User" or to "Acivate as Activator" and I have verified the subkey is indeed present after registering the object with regsvr32.  But I still always get the same error.

    Vista build 5384

    As I said, my knowledge of COM is admittedly spotty to begin with.  So I know it's entirely possible (likely, even?) that I'm doing some very elemental COM 101 thing incorrectly.

    The code, after I LoadLibrary my DLL, looks like:

    _errorCode = CoInitialize(NULL);

    if (FAILED(_errorCode))

    {

        return;

    }

     

    hr = StringCchPrintf(monikerName,

    RTL_NUMBER_OF(monikerName),

    L"Elevation:Administrator!new:%s",

    szCLSID);

    // monikerName now correctly set to "Elevation:Administrator!new:{<<My GUID>>}"

     

    BIND_OPTS2 bo;

    ::ZeroMemory(&bo, sizeof(BIND_OPTS2));

    bo.cbStruct = sizeof(BIND_OPTS2);

    bo.dwClassContext = CLSCTX_LOCAL_SERVER;

     

    _errorCode = CoGetObject(monikerName, &bo, _uuidof(IMyComObjectr), (void **) &_pMyObj);

     

    if ( FAILED(_errorCode) )

    {

        // Always fails here:  0x80080017 the class is not configured to support Elevated activation.

        return;

    }

    Do the symptoms described above bring to mind any possibilityes among those of you who have managed to do this successfully?

     

    - Michael

    Thursday, December 14, 2006 3:18 AM

Answers

  • Hello michaelmwelch,

    I am in the same boat as you, my COM is at best extremely basic (in fact you're doing better than me by just writing the code ). My question however is regarding virtualization, if the data you are writing is public accross all users then why not write the data to C:\Users\Public. No virtualization will take place and all users can access this directory and as a bonus your app is still running within a standard user context .

     

    Thanks!

    Matthew Braun

    Thursday, December 14, 2006 7:44 PM
  • The C:\Users\Public directory is the best place to put public data to be shared across all users. C:\ProgramData is the location for shared application data, please see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=654928&SiteID=1.

    Thanks!

    Matthew Braun

    Wednesday, December 27, 2006 8:02 PM

All replies

  • Hello michaelmwelch,

    I am in the same boat as you, my COM is at best extremely basic (in fact you're doing better than me by just writing the code ). My question however is regarding virtualization, if the data you are writing is public accross all users then why not write the data to C:\Users\Public. No virtualization will take place and all users can access this directory and as a bonus your app is still running within a standard user context .

     

    Thanks!

    Matthew Braun

    Thursday, December 14, 2006 7:44 PM
  • Mostly because that's not the best place for it given the nature of the data.  \PrograrmData (formerly \Documents and Settings\All Users\Application Data\ in pre-Vista versions of Windows) is really the best place for it.  Putting it some place non-virtualized is certainly an option if I can't get this to work.  But understandably, I'd much rather just get this to work so I can keep it where it is supposed to be.

    I mean I'm sure someone out there has gotten this working, so there has to be a way to do it. 

    I'm told that the error I'm getting is CO_E_ELEVATION_DISABLED, which supposedly indicates that the Elevation\Enabled key is not present or not populated.  I've verfied in regedit, though, that mine is present and indeed set to a value of 1.  But I still get this same error every time.


    - Michael

    Thursday, December 14, 2006 10:58 PM
  • The C:\Users\Public directory is the best place to put public data to be shared across all users. C:\ProgramData is the location for shared application data, please see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=654928&SiteID=1.

    Thanks!

    Matthew Braun

    Wednesday, December 27, 2006 8:02 PM
  • Hi Michael,

     

    Did you ever figure out why you were getting CO_E_ELEVATION_DISABLED message?  I also have Elevation Enabled set to 1 in the registry and still getting this error.  Any ideas?

     

    Thank you.

     

    Monday, October 13, 2008 9:26 PM
  • I've been working on this issue as well.

    As near as I can tell; there are 3 registry related updates that must exist for your COM library to work with UAC.

    1) HKLM\Software\Classes\AppID\YourDLL.DLL\AppID = "GUID"  <-- APPID in ATL/COM projects.  See the generated file DLLMain.h for this GUID
    2) HKLM\Software\Classes\AppID\{APPID GUID}\AccessPermission = <Binary> <- Over the Shoulder elevation
        HKLM\Software\Classes\AppID\{APPID GUID}\DllSurrogate = "" <- Allow this thing to be hosted in svchost.exe
    3) HKLM\Software\Classes\CLSID\{APPID GUID}\(Default) = "MyObject".  (Second field in program id. eg: mylib.lib.1)
        HKLM\Software\Classes\CLSID\{APPID GUID}\AppID = {APPID GUID} <- seems redundant but everyone says it is necessary
        HKLM\Software\Classes\CLSID\{APPID GUID}\LocalizedString = "@<path to dll>,-101"  <- 101 must be a string resource in DLL
        HKLM\Software\Classes\CLSID\{APPID GUID}\Elevation\Enabled = 1 <- dword

    I am still struggling to get my class to elevate but I am most sure that these registry entries need to be made.  If you are an expert with the .RGS files that are generated during ATL/COM project creation you can add the above information in those files.  Myself, I just added extra code to DllRegisterServer to add these keys and values using the registry api.  It makes more sense to me.

    Here is my extra code, which shows how AccessPermission is created programmatically.  It uses the scary ACL *** that M$ forced onto us.  Anyway, I lifted the code from MSDN so don't credit or fail me for posting....


    Code in the client to create an admin COM object

    #import "YourDLL.dll"
    #include "YourDLL_i.c" <-- CLSID and IID for the CoCreate stuff

    HRESULT 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])); 
        HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
        if (FAILED(hr))
            return hr;
        memset(&bo, 0, sizeof(bo));
        bo.cbStruct = sizeof(bo);
        bo.hwnd = hwnd;
        bo.dwClassContext = CLSCTX_LOCAL_SERVER;
        return CoGetObject(wszMonikerName, &bo, riid, ppv);
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
    CoInitialize(NULL);
    IDispatch * pv = NULL;
    HRESULT hr = CoCreateInstanceAsAdmin(NULL,CLSID_xxx,IID_xxx,(void **)&pv);
    if (pv)
    {
    pv->Release();
    pv = NULL;
    }
    CoUninitialize();
    return 0;
    }

    Server code to add AccessPermission

    // Over the shoulder elevation support
    BOOL GetAccessPermissionsForLUAServer(SECURITY_DESCRIPTOR **ppSD)
    {
    // Local call permissions to IU, SY
        LPWSTR lpszSDDL = L"O:BAG:BADSadA;;0x3;;;IU)(A;;0x3;;;SY)";
        SECURITY_DESCRIPTOR *pSD;
        *ppSD = NULL;

        if (ConvertStringSecurityDescriptorToSecurityDescriptorW(lpszSDDL, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR *)&pSD, NULL))
        {
            *ppSD = pSD;
            return TRUE;
        }

        return FALSE;
    }

    // The following code example shows how to call CoInitializeSecurity implicitly with the SD from the previous code example:
    BOOL SetAccessPermissions(HKEY hKey,SECURITY_DESCRIPTOR * pSD)
    {
        BOOL bResult = FALSE;
        DWORD dwLen = GetSecurityDescriptorLength(pSD);
        LONG lResult;
        lResult = RegSetValueExA(hKey, 
            "AccessPermission",
            0,
            REG_BINARY,
            (BYTE*)pSD,
            dwLen);
        if (lResult != ERROR_SUCCESS) goto done;
        bResult = TRUE;
    done:
        return bResult;
    }

    // DllRegisterServer - Adds entries to the system registry
    STDAPI DllRegisterServer(void)
    {
        // registers object, typelib and all interfaces in typelib
        HRESULT hr = _AtlModule.DllRegisterServer();

    SECURITY_DESCRIPTOR * pSD = NULL;
    if (GetAccessPermissionsForLUAServer(&pSD))
    {
    // Main means to get path using registry
    HKEY hKey = (HKEY)INVALID_HANDLE_VALUE;
          // replace with your GUID...
    const wchar_t * szKey = L"SOFTWARE\\Classes\\AppID\\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}";
    DWORD dwDisposition = REG_OPENED_EXISTING_KEY;
    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,szKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwDisposition) == ERROR_SUCCESS)
    SetAccessPermissions(hKey,pSD);
    }

    return hr;
    }


    Thursday, November 13, 2008 5:41 PM
  • Here is a hint that may help someone else.

    I had this same issue of getting CO_E_ELEVATION_DISABLED.  I was building under Windows XP using Visual Studio 2008 with the Windows SDK version 6.0A.  I upgraded to Windows SDK version 6.1 (Windows SDK for Windows Server 2008 and .NET Framework 3.5) and rebuilt my project.  I no longer recieved the error and UAC worked correctly.
    Wednesday, June 17, 2009 5:10 PM
  • Is this elevation moniker works on XP?
    I though only Vista and above has UAC.


    C--s
    Thursday, July 2, 2009 7:53 PM
  • I build an application in XP that uses UAC when the application is running on Vista.
    Friday, July 3, 2009 8:04 PM