Writing Solid Code

My apologies to Steve Maguire for "borrowing" a title.

I constantly see code, examples, and advice that perpetuate unsafe coding practices.  As programmers we have the habit of getting something to a "working" state and call it "done".  This is especially true in processes that have no real architecture or design phases.  Over the years, as a society, programmers have begun to realize some of the obvious flaws and have been perpetuating practices and code checkers to avoid such flaws.  But, there's still the mentality of "but it works [in my limited tests], how could it be wrong". 

For example, I don't know of any programmers that would sanction the following C++ code:

    int * const p = new int[10];

    p[1] = 10;

    delete[] p;

    if(p[1] == 10)

    {

        puts("ten");

    }

    else

    {

        puts("not ten");

    }

But, "it works" in a release build.

There are many, many similar examples of code that "works" in limited circumstances and this is deemed acceptable by, what seems to be, a majority of programmers.  I've seen many discussions of programming constructs that can't work 100% of the time; with impassioned participants that will always argue that either they can prove it works with an example and simply ignore proofs where it fails as "contrived" or statistically insignificant.  Although, I don't know of a single programmer that can claim they've never been guilty of this.

From a bricks-and-mortar building standpoint; we, as a society, realized the errors of assuming just 99% is good enough.  From this there were the instigation of Engineering certifications/licensing, building standards, etc.  All to ensure that 1% was equally as important as the other 99%; to ensure engineers don't unintentionally kill someone.  Even with this we're still reminded how important it is to abide by these standards and what happens when we don't (like the Hyatt Regency Walkway Collapse, or the Sampoong Department Store Collapse), despite the likelihood.

To a certain extent, our tools, processes, training, all seem to perpetuate the "good enough" mentality.  The ANSI C library is a prime example.  Largely designed in the 70's when security wasn't an issue yet, it's rife with functionality to let programmer write buffer overflow code to their heart's content.  For example:

#pragma pack(1)

    struct MyStruct

    {

        char s[10];

        int i;

    } myStruct = {"", 1};

#pragma pack(pop)

    sprintf(myStruct.s, "1234567890");

    printf("%d", myStruct.i);

…where the output is "0", not "1"; with nary a compiler warning or runtime error.  It's APIs like these and the mentality of "when is that ever going to happen" that lead to software security flaws.  Even with continual bombardment of security patches, developers still can't get past the "works 99% of the time" hurdle.

Here is small list of some of the "hot spots" that will still cause heated discussions even amongst experienced developers:

  • .NET: Avoid catch(Exception) in anything other than a last-chance handler.
  • C++: Avoid catch(…) in anything other than a last-chance handler.
  • Windows: Don't access windows data from a thread that didn't create the window.
  • .NET: Avoid Control.Invoke.
  • .NET/VB: Avoid DoEvents.
  • Performing potentially lengthy operations on the main/GUI thread.
  • Testing for valid pointers and IsBad*Ptr().

Code Analysis: How to Correctly Resolve CA1300 – "Specify MessageBox Options"

This is the first installment of what I hope to be many short bits of guidance about correctly resolving some of the more complex warnings coming out of FxCop and Code Analysis.

The CA1300 warning is specifically about the right-to-left or left-to-right reading order of a message box.  The documentation for the warning alludes that a message box doesn't automatically inherit the containing form's reading order or the reading order from the user's current culture.  Although, I can't find any documentation that confirms either.

In any case, this is a rather complex warning to resolve, you possibly have to traverse the form's lineage (when reading order is inherited) to find the reading order setting.  The example fix in the documentation basically puts this logic in the code that calls MessageBox.Show.  This is very un-resuable and couples the logic not only to the containing class but to the method it's contained within.  My solution attempts to allow resolution of CA1300 with as little coupling as possible and little or no changes to existing code.

My solution is obviously going to implement the extra logic in a new class; but I'll be implementing in a way where you don't have to change your existing calls to MessageBox.Show.  My resolution extends the Form class and provides a MessageBox property that is implemented with a protected nested class that provides Show methods with all the same signatures as System.Windows.Forms.MessageBox.Show.  The only requirement to using this class is that you haven't explicitly used the full namespace when calling MessageBox.Show, like 'System.Windows.Forms.MessageBox.Show("text", "caption")'.

The following is an example of code that generates the warning:

namespace WinFormApplication

{

    internal sealed partial class MainForm : Form

    {

        public MainForm ( )

        {

            InitializeComponent();

        }

 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",

            "CA1303:DoNotPassLiteralsAsLocalizedParameters",

            MessageId = "System.Windows.Forms.MessageBox.Show(System.String,System.String)")]

        private void button1_Click ( object sender, EventArgs e )

        {

            MessageBox.Show("text", "caption");

        }

    }

}

The following is the class that extends Form using a protected class to implement a MessageBox property to redirect calls to MessageBox.Show–which eventually creates the appopriate MessageBoxOptions value and delegates to System.Windows.Forms.MessageBox.Show.

namespace PRI.Windows.Forms

{

    public class RtlAwareForm : Form

    {

        private MessageBoxProxy messageBoxProxy;

 

        protected MessageBoxProxy MessageBox

        {

            get

            {

                return messageBoxProxy;

            }

        }

 

        public RtlAwareForm ( )

        {

            messageBoxProxy = new MessageBoxProxy(this);

        }

 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design",

            "CA1034:NestedTypesShouldNotBeVisible")]

        protected class MessageBoxProxy

        {

            private Control parent;

            public MessageBoxProxy (Control parent)

            {

                this.parent = parent;

            }

 

            private static Boolean IsRightToLeft ( Control control )

            {

                if (control.RightToLeft == RightToLeft.Inherit)

                {

                    Control parent = control.Parent;

                    while (parent != null)

                    {

                        if (parent.RightToLeft != RightToLeft.Inherit)

                        {

                            return parent.RightToLeft == RightToLeft.Yes;

                        }

                    }

                    return CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;

                }

                else

                {

                    return control.RightToLeft == RightToLeft.Yes;

                }

            }

 

            public DialogResult Show ( string text )

            {

                return Show(text,

                    string.Empty,

                    MessageBoxButtons.OK,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( string text, string caption )

            {

                return Show(text,

                    caption,

                    MessageBoxButtons.OK,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons )

            {

                return Show(text,

                    caption,

                    buttons,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon )

            {

                return Show(text,

                    caption,

                    buttons,

                    icon,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( String text,

                String caption,

                MessageBoxButtons messageBoxButtons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton )

            {

                MessageBoxOptions options =

                    IsRightToLeft(parent) ?

                        MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading : 0;

 

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    messageBoxButtons,

                    icon,

                    defaultButton,

                    options);

            }

 

            public DialogResult Show ( IWin32Window owner, string text )

            {

                return Show(owner,

                    text,

                    string.Empty,

                    MessageBoxButtons.OK,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( IWin32Window owner, string text, string caption )

            {

                return Show(owner,

                    text,

                    caption,

                    MessageBoxButtons.OK,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons )

            {

                return Show(owner,

                    text,

                    caption,

                    buttons,

                    MessageBoxIcon.None,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon )

            {

                return Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    MessageBoxDefaultButton.Button1);

            }

 

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton )

            {

                MessageBoxOptions options = parent != owner ?

                    0 : (IsRightToLeft(parent) ?

                        MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading : 0);

 

                return System.Windows.Forms.MessageBox.Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options);

            }

 

            public DialogResult Show ( string text,

                string caption, MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options )

            {

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                bool displayHelpButton )

            {

                return System.Windows.Forms.MessageBox.Show(text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    displayHelpButton);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath )

            {

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options )

            {

                return System.Windows.Forms.MessageBox.Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                string keyword )

            {

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    keyword);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                HelpNavigator navigator )

            {

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    navigator);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath )

            {

                return Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath);

            }

 

            public DialogResult Show ( string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                HelpNavigator navigator,

                object param )

            {

                return System.Windows.Forms.MessageBox.Show(parent,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    navigator,

                    param);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                string keyword )

            {

                return System.Windows.Forms.MessageBox.Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    keyword);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                HelpNavigator navigator )

            {

                return System.Windows.Forms.MessageBox.Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    navigator);

            }

 

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",

                "CA1822:MarkMembersAsStatic")]

            public DialogResult Show ( IWin32Window owner,

                string text,

                string caption,

                MessageBoxButtons buttons,

                MessageBoxIcon icon,

                MessageBoxDefaultButton defaultButton,

                MessageBoxOptions options,

                string helpFilePath,

                HelpNavigator navigator,

                object param )

            {

                return System.Windows.Forms.MessageBox.Show(owner,

                    text,

                    caption,

                    buttons,

                    icon,

                    defaultButton,

                    options,

                    helpFilePath,

                    navigator,

                    param);

            }

        }

    }

}

I've attached a non-web-site friendly version of the class in the attachements, that includes more comments and less line breaks. 

Now, the only change to the original code to resolve the warnings is the addition of a using namespace directive and a change to the form's base class (and in this example, a change to the CodeAnalysis suppression):

namespace WinFormApplication

{

    using PRI.Windows.Forms;

 

    internal sealed partial class MainForm : RtlAwareForm

    {

        public MainForm ( )

        {

            InitializeComponent();

        }

 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",

                "CA1303:DoNotPassLiteralsAsLocalizedParameters",

                MessageId = "PRI.Windows.Forms.RtlAwareForm+MessageBoxProxy.Show(System.String,System.String)")]

        private void button1_Click ( object sender, EventArgs e )

        {

            MessageBox.Show("text", "caption");

        }

    }

}

‘System.Threading.Thread.Suspend()’ is obsolete: ‘Thread.Suspend has been deprecated…

For time eternal there has been a way to suspend (and by association resume) another thread.  It wasn't until .NET 2.0 that someone took leadership and conceded that suspending another thread is not a safe thing to do.  .NET 2.0 deprecates System.Threading.Thread.Suspend() and Resume() (although, Resume() isn't dangerous by association…).

Basically, Suspend() has no care that the thread may actually be managing one or more complex invariants that can't be changed atomically.  Normally with complex invariants synchronization is used to ensure while one thread is modifying the individual components of an invariant another thread doesn't try to access it.  This is important because the validity of an invariant is rarely atomic; meaning changing an invariant may take several steps and may be in an unknown or invalid state between the first and the last step (a date is a good example, setting the day and month is two steps; until both steps are complete the date might be invalid).  Suspending a thread circumvents the synchronization primitives and cuts the thread off at the knees.

On a lower level, a thread may be performing a system-wide-synchronized operation like locking a range of bytes in a file, writing to those bytes, unlocking those bytes, then closing the file.  If a thread is suspended before the unlock, that range of bytes will remain locked for an indeterminate amount of time until the thread is resumed or the process terminates.  Not a good thing.  Most threads don't do all their work with low-level methods and call various higher-level helper methods to perform tasks.  This means the author of a thread's routine really has no way to determine whether their thread is in a "suspendable" state at any given point in time.  .NET complicates this even further by using locks during execution of class constructors–increasing the likelihood that suspending a thread will cause dire consequences.

Unfortunately, the documentation for Thread.Suspend() really doesn't offer much in the way of guidance on how to get around this issue with your own threads, it just casually mentions classes Monitor, Mutex, Event and Semaphore; but doesn't offer any detail on how to use them to put your thread into a wait state–the equivalent of Suspend.

When I create threads they're meant simply to let my UI be responsive or make use of multiple CPUs (i.e. in both cases they're CPU bound) and don't have much need to put a thread into a wait state manually.  But, it's not without merit.  There are lots of circumstances I don't normally get into that could benefit from being able to put a thread into a wait state.  It's also a common question; so, the following is small class that I created: SuspendableThread.

namespace PRI.Threading

{

    using System.Threading;

    abstract class SuspendableThread

    {

        #region Data

        private ManualResetEvent suspendChangedEvent = new ManualResetEvent(false);

        private ManualResetEvent terminateEvent = new ManualResetEvent(false);

        private long suspended;

        private Thread thread;

