Managed Space : Jason Whittington's Radio Weblog

Updated: 4/9/2003; 1:47:22 PM.DevelopMentor

 

Subscribe to "Managed Space" in Radio UserLand.

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.

 
 

Tuesday, March 04, 2003
CodeDom needs help.

So a few weeks ago I got an idea to write a little helper shim that would make it easier to pass a parameter into a managed thread proc (the ThreadStart delegate type doesn't accept parameters).  What I ultimately decided was to write a little wrapper class that took an object array as parms to Start() and passed them to a thread object held internally.  The wrapper class "ThreadEx" would behave just like Thread, except that it would take that extra parameter to Start. 

Then I decided that that this was a generically useful thing to do.  Some scenarios where this kind of wrapping/composition would be useful include:

  • Extending /modify the behavior of a sealed class
  • Adding thread guards around all the methods on a class
  • Making a non-MarshalByRef class remoteable.
  • Building a type-safe wrapper around a class that takes generic parameters (Chris Sells has a tool on his site that does this kind of wrapping for the standard collection classes).

So I decided to write a tool that would take a class as input and produce a class that wraps it.  So for example if the original class looked like this:

class A
{
   public int myField;
   public void method(int a, int b);
}

the wrapper would look like this:

class AEx
{
   A wrapped = new A();
   public int myField
   {
      
get { return wrapped.myField; } 
       set { wrapped.myField == value; }
   }
   public void method(int a, int b) { wrapped.method(a,b); }
}

This seemed like a fun little project.  It seemed like I could re-expose all the interfaces, forward the methods, and expose public properties and fields as properties on the wrapper.  I had toyed with code generation with System.CodeDom a bit and decided it would be the perfect technology for implementing the tool, because then users of the tool could choose C# or VB.NET for their wrappers. 

The ida  behind CodeDom is pretty straightforward.  You generate an object model with objects that represent "code" things like classes, methods, properties, and events.  The objects in this model are somewhat language-neutral and this model is consumed by Language specific Providers that then produce source code in that language. 

When I started writing the code it indeed started out pretty easily, but it didn't take too long for limitations of CodeDom to appear that basically make it impossible to build such a tool without some nasty hacks.  Nearly all of them showed up when I  tried to wrap class System.String. The problem is that some languages can handle some constructs (like say, unsafe keywords) and some familiar language constructs don't really have 1::1 mappings into IL/Metadata (e.g. Indexers). In addition CodeDom can't handle some types of constructs.  The ones I ran into included:

CodeDom can't emit nested namespaces.  This one doesn't affect my tool that much but it does kind of suck.

CodeDom can't be used to emit custom Add/Remove functions for events.  For many events this wouldn't matter, but it matters a lot if the wrapped class provides implementation with a construct like

public event myDelegateType MyEvent
{
   add{...}
   remove{...}
}

CodeDom cannot properly represent indexers.  I wound up emitting indexers by using CodeSnippets, which are basically string values.  Indexers are actually implemented as properties marked with a [DefaultMember] attribute.  CodeDOM has a CodeIndexerExpression class (useful if you want to call an indexer) but no CodeMemberIndexer class to use if you want to actually *implement* an indexer.  I wound up generating strings manually and saving them into the CodeDOM tree as CodeSnippets.  This sucks because CodeSnippets are language-specific.

CodeDom cannot represent explicit interface implementations.  Explicit interface implementation functions are kind of strange, because they are sort of private. and sort of public.  I haven't been able to find a way to represent them in the CodeDom.  For example, if the wrapped type has a method with a signature like void iface.method() then there is no way to write a CodeDom tree that will emit the proper function declaration in VB, e.g:

Overloads Function test(ByVal a As Integer, ByRef b As Integer, ByRef c As Integer) As Integer Implements ITest.test

A somewhat amusing thing I ran into as well was that in VB parameter names must not be the same as the method name.  So for example, if I wrap String.Format in VB by default I get this:

Public Overloads Shared Function Format(ByVal format As String, ByVal arg0 As Object) As String
   Return String.Format(format, arg0)
End Function

This would be fine in C#, but in VB this declaration breaks because the first parameter has the same name as the function (This proves that System.String wasn't implemented in VB!)

CodeDom can't hold the "unsafe" keyword. This makes some sense, because VB can't implement unsafe code at all, but it's still irritating that it's not a method flag.  System.String for example has constructors that take a Char * initializer.  I can dutifully emit the "Char *" but I can't easily emit the "unsafe" keyword.

All of these together meant that writing a completely capable wrapper generator is well-night impossible with CodeDom.  CodeDom has other limitations as well (Ian Griffiths tells me it can't emit switch statements for example) that really degrade its usefulness.  My little tool currently works for the 80% case but the interesting classes to wrap almost all have some features my tool can't handle.  CodeDom is a fairly interesting idea - here's hoping it gets a little TLC someday...

 


9:37:07 AM      comment []
Cool hack from CraigBlog

I've been struggling with running as a non-admin ever since I took the plunge. I, quite frankly, hate it. Lots of little things don't work right, and it's a pain to have to figure out what's wrong just because someone decided to write to the HKLM in the registry or drop data in program files. Well today I figured out something that will save me a lot of pain. Running a command prompt as admin lets me do some stuff, but I've found that installs in particular tend to take a real login to work properly. Switching back and forth was getting old.

I'm running RC2 of Windows Server 2003. Well, I realized that I've got Terminal Services at my disposal - so I can actually log in twice! Just open up a remote connection to an administrative desktop and park it. I changed the colors on the admin desktop to give me a visual cue as to where I'm at. I still run most things in the regular user desktop, but when I need to elevate myself to admin, all I have to do to get there is ALT-TAB. 

Ahhh......[CraigBlog]

What a cool hack!  I have to admit that [mostly to tweak my friend Keith] I still usually run with admin rights.  Craigs idea really is a great one though -  I like it!


9:33:49 AM      comment []

© Copyright 2003 Jason Whittington.



Click here to visit the Radio UserLand website.

 


March 2003
Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Feb   Apr

Stuff I recommend:
.NET Resources
COM/C++ Resources
Fun stuff
Stuff I've done:
Windows/COM
.NET
Writings
Conferences

Stuff I read: