Debug vs Release mode differences when storing a delegate in a hashtable

The following code demonstrates a difference in the behavior of the .NET runtime between debug and release mode.  I'm running .NET 1.1 with SP1 on Server 2003, but this also happens on Win2k and XP.  We've been seeing it for months, but it was finally isolated by a team in Samara.

The code demonstrates three ways to test if a delegate is stored in the hashtable.  All work in debug mode, but the first fails in Release mode unless the Release mode code is running under a debugger. 

Does anyone have experience with this problem?  Or know if a fix is available already or coming in 2.0?

using System;

using System.Collections;

 

namespace DelegatesBug

{

      /// <summary>

      /// Illustrates a difference in the Release and Debug mode behavior of delegates that are

      /// contained in a hashtable. 

      /// Creates a delegate around a method and adds the delegate to a hashtable. 

      /// At that point, asking the hashtable if it contains a new delegate around the same

      /// method returns true in debug mode and false in release mode.  Running the release

      /// mode in the debugger returns true (the same result as in debug mode)

      /// </summary>

      class DelegatesBug

      {          

            [STAThread]

            static void Main(string[] args)

            {

                  Test test = new Test();

                  Hashtable hashtable = new Hashtable();

                  TestDelegate td = new TestDelegate(test.Method);

                  hashtable.Add(td, null);

 

                  td(); // if this call is commented out, release and debug modes work the same

 

                  // the Contains call returns false in release mode with no debugger (Ctrl + F5). With

                  // debug mode or the debugger enabled, it returns true.

                  Console.WriteLine("New delegate is contained in hashtable: " + hashtable.Contains(new TestDelegate(test.Method)));

 

                  // comparing the two delegates returns true

                  foreach (MulticastDelegate del in hashtable.Keys)

                        Console.WriteLine("The delegate from hashtable.Keys equals td: " + del.Equals(td));

 

                  // asking the hashtable if it contains the original delegate returns true

                  Console.WriteLine("td is contained in the hashtable: " + hashtable.Contains(td));

                  Console.ReadLine();               

            }

      }

 

      public delegate void TestDelegate();

      public class Test

      {

            public void Method()

            {          

            }

      }

}

 

Executing custom code on attributed classes at compile time
Eoghan Murray read my blog on custom attributes and sent the following question:
 
I am interested in doing the following:  Adding an attribute to certain classes, so that, at Compile time, information about them gets written to an xml file. I see that the built in System.ObsoleteAttribute can raise warnings at compile time, so I want to do something similar, except execute a piece of my own code at compile time.

Now that I've spelled it out, it seems impossible!
 
It is an interesting question.  I don't think it is possible to execute the code at compile time.  If anyone knows how, please explain.
 
What you can do is build a custom attribute and mark your classes with it.  Then, have a post-build step that executes a program against the compiled assemblies that uses reflection to find the classes marked with your attribute and execute whatever code you want. 
 
For example, you create a custom attribute called DocumentThis.  You mark all of your classes with that attribute, then create a small Documentor program that reflects over a .NET assembly and writes the name of each class that has the DocumentThis attribute to a text file.  Then your post-build step for the project can call Documentor with the project's output as the target.
 
That's the best I can think of, let me know how it works.
Could not establish trust relationship with remote server.

When you use HTTPS for your web services, you may get a System.Net.WebException that, "The underlying connection was closed: Could not establish trust relationship with remote server." This indicates that the client is unable to negotiate a secure connection with the server.

Try visiting the URL of your web service with IE. You will likely get a Security Alert message box warning about one or more of the following:

1) The certificate is not from a trusted authority. This happens if the issuing authority is not trusted by the Certificate Manager. For testing, you can issue your own certificates and add yourself to the trusted authorities list. For production, you should probably buy a certificate.

2) The date on the certificate is invalid. The certificate's dates don't match those on the client computer. If this happens only on some computers, check that the clock on the offending computers is set to the right day.

3) The name on the certificate does not match the name of the site. Most certificates are issued with a www prefix; for example: www.yahoo.com. If your web service is hosted on a named server, (daffy.yahoo.com) you will get this warning. I understand you can buy wildcard certificates that accept any server name, but I've never used them.

Once you know the problem, you can decide to fix it, or ignore it when establishing the connection. The following class shows how to selectively ignore any CertificateProblems that you choose. Use this carefully, as establishing a secure connection to an attacker's server is worse than sending data in the clear.

using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace Graham.Utilities
{
        public class AcceptServerNameMismatch : ICertificatePolicy
        {
                // HACK: This is a workaround.  The .NET Framwork should expose these, but they don't.
                public enum CertificateProblem : long
                {
                        CertEXPIRED                   = 2148204801,
                        CertVALIDITYPERIODNESTING     = 2148204802,
                        CertROLE                      = 2148204803,
                        CertPATHLENCONST              = 2148204804,
                        CertCRITICAL                  = 2148204805,
                        CertPURPOSE                   = 2148204806,
                        CertISSUERCHAINING            = 2148204807,
                        CertMALFORMED                 = 2148204808,
                        CertUNTRUSTEDROOT             = 2148204809,
                        CertCHAINING                  = 2148204810,
                        CertREVOKED                   = 2148204812,
                        CertUNTRUSTEDTESTROOT         = 2148204813,
                        CertREVOCATION_FAILURE        = 2148204814,
                        CertCN_NO_MATCH               = 2148204815,
                        CertWRONG_USAGE               = 2148204816,
                        CertUNTRUSTEDCA               = 2148204818
                }

                /// <summary>
                /// Implement CheckValidationResult to ignore problems that we are willing to accept.
                /// </summary>
                public bool CheckValidationResult(ServicePoint sp, X509Certificate cert,
                        WebRequest request, int problem)
                {       
                        int CertificateNameDoesntMatch = unchecked( (int) CertificateProblem.CertCN_NO_MATCH);
                        if ( problem == CertificateNameDoesntMatch ) // only accept server name failed match
                                return true;

                        // The 1.1 framework calls this method with a problem of 0, even if nothing is wrong
                        return (problem == 0);         
                } 
        }
}

Determine if a .NET Assembly is Release or Debug

We recently got a bug report where a tester got a Debug.Assert failure in what I thought was a Release mode build. I went over to her machine, but found I couldn't tell from the binaries if it was a Release or a Debug build. ILDASM is no help, as Debug and Release are just build modes, and don't leave markers in the assemblies.

To differentiate between Release and Debug builds, I added the following to my AssemblyInfo.cs

// Compile a Debug or Release flag into the assembly.

#if DEBUG

[assembly: AssemblyDescription("Debug")]

#else

[assembly: AssemblyDescription("Release")]

#endif

 

This places Debug or Release in the file's metadata, where it can be seen under Properties | Version | Comments.

ExecutionEngineException explained
 
Last week several of our developers, myself included, experienced repeatable crashes of our Windows Forms application with an ExecutionEngineException.  MSDN describes it as "The exception that is thrown when there is an internal error in the execution engine of the common language runtime.", which didn't help us to track it down.  The debugger was useless, as ExecutionEngineException cannot be caught and the callstack was pointing into a Application Block that hadn't changed. 
 
Our problem turned out to be old versions of some of our assemblies in a folder that was in the DEVPATH environment variable.  Our nAnt build uses the resgen tool, which requires setting the DEVPATH variable.  Unfortunately, DEVPATH breaks the normal .NET loading rules.  DEVPATH assemblies are bound to at runtime, ignoring the assembly version, and overriding the GAC.  (http://blogs.msdn.com/suzcook/archive/2003/08/15/57238.aspx)
 
Lessons learned:
  1. Don't use DEVPATH - See Suzanne Cook's linked blog above for all the reasons why
  2. One cause of ExecutionEngineException is mixing assemblies at runtime that weren't compiled together on the last compile.
  3. If only some developers or users are experiencing a problem, don't write it off as unreproducible.  Finding the difference that causes the problem will lead you to the solution.

 

Checking if an application is already running
Many Windows Forms applications only allow a single instance to run at a time. The following snippet is a clean way to check if your process is already running:

static public bool AlreadyRunning()
{
    string processName = Process.GetCurrentProcess().ProcessName;
    Process[] processes = Process.GetProcessesByName(processName);
    return (processes.Length > 1);
}
No way to add custom performance counters to existing custom categories...?

According to http://www.devcity.net/net/article.aspx?alias=perf_counters, “You cannot create new counters within existing custom categories. If you need to add counters to categories that already exist, the only way you can do so is to delete the category and recreate it with all of its contents, including the new counters you want to add.”

This is nasty, as it prevents lazy instantiation of performance counters.  During development, when new counters are being frequently added, the application has to delete any custom categories on startup and recreate all the counters.  During production, the application could usually get away with reusing the counters, but that leads to complex verification scenarios.  The only safe way to do this appears to be deleting and recreating the entire set of counters on startup.

Anyone found a workaround?

Implementing Equals in C#

While working on refactorings, I often notice domain objects that implement Equals incorrectly.  Below is a sample implementation of Equals.  The key points are that Equals should not throw an exception if obj is null or of the wrong type.  Also note the different patterns for comparing reference and value members.  See pages 154-160 of Richter's excellent Applied Microsoft .NET Framework Programming for more details, including different patterns for reference or value objects:

public override bool Equals(object obj)
{
    if (obj == null) return false;

    if (this.GetType() != obj.GetType()) return false;

    // safe because of the GetType check
    Customer cust = (Customer) obj;     

    // use this pattern to compare reference members
    if (!Object.Equals(Name, cust.Name)) return false;

    // use this pattern to compare value members
    if (!Age.Equals(cust.Age)) return false;

    return true;
}

Implementing IDisposable
Classes that need explicit destruction semantics or wrap managed resource usually implement IDisposable to allow for predictable destruction outside of garbage collection.  As I was just reminded by a defect in my code, all classes that implement IDisposable must provide a finalizer (C# uses destructor syntax: ~MyClass) to handle freeing resources when Dispose is not called.  Additionally, the Dispose method should call should call the GC.SuppressFinalize method for the object it is disposing.  From MSDN, "If the object is currently on the finalization queue, GC.SuppressFinalize prevents its Finalize method from being called."
 
 
 
Debug only methods in C#/.NET
 
In Steve Maguire's Writing Solid Code, he encourages writing Debug only code that double checks complex logic.  For example, Excel uses a highly optimized evaluation method.  In the Debug build, there is another method that evaluates the same arguments and the two results are compared to ensure that the optimizations haven't broken the intent of the code.
 
Writing Debug only code is a powerful technique, but can break the Release mode build.  For example, debug only methods are often defined as follows:
#ifdef DEBUG
private void DoDebugChecks()    {}
#endif
 
Then each call to DoDebugChecks needs to be wrapped in #ifdef blocks.  Forget one, and the release mode build breaks.  C# offers a better solution: the Conditional attribute.
 
[Conditional("DEBUG")]
private void DoDebugChecks()     {}
 
This indicates to the compiler that DoDebugChecks is only executed in builds where DEBUG is true.  The compiler automatically compiles out all references during the build.  Two comments:
  1. I wish the .NET team had provided a [DebugOnly] attribute that gives the same functionality.  I'm a stickler for compile time checks, and so I worry about someone mistyping DEBUG and creating a very hard to find defect.
  2. Debug only methods are great, but make sure they do not alter the state of the application.  Just like in Debug.Asserts, make sure there aren't any side effects, or the release build will behave differently than expected.
More Posts « Previous page - Next page »