        private System.Threading.ThreadState failsafeThreadState = System.Threading.ThreadState.Unstarted;

        #endregion Data

 

        public SuspendableThread ( )

        {

        }

 

        private void ThreadEntry ( )

        {

            failsafeThreadState = System.Threading.ThreadState.Stopped;

            OnDoWork();

        }

 

        protected abstract void OnDoWork ( );

 

        #region Protected methods

        protected Boolean SuspendIfNeeded ( )

        {

            Boolean suspendEventChanged = suspendChangedEvent.WaitOne(0, true);

            if (suspendEventChanged)

            {

                Boolean needToSuspend = Interlocked.Read(ref suspended) != 0;

                suspendChangedEvent.Reset();

                if (needToSuspend)

                {

                    /// Suspending…

                    if (1 == WaitHandle.WaitAny(new WaitHandle[] { suspendChangedEvent, terminateEvent }))

                    {

                        return true;

                    }

                    /// …Waking

                }

            }

            return false;

        }

 

        protected bool HasTerminateRequest ( )

        {

            return terminateEvent.WaitOne(0, true);

        }

        #endregion Protected methods

 

        public void Start ( )

        {

            thread = new Thread(new ThreadStart(ThreadEntry));

 

            // make sure this thread won't be automaticaly

            // terminated by the runtime when the

            // application exits

            thread.IsBackground = false;

 

            thread.Start();

        }

 

        public void Join ( )

        {

            if (thread != null)

            {

                thread.Join();

            }

        }

 

        public Boolean Join ( Int32 milliseconds )

        {

            if (thread != null)

            {

                return thread.Join(milliseconds);

            }

            return true;

        }

 

        /// <remarks>Not supported in .NET Compact Framework</remarks>

        public Boolean Join ( TimeSpan timeSpan )

        {

            if (thread != null)

            {

                return thread.Join(timeSpan);

            }

            return true;

        }

 

        public void Terminate ( )

        {

            terminateEvent.Set();

        }

 

        public void TerminateAndWait ( )

        {

            terminateEvent.Set();

            thread.Join();

        }

 

        public void Suspend ( )

        {

            while (1 != Interlocked.Exchange(ref suspended, 1))

            {

            }

            suspendChangedEvent.Set();

        }

 

        public void Resume ( )

        {

            while (0 != Interlocked.Exchange(ref suspended, 0))

            {

            }

            suspendChangedEvent.Set();

        }

 

        public System.Threading.ThreadState ThreadState

        {

            get

            {

                if (null != thread)

                {

                    return thread.ThreadState;

                }

                return failsafeThreadState;

            }

        }

    }

} // namespace

This thread class works a little differently than the run-time's System.Threading.Thread class in that you can't just pass it a method from any-old place.  Because we don't want just anyone getting at the implementation details used to suspend the thread, you have to derive from SuspendableThread and override OnDoWork so your code can gain access to helper methods.

