Saturday, May 15, 2004
« VB 2004 World Tour | Main | Virtually Everywhere? »

Recently, a client of mine had a need for me to write (largely rewrite) an application for them.  Without getting specific or too detailed, the gist of the matter was that they had an application written and (semi-)working on Windows 95 that they NEEDED to migrate to Windows 2000/XP.  Unfortunately, they couldn't exactly take their existing investment and run it on Win2k due to some weird incompatibilities.

It came down to them needing to rewrite it entirely.  The client (with some guidance and directly from yours truly) opted to go with a .NET solution.  A hiccup was that the application relied very heavily on a third-party suite of tools that provides scientific analysis of images.  This suite was written entirely in C++ and is an MFC application.  Well, I'm not an expert in C++ and despite my experience in C++ it would take me a long time to write a full-blown app in C++, a lot longer than to, say, code the same app in C# or VB.  Well, the route I've decided to take (and one that I feel will provide the best experience going forward) is to design and code a translation layer between what would be considered a '.NET' interface to the backend MFC.

To accomplish this, I created a C++.NET dll that natively talks MFC and exposes the objects out to a .NET/managed client.  This was not a cake walk as it was fraught with gotchas.  These are a few of the caveats that I ran into:

  1. Memory management.  I had to manually control object lifetime of the MFC components.  I did this by designing an object hierarchy wherein each object implemented IDisposable.  Additionally, I supported the Finalize method by creating object destructors.
  2. Documentation.  The documentation for the suite of tools was horrendous.  The example code snippets were riddled with errors and inconsistencies.  The documentation was flat-out wrong in some cases.  Possible exceptions were not documented and identified.  This was one of the biggest hurdles to overcome.  Unfortunately, I really didn't have much of a choice with regards to their documentation - I had to use it.  I could find NOTHING online about this tool and we were stuck using it - no if's, and's or but's about it.
  3. Object interfaces.  The objects against which I was coding had very convoluted and relied on a lot of programmer knowledge of the inner-workings of the objects.  It was expected that to call method B() you already called A().  This particular design wouldn't be so bad except that the documentation never stated that you should have called A() in the first place.
  4. Lack of good customer support.  Emails were not responded to in a timely manner and were often not helpful.  Calls resulted in 'you're already in the queue' answers.
  5. Time Constraits.  The customer needs the app yesterday.

Well, the above list is by no stretch comprehensive, but gives a flavor of what I was up against.  Anyway, push come to shove, I was able to complete my .NET API version of the MFC components which, if I do say so myself, is much more useable and more understandable than the original objects.

This is what I did:

I created a base object (let's call it ObjectBase (names have been changed to protect the innocent)).  ObjectBase implements the IDisposable interface and manages the object's lifetime.  When the .NET object is created, a pointer to the object is acquired in ObjectBase and is then held until the .NET object is destroyed at which point the pointer is deleted.  Each ObjectBase-derived class can and may hold other non-.NET objects in memory.  The Dispose() method in ObjectBase makes a call to a virtual function called releaseUnmanagedMemory() that may be overridden in the derived class.

public __gc __abstract class ObjectBase : public IDisposable
{
   public:
      ObjectBase(void);
      ~ObjectBase(void);
      void Dispose(void);

      __property String* get_Name(void);
      __property void set_Name(String* value);

   protected:
      virtual void releaseUnmanagedMemory(void);

   private:
      mfcObj* _obj;
      bool _dispose;
      void dispose(bool disposing);

   protected public:
      ObjectBase(mfcObj* obj);
      __inline mfcObj* obj() { return _obj; }
}

That's not the whole object, but gives an idea of the direction I was going.  There's a protected public ctor as well that allows the .NET object to be created within my dll given the object pointer.  When I had finished the entire design it turned out that it was very elegant.

I tried to make my objects as self-documentating as I could while retaining all of the capabilities of the underlying third-party components at the same time making the classes have a .NET feel to them.  This required cleaning up object and variable names as well as creating several additional class types that weren't defined in the original API.

If anyone's interested I could probably post my actual implementation with some descriptions and comments.

Saturday, May 15, 2004 5:01:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback