<2017 March>

On this page...



Member of...

ASP Insiders

MVP Visual Developer ASP/ASP.NET

Enter CodeZone

Blog Categories



Deutsche Resourcen


Sign In

#  Sunday, 04 February 2007

In the blog post UAC Elevation in Managed Code: Starting Elevated Processes I talked about how to start an elevated process. However, just starting a process might not cut the mustard, for example if you need to hand over data to the elevated process. You could achieve this by passing, let's say, some data as command line arguments to ProcessInfo before starting the elevated process. But that seriously limits communication.

So how can you perform communication with an elevated process? My first idea was to use .NET Remoting. Once I thought through the multi-instance scenario, I quickly realized that this meant the server had to be running in the non-elevated application, because only it could properly choose a port. And because I am not a fan of Remoting anyways, I decided to give WCF (Windows Communication Foundation, a pillar of .NET 3.0) a try.

It looked like smooth sailing at first, but then I realized that with WCF too I needed to implement the service inside the non-elevated application. This time, however, the reason was "How do I know when the elevated application has initialized before I can actually start communicating with it?". Back to the drawing board.

The final solution now looks like this: the non-elevated application starts a service. The operations contract specifies a callback, which, once the elevated application has signalled its readiness, can be used by the non-elevated application to "talk" with the elevated application. I didn't intend to go duplex, but hey, if there's no other way I am willing to take plunge. Speaking of tricks of the trade: I am using imperative binding to a named pipe. Reason? Well, WS bindings won't work (see here and here), and the TCP channel would pop up a firewall warning. That's why.

Let's look at the applications - first the non-elevated one:

This time I forfeited eye candy (the shield button). Same (missing eye candy) goes for the elevated application as it is a console application only:

Solution-wise, this simple two-application scenario is split into four projects:

So where do we start? With the easy part inside ElevationContract:

[ServiceContract(Namespace = "http://Christoph.Wille.Samples",
CallbackContract = typeof(IElevatedProcess))]
public interface IWaitForElevatedProcess
  [OperationContract(IsOneWay = false)]
  void ElevatedProcessStarted();

[ServiceContract(Namespace = "http://Christoph.Wille.Samples")]
public interface IElevatedProcess
  [OperationContract(IsOneWay = false)]
  void SayHello(string message);

The interface IWaitForElevatedProcess is implemented in StandardUserApp. It is the service endpoint that is initialized before the elevated process is started - and once the elevated application is up and running, it calls into ElevatedProcessStarted. And we are in business for using the IElevatedProcess callback that is implemented in the ElevatedProcess console application.

So how is the service endpoint intialized - let's take a look inside:

private const string theProcess = @"..\..\..\ElevatedProcess\bin\Debug\ElevatedProcess.exe";

private void tryitButton_Click(object sender, EventArgs e)
  string channelIdentifier = MiscHelpers.CreateRandomString(64);

  // starting it modal doesn't work (obviously - unless we have more threads, of course)
  ElevatedProcess.Start(theProcess, channelIdentifier);

Interesting tidbit #1 is CreateRandomString: it creates a random string to use for the address. Why? Well, if multiple instances of our application are running and trying to elevate a process, we are in trouble. Which brings me to StartService:

internal static void StartService(string pipeEndPoint)
  NetNamedPipeBinding binding = new NetNamedPipeBinding();
  binding.Name = "uacbinding";
  binding.Security.Mode = NetNamedPipeSecurityMode.Transport;

  Uri baseAddress = new Uri("net.pipe://localhost/uac/" + pipeEndPoint);

  myServiceHost = new ServiceHost(typeof(SampleService), baseAddress);
  myServiceHost.AddServiceEndpoint(typeof(IWaitForElevatedProcess), binding, baseAddress);

As I said before, I am doing it imperatively (no configuration in app.config necessary). That's all there is to getting the service up and running.

Now let's switch to the console application's Main method:

static void Main(string[] args)
  if (args.Length != 1)
    Console.WriteLine("One argument expected - the channel identifier");

  NetNamedPipeBinding binding = new NetNamedPipeBinding();
  binding.Name = "uacbinding";
  binding.Security.Mode = NetNamedPipeSecurityMode.Transport;

  String url = "net.pipe://localhost/uac/" + args[0];
  EndpointAddress address = new EndpointAddress(url);

  WaitForElevatedProcess client = new WaitForElevatedProcess(
      new InstanceContext(new SampleCallback()),


  Console.WriteLine("The elevated process is now ready");


Similar to normal client WCF code, however, with the duplex twist hidden inside WaitForElevatedProcess:

public class WaitForElevatedProcess : DuplexClientBase<IWaitForElevatedProcess>, IWaitForElevatedProcess
  public WaitForElevatedProcess(System.ServiceModel.InstanceContext callbackInstance,
    System.ServiceModel.Channels.Binding binding,
    System.ServiceModel.EndpointAddress remoteAddress)
       : base(callbackInstance, binding, remoteAddress)

  public void ElevatedProcessStarted()

Once the channel is connected, this elevated process calls back into the service piece which lives in the non-elevated application, namely SampleService:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,
      InstanceContextMode = InstanceContextMode.PerSession)]
public class SampleService : IWaitForElevatedProcess
  public void ElevatedProcessStarted()

This method is the workhorse where I can talk to the elevated process - if only my callback interface had more as well as more serious methods ;-)

Speaking of talking, I owe you the code for the callee in the console application:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class SampleCallback : IElevatedProcess
  public void SayHello(string message)
    Console.WriteLine("Hello world " + message);

That's it - to recap: first, we initialize the WCF service. Then elevate a process. This process, once initialized, calls into our service and leaves a callback. And then we are in business talking to the elevated process (setting data, being notified when the elevated application quits and why, ...).

Sample warnings before you download: MyUACServiceHost definitely should be instance instead of static. And, more restricting - starting the elevated process modal won't allow communication unless you start the service on a separate thread. For simplicity reasons I didn't do this for the POC.

ElevateProcessTalkWCF.zip (27 KB)

Before concluding I wanted to add a few words: my ideal implementation for UAC would be COM elevation. That way, one can put more than one component into a single DLL, and still get a meaningful UAC prompt thanks to the LocalizedString registry key - which is per component, and not per executable (which is the case for this solution if you add multiple actions). If you need differing prompts for each administrative action, there is only one course of action you can take with processes: create multiple executables. Not very pretty, but I failed with writing an elevatable (not a word, I am sure) managed (C#) COM component.

Categories: .NET | 3.0 | Security | UAC | Vista | WCF
Sunday, 04 February 2007 22:23:45 (W. Europe Standard Time, UTC+01:00)  #    Comments [3]


Sunday, 04 February 2007 23:00:57 (W. Europe Standard Time, UTC+01:00)
Good article! Why not putting it on Codeproject?
Monday, 05 February 2007 15:45:53 (W. Europe Standard Time, UTC+01:00)
Using TCP sockets might be simpler:

- the non-elevated process chooses a random port and opens a socket localhost:<port>

- the elevated process will receive the port number through its command-line

- when the elevated process has initialised it will make a connection to localhost:<port>

- both process can communicate using the established connection

I would avoid using a service. Spyware detection software will find it malicious behaviour if desktop software tries to install a custom system service while running.
Monday, 05 February 2007 16:48:03 (W. Europe Standard Time, UTC+01:00)
Sockets are definitely lighter-weight (and more error prone especially if you want to do message-based communication), but the Vista firewall will kick in, so it is a no-go .
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.