This class has another feature.  If you're likely to want to initiate thread suspension externally, you're likely to want to initiate thread termination externally as well.  To that effect this thread also has Terminate() and TerminateAndWait() methods that ask the thread to terminate and will awaken the thread is suspended (something System.Thread.Suspend() and System.Thread.Abort() won't do).

Using this class is fairly simple.  If you want a suspendable/terminatable thread you simply create a new SuspendableThread-derived class and override OnDoWork with the following pattern:

class MyThread : PRI.Threading.SuspendableThread

{

    protected override void OnDoWork ( )

    {

        try

        {

            while (false == HasTerminateRequest())

            {

                Boolean awokenByTerminate = SuspendIfNeeded();

                if (awokenByTerminate)

                {

                    return;

                }

 

                // TODO: replace the following to lines

                Debug.WriteLine("doing some work…");

                Thread.Sleep(450);

            }

        }

        finally

        {

            // TODO: Replace the following line with thread

            // exit processing.

            Debug.WriteLine("Exiting ThreadEntry()…");

        }

    }

}

For this class to be useful you generally have to have an iterative algorithm that can test for suspend/terminate during each iteration (the while loop in the above pattern).

To initiate thread suspension, simply call the SuspendableThread.Suspend() method–which asynchronously suspend the thread.  SuspendableThread.Resume() resumes the thread.  For terminate, generally you want to be able to determine when the thread has terminated.  If you want to do that asynchronously  you use the SuspendableThread.Terminate() and SuspendableThread.Join() methods.  To start the terminate process Terminate() is called.  When your asynchronous work is done (i.e. you went on to do something else while the thread was terminating) call Join() to block until the thread is terminated.  If you don't need to, or don't have anything to, do while the thread is terminating, use SuspendThread.TerminateAndWait().

This class gives complete control of suspension and termination to the thread.  So, use this class with great care.  You have to write your OnDoWork override in a way that won't cause deadlocks.  If you don't have an iterative process, this class isn't for you.  If each of your iterations takes much time (more than say 500ms) then this class probably isn't for you either.  If you can't terminate in the middle of your process, this class also is not for you.  This class will not let the application terminate until the thread routine has exited; so, be sure you initiate termination of the thread before the application exits or you'll get a deadlock.  The class is a scaled-down version of System.Threading.Thread.  It doesn't provide all the same goodies System.Threading.Thread does (partially for .NET Compact Framework 2.0 support, and partially for readability) and is intended to be used in limited scenarios.  But, you can extend it to mirror some of System.Threading.Thread's functionality fairly easily–like Name and Priority properties.

This class was intentionally designed to be compatible with the .NET CF 2.0 (delete Join(TimeSpan) before compiling) but has not been tested with .NET CF 2.0 (if you use the class in .NET CF 2.0, please send me a note and I'll update this post for the benefit of other readers).

[30-Oct-06 Update: as it turns out, this class is not entirely .NET CF 2.0 compatible, please contact me if you're interested in a .NET CF 2.0 compatible version]

Using Toolbox for Code Snippets in Visual Studio 2005

Just noticed today that you can drag text into the Toolbox then repeatedly drag it back out wherever you want, into a Visual Studio source window.

Simply select some text in a source window, drag it over to the Toolbox tab (assuming you have Toolbox set to Auto Hide), wait for Toolbox to expand and drop the text wherever you want it.

Now you can simply open up the Toolbox and drag-drop that text whenever you want, much like a code snippet; but much faster (and without literals).

Writing Libraries with Visual C++

Writing libraries of APIs is a very diffcult task on any platform.  There are many things you have to consider during the design of that API, especially with an unmanaged run-time.

Visual C++ actually makes it more complicated to build and use libraries.

Some rules to evaluate an API you're thinking of using or to help design your APIs:

Use the same run-time

This may seem obvious just reading it; but it's very complex to use the same run-time.  With Visual C++ 6 there are 6 different versions of the run-time that you have to be careful to match.  There are DEBUG and RELEASE versions of the following: Single-Threaded, Multithreaded, and Multithreaded DLL.  Multithreaded-DLL is used when you want to create a DLL that needs a run-time that supports multithreading (either in a multithreaded host or a single-threaded host).  Unfortunatly Visual C++ doesn't make your life easy if you happen link to a LIB using the wrong run-time threading and just spits out a LNK4098 warning and you must infer from that that you've just built an unsupported configuration.

One problem I often see is the use of a 3rd party library compiled with a different version of Visual C++.  The Linker and the Librarian think they're being smart by trying their hardest to link the library, only raising warnings/errors when there's enough difference between the run-times used that it can't resolve everything.  Under some circumstances you get no warnings when linking a static library built with a different version of Visual C++ despite the resulting configuration being unsafe and entirely unsupported.  Even when you get errors there's lots of things you can do to resolve them; but that's just

If at any time the vender of the 3rd party library s

Allocate memory and free memory with the same module

Memory management routines are a common and plentiful thing with programming.  Memory allocated with a particular memory management system must be deallocated with the same memory management system.  With the C/C++ run-time most allocations end up going thro

Provide Ability to Pass Along State

If an API uses callbacks at all, or ever plans to, it's essential that the callback be able to figure out the context in switch it is called.  If the callback is being called with any sort of data that can be overridden or tagged by the application this may not be necessary.  Otherewise the callback should include a void * that was supplied by the application upon some initialization.  This type of pattern can be seen with "application-defined data" of type LPARAM with many Windows function calls or structures.  VB design use it extensively as the "Tag" property.

Visual Studio 2005 Service Pack 1 Beta

As with any Beta, it's not wise to think of it as "stable" and expect it to work as reliably, or be as tested as an RTM version.

But, I've found the second of what I consider blocking issues with SP1 that leads me to think it's time to test the uninstallation.  I independently found that enabling Code Analysis for C/C++ spits out what seems like verbose tracing information that causes Visual C++ to view building with Code Analysis turn on to fail a Build.  I found that someone else had found this already and logged it on Connect: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=218742.  Breaking a major like Code Analysis for C/C++ seems a little disconcerting.

The second blocking issue is the inability to step through code running in a second thread create via an asynchronous delegate: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=223081  Again, a reasonably fundamental feature gone.

Add Visual Studio 2005 Intermediate Files to Windows Disk Cleanup

A few years ago I remember a small little Code Project project about a Windows Disk Cleanup component that would cleanup VC6 files.  I had always meant to look into that for VS2005 when I had the time.

Well, I had a look at the interfaces that the the Disk Cleanup applet uses and it turns out you can add cleanup items to Disk Cleanup without writing a single line of code.  There is a “fallback” cleanup COM component (DataDrivenCleaner) that you can reuse for situations where cleanup can be defined by a directory and/or set of files/wild-cards.  Visual Studio 2005 fits this criteria quite nicely; so I whipped together a few registry entries that adds an item to Disk Clean to cleanup select Visual Studio intermediate files older than 30 days.  I exported the entries into the attached file (zipped).  The contents are as follows:

Windows Registry Editor Version 5.00

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Visual Studio 2005]

@=“{C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}”

“FileList”=“*.obj|*.pch|*.ilk|vc80.pdb|vc80.idb|buildlog.htm”

“Display”=“Visual Studio 2005 Intermediate Files.”

“Priority”=dword:00000064

“Description”=“Clean up Visual Studio project build files older than 30 days.”

“IconPath”=hex(2):65,00,78,00,70,00,6c,00,6f,00,72,00,65,00,72,00,2e,00,65,00,\

  78,00,65,00,2c,00,31,00,33,00,00,00

“Flags”=dword:00000001

“Folder”=hex(2):25,00,55,00,53,00,45,00,52,00,50,00,52,00,4f,00,46,00,49,00,4c,\

  00,45,00,25,00,5c,00,4d,00,79,00,20,00,44,00,6f,00,63,00,75,00,6d,00,65,00,\

  6e,00,74,00,73,00,5c,00,56,00,69,00,73,00,75,00,61,00,6c,00,20,00,53,00,74,\

  00,75,00,64,00,69,00,6f,00,20,00,32,00,30,00,30,00,35,00,5c,00,50,00,72,00,\

  6f,00,6a,00,65,00,63,00,74,00,73,00,5c,00,00,00

“LastAccess”=dword:0000001e


Most items are pretty self-explanatory.  Both IconPath and Folder are REG_EXPAND_SZ value types, so the export shows them as hex bytes here.  When merged with the registry you’ll see IconPath: “explorer.exe,13″, which just sets the item’s icon to the folder icon; and Folder: “%USERPROFILE%\My Documents\Visual Studio 2005\Projects\”.  Flags is set to 1 to recurse subdirectories.  And LastAccess = 1e means only files older than 30 (hex 0x1e) days will be cleaned.


So, basically, this will recurse the Visual Studio projects directory deleting files matching the FileList wild-cards (in this case *.obj, *.pch, *.ilk, vc80.pdb, vc80.idb, and buildlog.htm) and are older than 30 days.  I purposely didn’t add *.exe *.pdb because I often have files of these times that aren’t with the Debug or Release directory of a project.


Handy if you have a scheduled cleanup (e.g. you run cleanup a hour before you disk defrag–no point in defragging a bunch of intermediate files…).