DCSIMG
January 2010 - Posts - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

January 2010 - Posts

The Return of the Class Wizard

Published at Jan 24 2010, 10:20 AM by pavely

Visual C++ 6 aficionados remember (fondly) the Class Wizard, most useful in MFC applications. The Class Wizard has gone away in later version of Visual Studio (2002, 2003, 2005, 2008), but is now making a smashing return in Visual Studio 2010 (Project menu –> Class Wizard or Ctrl+Shift+X).

image

This has the familiar options (relative to VC++ 6), such as adding/removing message handlers, command and update command handlers, creating new classes, member variables, etc. There are new options, such as creating custom message handlers and the ability to search.

In my experience, many developers back then were very dependent on the almighty Class Wizard, so the same warnings apply like any other wizard: use but do not abuse, and always, always – know what it does so you can do it yourself or undo it, if the need arises.

Gotcha: CreateProcess causes Access Violation

Published at Jan 17 2010, 11:39 AM by pavely

The famous CreateProcess function may fail with an access violation. For example, this innocent looking code causes a crash:

STARTUPINFO si = { sizeof(si) };

PROCESS_INFORMATION pi;

 

CreateProcess(0, _T("notepad"), 0, 0, FALSE,

    0, 0, 0, &si, &pi);

What’s wrong with this code? Apparently nothing.

The key here is that CreateProcess is not actually a function at all, but a macro that’s expanded to CreateProcessA or CreateProcessW depending a compile time constant (UNICODE).

If we open the docs, we find for the second argument: “The Unicode version of this function, CreateProcessW, can modify the contents of this string”.

The string I passed is a constant string, coming from a region marked as “read only” and this is why the access violation occurs. Changing CreateProcess to CreateProcessA avoids the crash. The reason is that the A function translates the string to unicode and calls the W function. The translated string is in a read/write buffer, so no problem there. This should generally be avoided because of the extra step A functions do.

In recent versions of Visual Studio the default libraries are unicode (as they should be, as the Windows 9x family of OSes is finally dead and buried). This is while a transition from (e.g.) Visual Studio 2003 to 2008 may cause old code to crash.

So, the general solution should be:

TCHAR name[] = _T("Notepad.exe");

CreateProcess(0, name, 0, 0, FALSE, 0, 0, 0, &si, &pi);

This still doesn’t answer the question: why? Why would CreateProcessW want to write back to the supplied string? What could it possibly write? It can’t write arbitrary stuff as the size of the buffer is unknown to the function and can cause access violation or memory corruption. But it does write something back (looks like the same string passed to it). For me, it’s still a mystery.

WeakReferences and Events

Published at Jan 07 2010, 01:35 PM by pavely

One of the well-known pitfalls of using events, is the holding of the subscriber instance by the publisher (if connecting to a delegate holding an instance method). If the subscriber forgets to unsubscribe, the instance cannot be garbage collected because the publisher holds a reference to it. Worse yet, the subscriber continues to receive event notifications even though it’s not interested.

One way to deal with this is presented in Jeffrey Richter’s book “CLR via C#”. The idea is based on a custom event implementation holding a list of WeakReference objects to the incoming subscribers delegates:

class Publisher {

   private List<WeakReference> _subscribers = new List<WeakReference>();

 

   public event EventHandler TheEvent {

      add {

         _subscribers.Add(new WeakReference(value));

      }

      remove {

         //...

      }

   }

 

   protected virtual void OnTheEvent() {

      // raise the event

      for(int i = _subscribers.Count - 1; i >= 0; i--) {

         EventHandler eh = (EventHandler)_subscribers[i].Target;

         if(eh == null)   // delegate GC’d

            _subscribers.RemoveAt(i);

         else   // raise the event

            eh(this, EventArgs.Empty);

      }

   }

}

This approach has a fatal flaw: the WeakReference wraps the delegate object – not the subscriber instance itself! This means, that if a GC occurs, all the delegates in the list will be gone, disconnecting all subscribers, even those who are still very much alive.

To correct this, we need to use a WeakReference around the subscriber instance itself and not the delegate. Here’s some code that accomplishes this:

class Publisher {

   private List<MethodInfo> _methods = new List<MethodInfo>();

   private List<WeakReference> _instances = new List<WeakReference>();

 

   public event EventHandler TheEvent {

      add {

         _methods.Add(value.Method);

         if(value.Target != null)

            _instances.Add(new WeakReference(value.Target));

         else

            _instances.Add(new WeakReference(value));

      }

      remove {

         //...

      }

   }

 

   public void Fire() {

      for(int i = _methods.Count - 1; i >= 0; i--) {

         object o = _instances[i].Target;

         if(o != null) {

            if(o.GetType() == typeof(WeakReference))

               ((EventHandler)o)(this, EventArgs.Empty);

            else

               _methods[i].Invoke(o, new object[] { this, EventArgs.Empty });

         }

         else {

            _methods.RemoveAt(i);

            _instances.RemoveAt(i);

         }

      }

   }

}

Although this is somewhat involved, it solves the issue. It shouldn’t be too difficult to create some helper class to handle this in a generic way, to be used by any event publisher. (and the remove part must be completed…)

Another approach would be to abandon the event idea and use an interface for notifications. Wrapping an interface by a WeakReference is easy enough. The only downside – a subscriber must supply an object (cannot use static methods as callbacks) implementing the entire interface (even if only some methods are interesting).