<2007 February>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728123
45678910

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
 

#  Tuesday, 20 February 2007

Yesterday, Peter Koen held a talk at the .NET UG Vienna about Office Business Applications (OBA). I had ignored this topic up until now, but it definitely is worth checking out. Get started with Office Business Applications: What is it, and how can I get started? and then head over to the Office Business Applications Developer Portal. (Also have a look at the topics in Architecture Journal #10)

Tuesday, 20 February 2007 11:09:37 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 

Get it from the revamped VPC homepage. What's new? Hardware virtualization is supported, x64 as host operating system, Vista as host and guest plus a couple other enhancements.

Categories: Cool Download | Virtual PC
Tuesday, 20 February 2007 10:57:34 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 



#  Sunday, 18 February 2007

Microsoft Not Discussing Windows 7, Office 14

As for the title of this blog post, please refer to Falco.

Categories: Newsbites
Sunday, 18 February 2007 12:23:00 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 



#  Friday, 16 February 2007

Categories: this | Vista
Friday, 16 February 2007 19:51:04 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 

In my last blog entry UAC Elevation in Managed Code: A .NET COM Component Elevated I showed how to get up and running with an all-managed code solution for UAC and COM elevation. Today I want close out my series on UAC with some information on how to properly organize the project plus present a library you can reuse to get up and running quickly - without many of the manual and tedious steps from the previous proof of concept example.

Speaking of the previous sample: it is still the basis for this best practice, so the following directory layout will look familiar to you:

Before diving into code, I want to start out with the SampleSetup directory, which contains the executables. As you can guess, the starting point is Step1Register. It contains register.bat, which you have to execute:

Note that on machines without the .NET Framework SDK, there is no gacutil.exe. In that case, you have to drag & drop ManagedElevator.dll to c:\windows\assembly.

And in case you have been wondering from this screenshot, yes, the application now also plays nicely on Windows XP:

Of course, there is no consent UI popping up, nor is there a shield icon like there is on Windows Vista:

The magic for this cross-platform functionality is hidden in the UACHelper project - which brings us to the source section of this blog post:

All the necessary COM elevation magic is now moved to this neat little library - including the adapted UAC bits of VistaBridgeLibrary (no longer necessary). The names already give away the purpose of each class and where they are used:

  • COMRegistration Used by the elevated component to automatically register the necessary registry keys.
  • ShieldButton Used by the client to display a button with a shield icon (on Vista). For XP, no shield is rendered.
  • COMElevation Starts the requested component with admin privileges.
  • ElevatedProcess If you want to start a simple process elevated. Not used in this guidance.

The first customer of this library is the elevated component, so we start discussing this guy next:

At first glance, this is similar to the previous POC implementation. The main difference now is that I have broken down the functionality by feature area into namespaces:

  • The "main" namespace
  • The .Components namespace
  • The .Guids namespace
  • The .InterOp namespace

Let's look at these one by one.

The "main" namespace

Here, we have one class only:

class RegisterFunctions
{
  [ComRegisterFunction]
  public static void CustomRegister(Type t)
  {
    COMRegistration.RegisterForElevation(Assembly.GetExecutingAssembly().Location,
       SampleComponent.ClassToElevate,
       Global.AppId,
       100);

    // add additional "for elevation" components here by duplicating the above
  }

  [ComUnregisterFunction]
  public static void CustomUnregister(Type t)
  {
    COMRegistration.UnRegisterFromElevation(Assembly.GetExecutingAssembly().Location,
        Global.AppId);
  }
}

It is called when the assembly is regasm'ed, and it is here where you call into COMRegistration.RegisterForElevation to add all the necessary registry keys for elevation:

public static void RegisterForElevation(string assemblyLocation,
    string classToElevate,
    string appId,
    int localizedStringId)
{
 if (!UACHelperFunctions.IsUACEnabledOS()) return;

 // [HKEY_CLASSES_ROOT\CLSID\{71E050A7-AF7F-42dd-BE00-BF955DDD13D4}]
 // "AppID"="{75AB90B0-8B9C-45c9-AC55-C53A9D718E1A}"
 // "LocalizedString"="@E:\\Daten\\Firma\\Konferenzen und Talks\\..."
 RegistryKey classKey = Registry.ClassesRoot.OpenSubKey(@"CLSID\{" + classToElevate + "}", true);
 classKey.SetValue("AppId", "{" + appId + "}", RegistryValueKind.String);
 classKey.SetValue("LocalizedString", "@" + assemblyLocation + ",-" + localizedStringId.ToString(), RegistryValueKind.String);

 // [HKEY_CLASSES_ROOT\CLSID\{71E050A7-AF7F-42dd-BE00-BF955DDD13D4}\Elevation]
 // "Enabled"=dword:00000001
 RegistryKey elevationKey = classKey.CreateSubKey("Elevation");
 elevationKey.SetValue("Enabled", 1, RegistryValueKind.DWord);
 elevationKey.Close();

 classKey.Close();

 // [HKEY_CLASSES_ROOT\AppID\{75AB90B0-8B9C-45c9-AC55-C53A9D718E1A}]
 // @="ManagedElevator"
 // "DllSurrogate"=""
 RegistryKey hkcrappId = Registry.ClassesRoot.OpenSubKey("AppID", true);
 RegistryKey appIdKey = hkcrappId.CreateSubKey("{" + appId + "}");
 appIdKey.SetValue(null, Path.GetFileNameWithoutExtension(assemblyLocation));
 appIdKey.SetValue("DllSurrogate", "", RegistryValueKind.String);
 appIdKey.Close();

 // [HKEY_CLASSES_ROOT\AppID\ManagedElevator.dll]
 // "AppID"="{75AB90B0-8B9C-45c9-AC55-C53A9D718E1A}"
 RegistryKey asmKey = hkcrappId.CreateSubKey(Path.GetFileName(assemblyLocation));
 asmKey.SetValue("AppID", "{" + appId + "}", RegistryValueKind.String);
 asmKey.Close();

 hkcrappId.Close();
}

Please take note that when the component is registered on eg Windows XP, no registry entries are written. After all, they are not needed.

The .Components namespace

Not much of a change - it contains the administrative component(s).

The .Guids namespace

The guids have been moved to a separate namespace. The reason? That way you can reference the assembly in the client project and use the guids directly - no magic strings anywhere any more.

The .InterOp namespace

This is the most important change with regards to the POC project - defining the correct ComImport'ed interface is now the responsibility of the implementer of the elevated component. That way, anyone needing access to this component only needs to reference the assembly and they are good to go. It is a bad idea to have this interface part of the client codebase!

Speaking of the client... here is the button code for DemoForm.cs:

private void cmdLaunch_Click(object sender, EventArgs e)
{
 if (UACHelperFunctions.IsUACEnabledOS())
 {
   IHelloWorld ihw = COMElevation.Start<IHelloWorld>(
        SampleComponent.ClassToElevate, SampleComponent.IHelloWorld);
   ihw.SayHello();
   COMElevation.Release(ihw);
 }
 else
 {
   ManagedElevator.Components.ClassToElevate c = new ManagedElevator.Components.ClassToElevate();
   c.SayHello();
 }
}

What looks interesting at first is COMElevation.Start as well as Release:

public class COMElevation
{
 public static TIFace Start<TIFace>(string IID_Class, string IID_Interface)
 {
  return Start<TIFace>(new Guid(IID_Class), new Guid(IID_Interface));
 }

 public static TIFace Start<TIFace>(Guid IID_Class, Guid IID_Interface)
 {
  object o = UACManager.LaunchElevatedCOMObject(IID_Class, IID_Interface);
  return (TIFace)o;
 }

 public static void Release(object o)
 {
  Marshal.ReleaseComObject(o);
 }
}

Actually all it does is encapsulate the necessary calls to UACManager and Marshal. Why is there no if / else using IsUACEnabledOS here? Well, at first I thought I'd build such a switch, but then I thought again: why would I use COM InterOp if I don't have to? I already referenced the assembly for the component (for the guids and interop interface), so why not use managed all the way and save time? That's what I did in the cmdLaunch_Click event handler.

That's it for the code folks, now a little discussion at the end on why in the world would you even think about doing this in a cross-platform way, or why it is a stupid idea all along:

This approach is only sensible if your application runs as administrative user on XP, otherwise all the calls in the administrative component will fail. However, the cross-platform part is only there to make it a complete best practice, there is no "you must use it cross-platform" - if you build applications for Windows Vista with the eventual need to elevate a task, then UACHelper is definitely for you! (and forget about that it would even work on XP)

Oh, and I almost forgot - here is the complete download, source code included of course (my code is BSD licensed):

AutomaticRegistration.zip (91.92 KB)

Categories: Security | UAC | Use the source Luke | Vista
Friday, 16 February 2007 08:02:29 (W. Europe Standard Time, UTC+01:00)  #    Comments [1]

 



#  Thursday, 15 February 2007

Yesterday was the last day of VSone, the German .NET conference featuring almost all  well-known German speakers. As I had hinted at earlier, I did three talks there, and I still owe the audience of my VSTE DbPro talk a couple of resource links:

Thursday, 15 February 2007 16:39:26 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 

At long last at least a CTP is available. Definitely worthwhile to check out.

Categories: .NET | SQL Server
Thursday, 15 February 2007 10:46:14 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 



#  Tuesday, 06 February 2007

Nikhil has updated his controls for ASP.NET AJAX 1.0. Download here

Quote from his blog (so you know why you should go and download them):

  • UpdateHistoryThis is a non-visual control that allows you to add history entries to the browser's navigation stack selectively for some post-backs, and not for some others. This helps fix the back button to make it work, and allows you to implement Ajax patterns such as "logical navigation" and unique URLs.
  • StyledUpdatePanel A simple derived UpdatePanel that adds CSS class semantics. A simple addition, but a useful feature, nevertheless, that didn't make the feature cut.
  • AnimatedUpdatePanel Another derived UpdatePanel that displays new content using a variety of animations or effects: slides, wipes, cross-fades as well as a visual highlight. This allows you to implement the "visual notification" Ajax patterns such as the one second spotlight and one second mutation.
Categories: ASP.NET | Cool Download
Tuesday, 06 February 2007 12:59:52 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 

Once again, the TFS installation guide has been updated (2/5/2007). Download here

Tuesday, 06 February 2007 10:53:50 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

 



#  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]

 



© 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.