<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>this.Pose() as Expert - WCF</title>
    <link>http://chrison.net/</link>
    <description />
    <language>en-us</language>
    <copyright>Christoph Wille</copyright>
    <lastBuildDate>Sun, 04 Feb 2007 21:23:45 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.3.9074.18820</generator>
    <managingEditor>christoph.wille@gmail.com</managingEditor>
    <webMaster>christoph.wille@gmail.com</webMaster>
    <item>
      <trackback:ping>http://chrison.net/Trackback.aspx?guid=a860d58a-d4c5-4073-9fee-b3e5fab629bf</trackback:ping>
      <pingback:server>http://chrison.net/pingback.aspx</pingback:server>
      <pingback:target>http://chrison.net/PermaLink,guid,a860d58a-d4c5-4073-9fee-b3e5fab629bf.aspx</pingback:target>
      <dc:creator>Christoph Wille</dc:creator>
      <wfw:comment>http://chrison.net/CommentView,guid,a860d58a-d4c5-4073-9fee-b3e5fab629bf.aspx</wfw:comment>
      <wfw:commentRss>http://chrison.net/SyndicationService.asmx/GetEntryCommentsRss?guid=a860d58a-d4c5-4073-9fee-b3e5fab629bf</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
In the blog post <a href="http://chrison.net/UACElevationInManagedCodeStartingElevatedProcesses.aspx">UAC
Elevation in Managed Code: Starting Elevated Processes</a> 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.
</p>
        <p>
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.
</p>
        <p>
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.
</p>
        <p>
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 <a href="http://dotnet.org.za/armand/archive/2006/06/14/53390.aspx">here</a> and <a href="http://blogs.msdn.com/drnick/archive/2006/10/16/configuring-http-for-windows-vista.aspx">here</a>),
and the TCP channel would pop up a firewall warning. That's why.
</p>
        <p>
Let's look at the applications - first the non-elevated one:
</p>
        <p>
          <img src="http://chrison.net/content/binary/uacwcfpoc1.png" border="0" />
        </p>
        <p>
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:
</p>
        <p>
          <img src="http://chrison.net/content/binary/uacwcfpoc3.png" border="0" />
        </p>
        <p>
Solution-wise, this simple two-application scenario is split into four projects:
</p>
        <p>
          <img src="http://chrison.net/content/binary/uacwcfpoc2.png" border="0" />
        </p>
        <p>
So where do we start? With the easy part inside ElevationContract:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">[ServiceContract(Namespace <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"http://Christoph.Wille.Samples"</span>,<br />
CallbackContract <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">typeof</span>(IElevatedProcess))]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">interface</span> IWaitForElevatedProcess<br />
{<br />
  [OperationContract(IsOneWay <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">false</span>)]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
void</span> ElevatedProcessStarted();<br />
}<br /><br />
[ServiceContract(Namespace <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"http://Christoph.Wille.Samples"</span>)]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">interface</span> IElevatedProcess<br />
{<br />
  [OperationContract(IsOneWay <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">false</span>)]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
void</span> SayHello(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> message);<br />
}</span>
        </p>
        <p>
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.
</p>
        <p>
So how is the service endpoint intialized - let's take a look inside:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">private</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">const</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> theProcess <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">@"..\..\..\ElevatedProcess\bin\Debug\ElevatedProcess.exe"</span>;<br /><br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">private</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> tryitButton_Click(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">object</span> sender,
EventArgs e)<br />
{<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
string</span> channelIdentifier <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> MiscHelpers.CreateRandomString(64);<br />
  MyUACServiceHost.StartService(channelIdentifier);<br /><br /><span style="FONT-SIZE: 11px; COLOR: green; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
// starting it modal doesn't work (obviously - unless we have more threads, of course)</span><br />
  ElevatedProcess.Start(theProcess, channelIdentifier);<br />
}</span>
        </p>
        <p>
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:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">internal</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">static</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> StartService(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> pipeEndPoint)<br />
{<br />
  NetNamedPipeBinding binding <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> NetNamedPipeBinding();<br />
  binding.Name <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"uacbinding"</span>;<br />
  binding.Security.Mode <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> NetNamedPipeSecurityMode.Transport;<br /><br />
  Uri baseAddress <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> Uri(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"net.pipe://localhost/uac/"</span><span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> pipeEndPoint);<br /><br />
  myServiceHost <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> ServiceHost(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">typeof</span>(SampleService),
baseAddress);<br />
  myServiceHost.AddServiceEndpoint(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">typeof</span>(IWaitForElevatedProcess),
binding, baseAddress);<br />
  myServiceHost.Open();<br />
}</span>
        </p>
        <p>
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.
</p>
        <p>
Now let's switch to the console application's Main method:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">static</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> Main(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span>[]
args)<br />
{<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
if</span> (args.Length !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> 1)<br />
  {<br />
    Console.WriteLine(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"One
argument expected - the channel identifier"</span>);<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">  
 return</span>;<br />
  } 
<br /><br />
  NetNamedPipeBinding binding <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> NetNamedPipeBinding();<br />
  binding.Name <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"uacbinding"</span>;<br />
  binding.Security.Mode <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> NetNamedPipeSecurityMode.Transport;<br /><br />
  String url <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"net.pipe://localhost/uac/"</span><span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> args[0];<br />
  EndpointAddress address <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> EndpointAddress(url);<br /><br />
  WaitForElevatedProcess client <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> WaitForElevatedProcess(<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">     
new</span> InstanceContext(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">new</span> SampleCallback()),<br />
      binding,<br />
      address);<br /><br />
  client.ElevatedProcessStarted();<br /><br />
  Console.WriteLine(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"The
elevated process is now ready"</span>);<br />
  Console.ReadLine();<br /><br />
  client.Close();<br />
}</span>
        </p>
        <p>
Similar to normal client WCF code, however, with the duplex twist hidden inside WaitForElevatedProcess:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">class</span> WaitForElevatedProcess
: DuplexClientBase&lt;IWaitForElevatedProcess&gt;, IWaitForElevatedProcess<br />
{<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
public</span> WaitForElevatedProcess(System.ServiceModel.InstanceContext callbackInstance,</span>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> <br />
    System.ServiceModel.Channels.Binding binding, 
<br />
    System.ServiceModel.EndpointAddress remoteAddress)<br />
       : <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">base</span>(callbackInstance,
binding, remoteAddress)<br />
  {<br />
  }<br /><br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> ElevatedProcessStarted()<br />
  {<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
base</span>.Channel.ElevatedProcessStarted();<br />
  }<br />
}</span>
        </p>
        <p>
Once the channel is connected, this elevated process calls back into the service piece
which lives in the non-elevated application, namely SampleService:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">[ServiceBehavior(ConcurrencyMode <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> ConcurrencyMode.Reentrant, 
<br />
      InstanceContextMode <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> InstanceContextMode.PerSession)]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">class</span> SampleService
: IWaitForElevatedProcess<br />
{<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> ElevatedProcessStarted()<br />
  {<br />
    OperationContext.Current.GetCallbackChannel&lt;IElevatedProcess&gt;().SayHello(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"Chris"</span>);<br />
  }<br />
}</span>
        </p>
        <p>
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 ;-) 
</p>
        <p>
Speaking of talking, I owe you the code for the callee in the console application:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">[CallbackBehavior(ConcurrencyMode <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> ConcurrencyMode.Reentrant)]<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">class</span> SampleCallback
: IElevatedProcess<br />
{<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"> 
public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">void</span> SayHello(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">string</span> message)<br />
  {<br />
    Console.WriteLine(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"Hello
world "</span><span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">+</span> message);<br />
  }<br />
}</span>
        </p>
        <p>
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, ...).
</p>
        <p>
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 <strong>unless</strong> you start the service on a separate thread.
For simplicity reasons I didn't do this for the POC.
</p>
        <p>
          <a href="http://chrison.net/content/binary/ElevateProcessTalkWCF.zip">ElevateProcessTalkWCF.zip
(27 KB)</a>
        </p>
        <p>
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.
</p>
        <img width="0" height="0" src="http://chrison.net/aggbug.ashx?id=a860d58a-d4c5-4073-9fee-b3e5fab629bf" />
      </body>
      <title>UAC Elevation in Managed Code: "Talking" to an Elevated Process via WCF</title>
      <guid isPermaLink="false">http://chrison.net/PermaLink,guid,a860d58a-d4c5-4073-9fee-b3e5fab629bf.aspx</guid>
      <link>http://chrison.net/UACElevationInManagedCodeTalkingToAnElevatedProcessViaWCF.aspx</link>
      <pubDate>Sun, 04 Feb 2007 21:23:45 GMT</pubDate>
      <description>&lt;p&gt;
In the blog post &lt;a href="http://chrison.net/UACElevationInManagedCodeStartingElevatedProcesses.aspx"&gt;UAC
Elevation in Managed Code: Starting Elevated Processes&lt;/a&gt; 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.
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;p&gt;
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 &lt;a href="http://dotnet.org.za/armand/archive/2006/06/14/53390.aspx"&gt;here&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/drnick/archive/2006/10/16/configuring-http-for-windows-vista.aspx"&gt;here&lt;/a&gt;),
and the TCP channel would pop up a firewall warning. That's why.
&lt;/p&gt;
&lt;p&gt;
Let's look at the applications - first the non-elevated one:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://chrison.net/content/binary/uacwcfpoc1.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
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:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://chrison.net/content/binary/uacwcfpoc3.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
Solution-wise, this simple two-application scenario is split into four projects:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://chrison.net/content/binary/uacwcfpoc2.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
So where do we start? With the easy part inside ElevationContract:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;[ServiceContract(Namespace &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"http://Christoph.Wille.Samples"&lt;/span&gt;,&lt;br&gt;
CallbackContract &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;typeof&lt;/span&gt;(IElevatedProcess))]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;interface&lt;/span&gt; IWaitForElevatedProcess&lt;br&gt;
{&lt;br&gt;
&amp;nbsp; [OperationContract(IsOneWay &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;false&lt;/span&gt;)]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
void&lt;/span&gt; ElevatedProcessStarted();&lt;br&gt;
}&lt;br&gt;
&lt;br&gt;
[ServiceContract(Namespace &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"http://Christoph.Wille.Samples"&lt;/span&gt;)]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;interface&lt;/span&gt; IElevatedProcess&lt;br&gt;
{&lt;br&gt;
&amp;nbsp; [OperationContract(IsOneWay &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;false&lt;/span&gt;)]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
void&lt;/span&gt; SayHello(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;string&lt;/span&gt; message);&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;p&gt;
So how is the service endpoint intialized - let's take a look inside:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;private&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;const&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;string&lt;/span&gt; theProcess &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;@"..\..\..\ElevatedProcess\bin\Debug\ElevatedProcess.exe"&lt;/span&gt;;&lt;br&gt;
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;private&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; tryitButton_Click(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;object&lt;/span&gt; sender,
EventArgs e)&lt;br&gt;
{&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
string&lt;/span&gt; channelIdentifier &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; MiscHelpers.CreateRandomString(64);&lt;br&gt;
&amp;nbsp; MyUACServiceHost.StartService(channelIdentifier);&lt;br&gt;
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: green; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
// starting it modal doesn't work (obviously - unless we have more threads, of course)&lt;/span&gt;
&lt;br&gt;
&amp;nbsp; ElevatedProcess.Start(theProcess, channelIdentifier);&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
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:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;internal&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;static&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; StartService(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;string&lt;/span&gt; pipeEndPoint)&lt;br&gt;
{&lt;br&gt;
&amp;nbsp; NetNamedPipeBinding binding &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; NetNamedPipeBinding();&lt;br&gt;
&amp;nbsp; binding.Name &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"uacbinding"&lt;/span&gt;;&lt;br&gt;
&amp;nbsp; binding.Security.Mode &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; NetNamedPipeSecurityMode.Transport;&lt;br&gt;
&lt;br&gt;
&amp;nbsp; Uri baseAddress &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; Uri(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"net.pipe://localhost/uac/"&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;+&lt;/span&gt; pipeEndPoint);&lt;br&gt;
&lt;br&gt;
&amp;nbsp; myServiceHost &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; ServiceHost(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;typeof&lt;/span&gt;(SampleService),
baseAddress);&lt;br&gt;
&amp;nbsp; myServiceHost.AddServiceEndpoint(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;typeof&lt;/span&gt;(IWaitForElevatedProcess),
binding, baseAddress);&lt;br&gt;
&amp;nbsp; myServiceHost.Open();&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;p&gt;
Now let's switch to the console application's Main method:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;static&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; Main(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;string&lt;/span&gt;[]
args)&lt;br&gt;
{&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
if&lt;/span&gt; (args.Length !&lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; 1)&lt;br&gt;
&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"One
argument expected - the channel identifier"&lt;/span&gt;);&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;
&amp;nbsp;return&lt;/span&gt;;&lt;br&gt;
&amp;nbsp; } 
&lt;br&gt;
&lt;br&gt;
&amp;nbsp; NetNamedPipeBinding binding &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; NetNamedPipeBinding();&lt;br&gt;
&amp;nbsp; binding.Name &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"uacbinding"&lt;/span&gt;;&lt;br&gt;
&amp;nbsp; binding.Security.Mode &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; NetNamedPipeSecurityMode.Transport;&lt;br&gt;
&lt;br&gt;
&amp;nbsp; String url &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"net.pipe://localhost/uac/"&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;+&lt;/span&gt; args[0];&lt;br&gt;
&amp;nbsp; EndpointAddress address &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; EndpointAddress(url);&lt;br&gt;
&lt;br&gt;
&amp;nbsp; WaitForElevatedProcess client &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; WaitForElevatedProcess(&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
new&lt;/span&gt; InstanceContext(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;new&lt;/span&gt; SampleCallback()),&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; binding,&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; address);&lt;br&gt;
&lt;br&gt;
&amp;nbsp; client.ElevatedProcessStarted();&lt;br&gt;
&lt;br&gt;
&amp;nbsp; Console.WriteLine(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"The
elevated process is now ready"&lt;/span&gt;);&lt;br&gt;
&amp;nbsp; Console.ReadLine();&lt;br&gt;
&lt;br&gt;
&amp;nbsp; client.Close();&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
Similar to normal client WCF code, however, with the duplex twist hidden inside WaitForElevatedProcess:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;class&lt;/span&gt; WaitForElevatedProcess
: DuplexClientBase&amp;lt;IWaitForElevatedProcess&amp;gt;, IWaitForElevatedProcess&lt;br&gt;
{&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
public&lt;/span&gt; WaitForElevatedProcess(System.ServiceModel.InstanceContext callbackInstance,&lt;/span&gt;&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceModel.Channels.Binding binding, 
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceModel.EndpointAddress remoteAddress)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;base&lt;/span&gt;(callbackInstance,
binding, remoteAddress)&lt;br&gt;
&amp;nbsp; {&lt;br&gt;
&amp;nbsp; }&lt;br&gt;
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; ElevatedProcessStarted()&lt;br&gt;
&amp;nbsp; {&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
base&lt;/span&gt;.Channel.ElevatedProcessStarted();&lt;br&gt;
&amp;nbsp; }&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
Once the channel is connected, this elevated process calls back into the service piece
which lives in the non-elevated application, namely SampleService:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;[ServiceBehavior(ConcurrencyMode &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; ConcurrencyMode.Reentrant, 
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InstanceContextMode &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; InstanceContextMode.PerSession)]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;class&lt;/span&gt; SampleService
: IWaitForElevatedProcess&lt;br&gt;
{&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; ElevatedProcessStarted()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; OperationContext.Current.GetCallbackChannel&amp;lt;IElevatedProcess&amp;gt;().SayHello(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"Chris"&lt;/span&gt;);&lt;br&gt;
&amp;nbsp; }&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
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 ;-) 
&lt;/p&gt;
&lt;p&gt;
Speaking of talking, I owe you the code for the callee in the console application:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;[CallbackBehavior(ConcurrencyMode &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; ConcurrencyMode.Reentrant)]&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;class&lt;/span&gt; SampleCallback
: IElevatedProcess&lt;br&gt;
{&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;
public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;void&lt;/span&gt; SayHello(&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;string&lt;/span&gt; message)&lt;br&gt;
&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"Hello
world "&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;+&lt;/span&gt; message);&lt;br&gt;
&amp;nbsp; }&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
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, ...).
&lt;/p&gt;
&lt;p&gt;
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 &lt;strong&gt;unless&lt;/strong&gt; you start the service on a separate thread.
For simplicity reasons I didn't do this for the POC.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://chrison.net/content/binary/ElevateProcessTalkWCF.zip"&gt;ElevateProcessTalkWCF.zip
(27 KB)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://chrison.net/aggbug.ashx?id=a860d58a-d4c5-4073-9fee-b3e5fab629bf" /&gt;</description>
      <comments>http://chrison.net/CommentView,guid,a860d58a-d4c5-4073-9fee-b3e5fab629bf.aspx</comments>
      <category>.NET</category>
      <category>3.0</category>
      <category>Security</category>
      <category>UAC</category>
      <category>Vista</category>
      <category>WCF</category>
    </item>
    <item>
      <trackback:ping>http://chrison.net/Trackback.aspx?guid=a0a62372-1488-4d98-b714-173ce90996a4</trackback:ping>
      <pingback:server>http://chrison.net/pingback.aspx</pingback:server>
      <pingback:target>http://chrison.net/PermaLink,guid,a0a62372-1488-4d98-b714-173ce90996a4.aspx</pingback:target>
      <dc:creator>Christoph Wille</dc:creator>
      <wfw:comment>http://chrison.net/CommentView,guid,a0a62372-1488-4d98-b714-173ce90996a4.aspx</wfw:comment>
      <wfw:commentRss>http://chrison.net/SyndicationService.asmx/GetEntryCommentsRss?guid=a0a62372-1488-4d98-b714-173ce90996a4</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
This Q&amp;A item is part of the <a href="http://msdn.microsoft.com/msdnmag/issues/06/11/SecurityBriefs/default.aspx">current
MSDN magazine's Security Brief's column</a> by <a href="http://www.pluralsight.com/blogs/keith/default.aspx">Keith
Brown</a>. I am pretty sure that this problem will rear its head sooner or later on
every developers machine, that's why I am 'pinning' the link in my blog for my own
reference too.
</p>
        <img width="0" height="0" src="http://chrison.net/aggbug.ashx?id=a0a62372-1488-4d98-b714-173ce90996a4" />
      </body>
      <title>Security Brief: Why won't my simple WCF service start when I run it as a non-administrator?</title>
      <guid isPermaLink="false">http://chrison.net/PermaLink,guid,a0a62372-1488-4d98-b714-173ce90996a4.aspx</guid>
      <link>http://chrison.net/SecurityBriefWhyWontMySimpleWCFServiceStartWhenIRunItAsANonadministrator.aspx</link>
      <pubDate>Sun, 12 Nov 2006 15:41:26 GMT</pubDate>
      <description>&lt;p&gt;
This Q&amp;amp;A item is part of the &lt;a href="http://msdn.microsoft.com/msdnmag/issues/06/11/SecurityBriefs/default.aspx"&gt;current
MSDN magazine's Security Brief's column&lt;/a&gt; by &lt;a href="http://www.pluralsight.com/blogs/keith/default.aspx"&gt;Keith
Brown&lt;/a&gt;. I am pretty sure that this problem will rear its head sooner or later on
every developers machine, that's why I am 'pinning' the link in my blog for my own
reference too.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://chrison.net/aggbug.ashx?id=a0a62372-1488-4d98-b714-173ce90996a4" /&gt;</description>
      <comments>http://chrison.net/CommentView,guid,a0a62372-1488-4d98-b714-173ce90996a4.aspx</comments>
      <category>.NET</category>
      <category>IIS</category>
      <category>Security</category>
      <category>3.0</category>
      <category>WCF</category>
    </item>
  </channel>
</rss>