<2017 March>
SunMonTueWedThuFriSat
2627281234
567891011
12131415161718
19202122232425
2627282930311
2345678

On this page...

Search

Links

Member of...


ASP Insiders

MVP Visual Developer ASP/ASP.NET

Enter CodeZone

Blog Categories

Microsoft

Blogroll

Deutsche Resourcen

Management

Sign In
 

#  Monday, 05 February 2007

I admit it: UAC Elevation in Managed Code: "Talking" to an Elevated Process via WCF is a kludge. The reason why I dabbled with this approach at all is that I failed to implement COM elevation with managed code (not elevating a COM component, but the COM component itself). However, at long last, I succeeded in that respect too: I now present you the all-managed code solution to UAC elevation!

Once again I built myself a small demo frontend application:

As you can guess, the first button does plain vanilla COM InterOp without any UAC elevation. Thus its code is rather simple:

private void simpleCallButton_Click(object sender, EventArgs e)
{
  Type t = Type.GetTypeFromCLSID(new Guid("71E050A7-AF7F-42dd-BE00-BF955DDD13D4"));
  object o = Activator.CreateInstance(t);
  t.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, o, null);
}

Why this reflection magic? Well, the COM component I am calling here is implemented in .NET - and both VS as well as tlbimp balk at reimporting the exported type library.

The COM component in question has been regasm'ed & gacutil'ed (ManagedElevator project in the download). Although the name implies that I am after elevation, it is pretty much a standard COM component written using C#:

public class TheGuids
{
  public const string IHelloWorld = "B8CD5C09-9ACD-49b0-BF6F-C7B0F29795F9";
  public const string ClassToElevate = "71E050A7-AF7F-42dd-BE00-BF955DDD13D4";
  public const string AppId = "75AB90B0-8B9C-45c9-AC55-C53A9D718E1A";
}

[Guid(TheGuids.IHelloWorld)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IHelloWorld
{
  [ComVisible(true)]
  void SayHello();
}

[Guid(TheGuids.ClassToElevate)]
[ClassInterface(ClassInterfaceType.None)]
public class ClassToElevate : IHelloWorld
{
 public ClassToElevate()
 {
 }

 [ComVisible(true)]
 public void SayHello()
 {
  MessageBox.Show("Hello World");
 }
}

So how do you go from "standard" "plain-vanilla" COM component to COM elevation? The part that stumped me for so long was the ClassInterface attribute - if you forget this guy, you'll end up with an InvalidCastException thrown by UACManager.LaunchElevatedCOMObject.

But that's not quite all to get up and running with COM elevation: in addition, you need to modify the default registration for this component - specifically, you need to configure the DllSurrogate. This is where the AppId GUID comes into play: it isn't used in code (kept there for documentation purposes only), but in registryadditions.reg. It binds the various registry keys. And speaking of this .reg file, please take note of the LocalizedString value: it contains the text for the UAC prompt (also check out UACPrompts.rc, resource.h, compilerc.bat as well as the properties of the ManagedElevator project where the compiled .res file is referenced).

Note Before importing the .reg file into the registry make sure to fix the file path contained in LocalizedString! And if you create your own elevated COM component DO NOT reuse any of my three GUIDs - use guidgen.exe to create your personal ones.

From there, UAC elevation is smooth sailing. The Reflection version of COM elevation looks very similar to non-elevated calls:

private void managedElevation_Click(object sender, EventArgs e)
{
  // CLSID
  Guid classId = new Guid("71E050A7-AF7F-42dd-BE00-BF955DDD13D4");

  // Interface ID
  Guid interfaceId = new Guid("B8CD5C09-9ACD-49b0-BF6F-C7B0F29795F9");

  object o = UACManager.LaunchElevatedCOMObject(classId, interfaceId);

  Type t = o.GetType();
  t.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, o, null);

  Marshal.ReleaseComObject(o);
}

Of course this is not really a good solution (late binding). So instead I manually imported the IHelloWorld interface:

[
ComImport(),
Guid("B8CD5C09-9ACD-49b0-BF6F-C7B0F29795F9"),
InterfaceType(ComInterfaceType.InterfaceIsDual)
]
  interface IHelloWorld
  {
   [
   MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime),
   PreserveSig
   ]
    void SayHello();
  }

Which makes calls into the elevated COM object much easier and cleaner:

private void managedElevationInterface_Click(object sender, EventArgs e)
{
  Guid classId = new Guid("71E050A7-AF7F-42dd-BE00-BF955DDD13D4");
  Guid interfaceId = new Guid("B8CD5C09-9ACD-49b0-BF6F-C7B0F29795F9");

  object o = UACManager.LaunchElevatedCOMObject(classId, interfaceId);

  IHelloWorld ihw = (IHelloWorld)o;
  ihw.SayHello();

  Marshal.ReleaseComObject(o);
}

So why should you use the COM elevation solution instead of starting the process? Well, there are a couple of reasons:

  • You can package more than one component into a DLL and still have custom UAC prompts thanks to LocalizedString
  • Your users don't get "an unidentified program..." warnings. Thank you COM registration
  • If you ever need to talk more extensively with the elevated process then this approach can be adapted more easily

The source code

ConsumeMyElevatedCOM.zip (97.56 KB)

You will find a file aptly named notes.txt in the ManagedElevator project that describes all the necessary steps to get up and running.

I hope you find this sample useful and not have to spend as much time as I did. Cheers!

Categories: .NET | Security | UAC | Vista
Monday, 05 February 2007 22:41:46 (W. Europe Standard Time, UTC+01:00)  #    Comments [7]

 



Sunday, 11 February 2007 23:50:55 (W. Europe Standard Time, UTC+01:00)
I tried running your sample project, but I get the following error message:

"Retieving the COM class factory for component..........
failed due to the following error: 80040154

Do you know why? I've imported the registry entries with the path to the DLL on my mahcine.

Thank you,
Tony
Monday, 12 February 2007 00:42:03 (W. Europe Standard Time, UTC+01:00)
OK, I checked off Register for COM Interop in the properties of the ManagedElevator project. That solved the 800040154 error. However, if I click the Managed Elevatation (Interface) button, I get this error:

ARGUMENT EXCEPTION
Value does not fall within the expected range.

Any clue?

Thank you,
Tony
Monday, 12 February 2007 09:32:16 (W. Europe Standard Time, UTC+01:00)
I am currently travelling, but at least I could re-verify my sample: rebuild, register.bat, adjust the path in the .reg file, import the .reg file, run the sample. That works, and this is a different machine from the one I wrote the sample on. Please check that you followed the step list precisely.
Chris
Tuesday, 13 February 2007 00:41:20 (W. Europe Standard Time, UTC+01:00)
Chris,

I tried to start from scratch and got the same results. Here are my steps:

1. Download the project

2. Change Resource file in properties of the ManagedElevator project to C:\Documents and Settings\traimo\My Documents\Visual Studio 2005\Projects\ConsumeMyElevatedCOM\ManagedElevator\UacPrompts.RES

3. Build the project

4. Run Register.bat

5. Change registry entry to "LocalizedString"="@C:\\Documents and Settings\\traimo\\My Documents\\Visual Studio 2005\\Projects\\ConsumeMyElevatedCOM\\ManagedElevator\\bin\\Debug\ManagedElevator.dll,-100"

6. Run the Sample

If I click SIMPLE CALL I get an error. However, if I select "Register for COM INTEROP" in the project properties, it works. However, I still get the error on the managed elevation buttons. I must be missing some minor detail. What do you think?
Tony
Tuesday, 13 February 2007 00:42:10 (W. Europe Standard Time, UTC+01:00)
This is the error I get when I click the Managed Elevatation (Interface) button:

ARGUMENT EXCEPTION
Value does not fall within the expected range.

The error occurs on this line of code:
object retVal = UnsafeNativeMethods.CoGetObject(monikerName, ref bo, InterfaceID);

in the LaunchElevatedCOMObject method

Please help.

Thank you,
Tony
Tuesday, 13 February 2007 05:08:00 (W. Europe Standard Time, UTC+01:00)
OK, I didn't tell you that I was running your sample on XP. So, I tried running it on VISTA and it errors out on the same line of code. However, I get this error message:

{"The activation requires a display name to be present under the CLSID key. (Exception from HRESULT: 0x80080015)"}

My LocalizedString =
@C:\Users\Tony\Documents\Visual Studio 2005\Projects\ConsumeMyElevatedCOM\ManagedElevator\bin\Debug\ManagedElevator.dll,-100

Any clue?
Tony
Tuesday, 25 September 2007 14:44:22 (W. Europe Daylight Time, UTC+02:00)
Hi,

I am creating my own based on yours and I'm getting the 0x80070002 error (the system cannot find the file specified). How did you get rid of it ?
David
Comments are closed.

© Copyright 2017 Christoph Wille

newtelligence dasBlog 2.3.9074.18820
Subscribe to this weblog's RSS feed with SharpReader, Radio Userland, NewsGator or any other aggregator listening on port 5335 by clicking this button.   RSS 2.0|Atom 1.0  Send mail to the author(s)

 
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.