如何在 C# < 2.0 中误捕获非 CLS 异常?

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

提问人:Dariusz Woźniak 提问时间:11/4/2023 最后编辑:Dariusz Woźniak 更新时间:11/4/2023 访问量:47

问:

问题如下。基于 CLR via C#(第 4 版):

CLR 的所有编程语言都必须支持引发异常派生对象,因为公共语言规范 (CLS) 强制要求这样做。但是,CLR 实际上允许引发任何类型的实例,并且某些编程语言将允许代码引发不符合 CLS 的异常对象,例如 String、Int32 或 DateTime。 C# 编译器只允许代码引发 Exception 派生对象,而用其他语言编写的代码允许代码抛出 Exception 派生对象以及不是派生自 Exception 的对象。 [...] 尽管 C# 编译器仅允许开发人员抛出异常派生对象,但在 C# 2.0 之前,C# 编译器确实允许开发人员使用此类代码捕获不符合 CLS 的异常。

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
 }
}

你有没有任何工作的例子,可以误捕并直接进入?catch(Exception)catch

这很有趣。我还没有看到这种行为的任何工作示例,所以我试图重现它。我在 VirtualBox 上安装了 Win XP,然后安装了 .NET Framework 1.1.x、Visual Studio 2003(哇,真是一段怀旧的旅程:)!还安装了 Winamp,Heroes of the...好的,回到问题。

我创建了 C++ 类,编译为 DLL。.cpp 文件为:

#include <exception>

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

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

我用 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

现在,我想在 C# 下运行它并误捕获异常:

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();
    }
}

现在,输出为:

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

另外,将显示消息框:

enter image description here

当我隐含将 DllImport CallingConvention 设置为时,我会得到不同的结果:CallingConvention.FastCall

enter image description here

尽管我在 C# < 2.0 版本中抛出非 CLS 错误,但我不会误会。catch(Exception)

注意 当我删除该行时,代码显示“1”,因此它与 DLL 互操作并获得正确的结果。throw 42

注 2 版本表示 CLR 版本,而不是消息中指示的 C# 版本。但是,我假设 C# 版本是 1.1 或 1.2(基于维基百科文章)。

你知道在这种情况下如何误捕吗?catch(Exception

C# 异常 try-catch 互操作

评论

0赞 shingo 11/4/2023
我认为这本书是在谈论 IL 中的投掷指令,而不是 C/C++,learn.microsoft.com/en-us/visualstudio/code-quality/ca2102
0赞 Dariusz Woźniak 11/4/2023
@shingo:因此,抛出非 CLS 异常的唯一方法是 IL?这意味着,没有真实世界的场景可以抛出(例如,通过非托管代码)?
0赞 shingo 11/4/2023
不确定,也许您可以尝试在托管类中抛出一个数字,而不是使用 p/invoke。

答:

0赞 Dariusz Woźniak 11/4/2023 #1

多亏了@shingo,链接中的示例正在工作。

.il 文件:

.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 文件:

// 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 汇编程序和 C# 编译器命令:

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

和。。。在 .NET Framework 1.x 上错过了(预期行为):catch(Exception)

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

enter image description here

在 Windows 11 和 CLR 4.x 上受到打击(预期行为):catch(Exception)

CLR version: v4.0.30319
CLS compliant exception caught