Search code examples
c#exceptiontry-catchinterop

How can I miscatch non-CLS exception in C# < 2.0?


The question is as following. Basing on CLR via C# (4th ed.):

All programming languages for the CLR must support the throwing of Exception-derived objects because the Common Language Specification (CLS) mandates this. However, the CLR actually allows an instance of any type to be thrown, and some programming languages will allow code to throw non–CLS-compliant exception objects such as a String, Int32, or DateTime. The C# compiler allows code to throw only Exception-derived objects, whereas code written in some other languages allow code to throw Exception-derived objects in addition to objects that are not derived from Exception. [...] Although the C# compiler allows developers to throw Exception-derived objects only, prior to C# 2.0, the C# compiler did allow developers to catch non–CLS-compliant exceptions by using code like this.

private void SomeMethod() {
 try {
  // Put code requiring graceful recovery and/or cleanup operations here...
}
catch (Exception e) {
 // Before C# 2.0, this block catches CLS­-compliant exceptions only
 // Now, this block catches CLS­-compliant & non–CLS­-compliant exceptions
 throw; // Re­throws whatever got caught
}
catch {
 // In all versions of C#, this block catches CLS­-compliant
 // non–CLS­-compliant exceptions
 throw; // Re­throws whatever got caught
 }
}

Do you have any working example that miscatch catch(Exception) and goes directly to the catch?

This is very interesting. I haven't seen any working example of that behaviour, so I tried to reproduce that. I installed Win XP on VirtualBox and then installed .NET Framework 1.1.x, Visual Studio 2003 (wow, such a nostalgic journey! :)). Also installed Winamp, Heroes of the... ok, back to the problem.

I created the C++ class, compiled to the DLL. The .cpp file is:

#include <exception>

class MyClass {
public:
    int doSomething() {
        throw 42;
        return 1;
        
    }
};

extern "C" __declspec(dllexport) int DoSomethingWrapper() {
    MyClass myObject;
    return myObject.doSomething();
}

I compile it with the g++ (i686-4.9.3-release-win32-sjlj-rt_v4-rev1):

"z:\mingw32\bin\g++.exe" -shared -o "z:\tc.dll" "z:\tc.cpp" -static-libstdc++ -static -static-libgcc

Now, I want to run it under C# and miscatch the exception:

class Class1
{
    [DllImport("tc.dll")]
    public static extern int DoSomethingWrapper();

    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("C# version: " + typeof(string).Assembly.ImageRuntimeVersion);

        try
        {
            int i = DoSomethingWrapper();
            Console.WriteLine(i);
        }
        catch(Exception) 
        {
            Console.WriteLine("e");
        }
        catch
        {
            Console.WriteLine("empty catch");
        }

        Console.WriteLine("Final");
        
        Console.Read();
    }
}

Now, the output is:

C# version: v1.1.4322
terminate called after throwing an instance of 'int'

Plus, message box appears:

enter image description here

When I implicity set DllImport CallingConvention to the CallingConvention.FastCall then I get different results:

enter image description here

I cannot miscatch catch(Exception) despite I'm throwing non-CLS error in C# < 2.0 version.

NOTE When I remove the throw 42 line, the code displays "1", so it interops with the DLL and gets a proper result.

NOTE 2 The version indicates CLR version, not the C# version as the message indicates. However, I assume the C# version is either 1.1 or 1.2 (basing on the Wikipedia article).

Do you have any idea how to miscatch catch(Exception in that case?


Solution

  • Thanks to @shingo, the example from the link is working.

    .il file:

    .assembly ThrowNonClsCompliantException {}
    .class public auto ansi beforefieldinit ThrowsExceptions
    {
       .method public hidebysig static void
             ThrowNonClsException() cil managed
       {
          .maxstack  1
          IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
          IL_0005:  throw
       }
    }
    

    .cs file:

    // CatchNonClsCompliantException.cs
    using System;
    
    namespace SecurityLibrary
    {
       class HandlesExceptions
       {
          void CatchAllExceptions()
          {
             try
             {
                ThrowsExceptions.ThrowNonClsException();
             }
             catch(Exception e)
             {
                // Remove some permission.
                Console.WriteLine("CLS compliant exception caught");
             }
             catch
             {
                // Remove the same permission as above.
                Console.WriteLine("Non-CLS compliant exception caught.");
             }
          }
    
          static void Main()
          {
             HandlesExceptions handleExceptions = new HandlesExceptions();
             handleExceptions.CatchAllExceptions();
          }
       }
    }
    

    IL assembler and C# compiler commands:

    ilasm /dll ThrowNonClsCompliantException.il
    csc /r:ThrowNonClsCompliantException.dll CatchNonClsCompliantException.cs
    

    And... the catch(Exception) is missed (expected behavior) on the .NET Framework 1.x:

    CLR version: v1.1.4322
    Non-CLS compliant exception caught
    

    enter image description here

    On Windows 11 and CLR 4.x catch(Exception) is hit (expected behavior):

    CLR version: v4.0.30319
    CLS compliant exception caught