Managed Space : Jason Whittington's Radio Weblog

Updated: 4/9/2003; 1:51:09 PM.DevelopMentor

 

Home

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.

 
 

Wednesday, April 09, 2003
Rotor Community Site open

Starting today the Rotor Community Site at http://www.sscli.net is open for the public. The site has been set up in a collaboration between my group at Cornell and CollabNet, with some help from the folks at the Rotor team and MSR Cambridge. [Via Rotor Mailing List] [Sam Gentile's Blog]


1:51:06 PM      comment []
Flakey laptop
Sorry I haven't been posting.  My T23 has been flaking out for the last few weeks.  Hopefully I will have it resolved soon.
1:46:22 PM      comment []

Wednesday, March 26, 2003
Parody
Posted a "hotel yorba" parody in "Fun Stuff" today. (I wrote it yesterday).
4:07:26 PM      comment []
New C# language features

People have been been asking me about this so...

NEW C# LANGUAGE FEATURES. At OOPSLA late last year, the C# team made a splash by announcing some of the features they'll be adding to the compiler in the next next version of C# (not Everett). This link has that presentation as well as a very nice white paper discussing the new features in detail. [sellsbrothers.com: Windows Developer News]


11:01:48 AM      comment []
Thread.Abort() + finally == scary

Saw an interesting post on the DOTNET-LANGUAGE-DEVS list today that mentioned "interesting" behavior with Thread.Abort().  Calling this function raises an exception in the thread object, which the thread normally can handle prior to shutting down. Nice and clean.  Joe Marshal pointed out a somewhat nasty little race condition, however - if the call to Abort comes out while the thread is executing a finally block the finally block is abandoned (!).  I whipped up this little bit of code to test it out.  Moral of the story - be very careful with Thread.Abort().

using System;
using System.Threading;

class Nasty
{
 static void ThreadProc()
 {
  try
  {
   try
   {
    Console.WriteLine("executing try block");
   }
   finally
   {
    Console.WriteLine("Starting Finally block");

    Thread.Sleep(5000);

    //This line of code will never execute. If someone
    //aborts the thread *while we're in the finally block*
    //the execution of the finally block will be abandoned.

    Console.WriteLine("Done with finally block");
   }
  }
  catch(Exception e)
  {
   Console.WriteLine("Exception caught! {0}", e.GetType());
  }
 }

 static void Main()
 {
  Thread th = new Thread(new ThreadStart(ThreadProc));
  th.Start();

  //These two lines of code crudely guarantee that we
  //will abort the thread while it's in the middle of its
  //finally block.
  Thread.Sleep(1000);
  th.Abort();

  Console.WriteLine("press a key to exit");
  Console.ReadLine();
 }
}


9:23:44 AM      comment []

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

Friday, February 21, 2003
Spike!

So today I got a report from Justin who (among many other responsibilities) oversees the box that hosts my little blog.  I'm somewhat embarrassed to admit that right now I don't really have a way of tracking the traffic I generate much beyond following its ranking on the Radio Community Server

What blew me away was the size of the traffic spike that my "25 pathetic attempts" post generated. I went to #3 on the RCS server that day with something like 3300 hits reported.  The data I got today indicates that the number of (raw) hits was closer to 40,000 (!).  Check it out:

Wow!  I had no idea that post generated so much traffic.  Alas it looks like most people read the controversy and just moved on - I've settled back down in the rankings. The fact that I remain in the top 100 is to me an indictment of Radio and blogging in general - I write stuff about an obscure corner of an obscure technology that very few people in the world care about. If the population at large cared at all about blogging I would be like #1,248,764.  Oh well. The #1 Radio blog is consistently safersex.org  so I guess order is still being maintained somewhere in the universe...


4:33:48 PM      comment []

Thursday, February 20, 2003
AOP fans take note - eXtensible C#

Fellow DM Instructor Pierre Nallet has been working on a compiler project for quite awhile now.  Whenever I would ask him what it was he mumbled something about "adding some stuff to C#" and wouldn't say much more. 

Today I got an email that "eXtensible C#" shipped.  Wow! XC# lets you say things like this:

[Requires ("o != null")]
void SomeMethod (object o)
{
  ...
}

I'm too busy making the donuts this week to really sit down with it but it looks like a pretty slick product.  Perhaps the most amazing thing of all is that it's free.  Very nice stuff...


1:26:45 PM      comment []

Wednesday, February 19, 2003
Jim Miller comments on the Microsoft .NET Patents

There's been some buzz lately about Microsoft patenting some .NET technologies.  Jim Miller is one of the main forces behind the CLI, one of the smartest guys I know, and one of the nicest. Today he had this to say on a Rotor listserv sponsored by the University of Pisa.

As one of the inventors on that patent as well as the person heading up the standardization efforts for the CLI, I'd like to explain why I've never felt the two are in conflict.

The ECMA process requires that all patents held by member companies that are essential for implementing its standards are available under "reasonable and non-discriminatory (RAND) terms" for the purpose of implementing those Standards. This is the normal condition used in all International Standards organizations, including both ECMA and ISO.

But Microsoft (and our co-sponsors, Intel and Hewlett-Packard) went further and have agreed that our patents essential to implementing C# and CLI will be available on a "royalty-free and otherwise RAND" basis for this purpose.

Furthermore, our release of the Rotor source code base with a specific license on its use gives wide use to our patents for a particular

(non-commercial) purpose, and as we explicitly state we are open to additional licenses for other purposes.


11:42:12 AM      comment []

Monday, February 10, 2003
Remoting fun

I found an interesting little eddy in the remoting world.  I'm sure I'm far from the first to find it but it's a pretty neato little place.

As you may know there are two ways in which you can control how an object gets remoted in .NET.  By deriving from MarshalByRefObject you tell the runtime that you want a proxy to be created and if you mark it as [Serializable] you're telling the runtime to just send a clone. Most of the time we think about this in terms of what happens when we connect to a server object from a client, but this setting also matters a great deal any time you go to pass a parameter to a remote object.  If you pass a MarshalByRefObject then any calls the server makes on the passed object come back to the client.  This is often considered "icky".  Serializable (and not MBRO) objects are just sent along as values and any calls the server makes to the object happen locally.

So here's the situation I found myself in.  I have a system that passes around a shared property bag, hidden behind an interface like so:

public interface IIntBag
{
 int this[string id] { get; set; }
}

[Serializable]
public cl
ass IntBagImpl : IIntBag
{
 Hashtable h = new Hashtable();

 public int this[string id]
 {
    get  {   return (int)h[id];  }
    set  {   h[id] = value;      }
 }
}

Pretty pedestrian stuff - nothing that interesting so far. A typical class that manipulates it might look like this:

public class BagManipulator : MarshalByRefObject
{
 public void AddItemToBag(IIntBag bag)
 {
  Console.WriteLine("bag[\"1"] = 1;");
  bag["3"] = bag["1"] + bag["2"];
 }
}

Now - what happens when I call AddItemToBag? In the case where the caller and the callee are in the same appdomain nothing very interesting happens at all - the server modifies the bag directly and the client sees the results.  Things get interesting when I want to call AddItemToBag remotely.  If I mark the underlying object as serializable then the whole schmeer gets sent up to the server and the calls into the bag all happens locally. On one hand this is good - the server can play with the bag all it wants and no network traffic is incurred.  Notice though, that any changes the server makes to the bag are NOT propagated back to the client. I could derive IntBagImpl from MarshalByRefObject but then every time the server called the bag it would be calling back across the network into the client's AppDomain.  This would be just a wee bit SLOW.

What I really want for this class is to serialize it both directions - I want the object to be serialized up to the server so the server can play with it, but when AddItemToBag returns I want the modified bag to come back down to the client. I was all geared up to start exploring custom marshaling when I tried one simple thing - I changed AddItemToBag to take a ref IIntBag instead of just a plain IIntBag:

 public void AddItemToBag(ref IIntBag bag)
 {
  Console.WriteLine("bag[\"1"] = 1;");
  bag["3"] = bag["1"] + bag["2"];
 }

This turned out to be exactly the combination I wanted - the hashtable is serialized in both directions!  Reasonably obvious I suppose but I wasn't convinced it was going to work this way.  I figured "ref Int" wouldn't send results back down unless I literally allocated a new hashtable and assigned it to bag.  This is too cool.

Interestingly the C# compiler demonstrates a little quirk (maybe it demonstrates my own quirkiness) when I try to call the function.  If I say this:

IntBagImpl i =  new IntBagImpl();
Server.Add(ref i);

the compiler complains:

error CS1503: Argument '1': cannot convert from 'ref IntBagImpl' to 'ref IIntBag'

Well, duh. Just for fun I tried this:

IntBagImpl i =  new IntBagImpl();
Server.Add(ref (i as IIntBag));

Sick stuff to be sure, but hey - it might work :)  Alas, the compiler slaps me down with this:

error CS1510: A ref or out argument must be an lvalue

Changing the type of i to IIntBag of course fixed it, but apparently the compiler doesn't like taking a ref on a temporary object :).  Interesting. I wonder what the Mono compiler does here...


11:57:26 PM      comment []
All we know is we are Devo

Yeah yeah, so I took the quiz too:


Which OS are You?
Which OS are You?

Thanks to CraigBlog for the link..


12:02:44 AM      comment []

© Copyright 2003 Jason Whittington.



Click here to visit the Radio UserLand website.

 


April 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      
Mar   May

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

Stuff I read: