The previous installment UAC Elevation in Managed Code: Starting Elevated Processes dealt with starting executables with the "real" administrative token. In this blog post, we deal with starting a COM component with elevated privileges. For in-depth background information, please consult Kenny Kerr's absolutely excellent post on Windows Vista for Developers – Part 4 – User Account Control.
To start with, we need a COM component. Instead of writing an ATL C++ COM component from scratch, I took the MyElevateCom sample from CoCreateInstanceAsAdmin or CreateElevatedComObject sample from the Vista Compatibility Team Blog. Note that for building it, check out my post Visual Studio on Vista: Not so Fast!
Assuming that you built and successfully registered the COM component (it is built to the instuctions from Kenny's post), you can go about and write the managed caller. First, we need a reference to the component:
Then comes the tricky part - actually instantiating the COM component. When you take a look at the C++ example, you see that quite some "moniker magic" is involved that cannot be replicated by simply newing up the component. So how to mimic this behavior in managed code? The Microsoft® Windows® Software Development Kit for Windows Vista™ and .NET Framework 3.0 Runtime Components comes to the rescue: inside, you find C:\Program Files\Microsoft SDKs\Windows\v6.0\Samples\CrossTechnologySamples.zip, which contains the VistaBridge sample.
From that, I took the VistaBridgeLibary, and modified the static UACManager.LaunchElevatedCOMObject method a bit:
public static object LaunchElevatedCOMObject(Guid Clsid, Guid InterfaceID)
string CLSID = Clsid.ToString("B");
string monikerName = "Elevation:Administrator!new:" + CLSID;
NativeMethods.BIND_OPTS3 bo = new NativeMethods.BIND_OPTS3();
bo.cbStruct = (uint)Marshal.SizeOf(bo);
bo.hwnd = IntPtr.Zero;
bo.dwClassContext = (int)NativeMethods.CLSCTX.CLSCTX_LOCAL_SERVER;
object retVal = UnsafeNativeMethods.CoGetObject(monikerName, ref bo, InterfaceID);
Modifications: the method is now public instead of internal, and CLSCTX changed to local server (otherwise it wouldn't work).
Next, we need a UI:
This button is the CommandLinkWinForms control from VistaBridgeLibary, with the ShieldIcon property set to true.
Let's hook up the event code:
private void tryItButton_Click(object sender, EventArgs e)
Guid IID_ITheElevated =
new Guid(0x5EFC3EFB, 0xC7D3, 0x4D00, 0xB7, 0x2E, 0x2F, 0x86, 0x4A, 0x1E, 0xAD, 0x06);
Guid CLSID_TheElevated =
new Guid(0x253E7696, 0xA524, 0x4E49, 0x9E, 0x50, 0xBF, 0xCC, 0x29, 0x91, 0x31, 0x23);
object o = UACManager.LaunchElevatedCOMObject(CLSID_TheElevated, IID_ITheElevated);
ITheElevated iface = (ITheElevated)o;
// Call the method on the interface just like in the C++ example
// Release the object
The interface ID as well as class ID guids come directly from the C++ project (it is always a good idea to "speak" more than one language), but you could obtain those from the type library or registry as well if you don't have the source code of the component handy.
Object creation is handled via LaunchElevatedCOMObject, and the resultant object is cast to the interface from the imported type library. Noteable (and important) is the last line: because the object wasn't created by the runtime, we have to take care of its destruction (the created interface doesn't have a Release() method, so we use Marshal.ReleaseComObject).
That's it - your managed code is now instantiating an elevated COM object that has full reign over the system.
ElevateCOMComponentSample.zip (117.07 KB)