C#与C++交互开发系列(十五):错误处理与异常传递

在这里插入图片描述

前言

在跨语言开发中,C++ 与 C# 的错误处理机制存在较大差异:C++ 使用传统的错误代码、异常(try/catch)和 HRESULT 等方式,而 C# 主要依赖于 try/catch 异常处理。因此,在 C# 与 C++ 的互操作中,如何优雅地传递和处理异常成为了一个重要课题。本文将介绍几种常用的跨语言错误处理方案,帮助实现高效稳定的异常传递。

1. 使用错误码进行异常传递

这是最简单的错误处理方式,通过返回特定的错误码,C# 可以检查调用是否成功。通常使用整数 int 类型来表示操作的成功或失败,例如 0 表示成功,非零值表示错误。

C++ 代码示例

C++ 中的函数返回错误码,并且可以将错误信息作为输出参数传递。

// MyNativeLib.cpp
#include <string>

extern "C" __declspec(dllexport)
int PerformOperation(int input, char* errorMsg, int errorMsgSize) {
    
    
    if (input < 0) {
    
    
        std::string error = "Input cannot be negative.";
        if (error.size() < errorMsgSize) {
    
    
            strncpy(errorMsg, error.c_str(), error.size() + 1);
        }
        return -1; // 返回错误码
    }
    return 0; // 操作成功
}

注意:在使用 strncpy 时会抛出异常警告

error C4996: ‘strncpy’: This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

这个时候,我们只需要在项目的预处理器定义中加入_CRT_SECURE_NO_WARNINGS消除警告即可
在这里插入图片描述

C# 代码示例

在 C# 中,可以检查返回的错误码,并根据需要读取错误信息。

using System;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int PerformOperation(int input, StringBuilder errorMsg, int errorMsgSize);

    static void Main()
    {
    
    
        int input = -5;
        StringBuilder errorMsg = new StringBuilder(256);

        int result = PerformOperation(input, errorMsg, errorMsg.Capacity);
        if (result != 0)
        {
    
    
            Console.WriteLine($"Error: {
      
      errorMsg}");
        }
        else
        {
    
    
            Console.WriteLine("Operation succeeded.");
        }
    }
}

执行结果

Error: Input cannot be negative.

2. 使用 HRESULT 错误码传递错误信息

在 Windows 环境中,HRESULT 是常见的错误表示方式。对于 Windows API 接口,使用 HRESULT 能够统一管理错误和状态。

C++ 代码示例

将操作的成功或失败通过 HRESULT 返回给 C#,并使用 SUCCEEDEDFAILED 宏检查结果。

// MyNativeLib.cpp
#include <windows.h>

extern "C" __declspec(dllexport)
HRESULT DoWork(int input) {
    
    
    if (input < 0) {
    
    
        return E_INVALIDARG; // 返回标准 HRESULT 错误码
    }
    return S_OK;
}

C# 代码示例

在 C# 中,通过 HRESULT 判断操作是否成功。可以利用 Marshal.GetExceptionForHRHRESULT 转换为 C# 的异常。

using System;
using System.Runtime.InteropServices;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int DoWork(int input);

    static void Main()
    {
    
    
        int result = DoWork(-1);
        if (result != 0)
        {
    
    
            Exception ex = Marshal.GetExceptionForHR(result);
            Console.WriteLine($"Error: {
      
      ex.Message}");
        }
        else
        {
    
    
            Console.WriteLine("Operation succeeded.");
        }
    }
}

执行结果:

Error: Value does not fall within the expected range.

3. 使用自定义异常类型

在某些情况下,可以在 C++ 中定义自定义异常类型,将错误详细信息包含在异常类中,然后在 C# 端处理这些异常。虽然不能直接将 C++ 异常抛到 C# 中,但可以通过错误码返回来模拟传递自定义异常信息。

C++ 代码示例

定义一个自定义异常类 CustomException 并封装异常信息。

#include <stdexcept>
#include <string>

class CustomException : public std::runtime_error {
    
    
public:
    explicit CustomException(const std::string& message)
        : std::runtime_error(message) {
    
    }
};

extern "C" __declspec(dllexport)
int DoTaskWithException(int input, char* errorMsg, int errorMsgSize) {
    
    
    try {
    
    
        if (input < 0) {
    
    
            throw CustomException("Input cannot be negative in DoTaskWithException.");
        }
        return 0;
    }
    catch (const CustomException& ex) {
    
    
        strncpy(errorMsg, ex.what(), errorMsgSize - 1);
        return -1;
    }
}

C# 代码示例

在 C# 中,通过检查错误码并读取错误信息的方式,处理 C++ 抛出的自定义异常。

using System;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int DoTaskWithException(int input, StringBuilder errorMsg, int errorMsgSize);

    static void Main()
    {
    
    
        StringBuilder errorMsg = new StringBuilder(256);
        int result = DoTaskWithException(-1, errorMsg, errorMsg.Capacity);

        if (result != 0)
        {
    
    
            Console.WriteLine($"Custom Exception Caught: {
      
      errorMsg}");
        }
        else
        {
    
    
            Console.WriteLine("Operation succeeded.");
        }
    }
}

执行结果

Custom Exception Caught: Input cannot be negative in DoTaskWithException.

4. 使用回调传递错误信息

对于复杂的跨语言交互,使用回调可以实现更加灵活的错误传递机制。C# 提供了委托的机制,可以将错误信息通过回调传递回 C# 层。

C++ 代码示例

通过函数指针将错误回调传递到 C++ 中,以便在出错时调用。

#include <iostream>
#include <string>

typedef void(*ErrorCallback)(const char*);

extern "C" __declspec(dllexport)
void PerformTaskWithCallback(int input, ErrorCallback errorCallback) {
    
    
    if (input < 0 && errorCallback != nullptr) {
    
    
        errorCallback("Input cannot be negative in PerformTaskWithCallback.");
    }
}

C# 代码示例

在 C# 中,将委托定义为 ErrorCallback 类型,并在调用 C++ 函数时传递回调,以捕获错误信息。

using System;
using System.Runtime.InteropServices;

class Program
{
    
    
    private delegate void ErrorCallback(string message);

    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void PerformTaskWithCallback(int input, ErrorCallback errorCallback);

    private static void HandleError(string message)
    {
    
    
        Console.WriteLine($"Error from C++: {
      
      message}");
    }

    static void Main()
    {
    
    
        PerformTaskWithCallback(-1, HandleError);
    }
}

执行结果

Error from C++: Input cannot be negative in PerformTaskWithCallback.

总结

C++ 和 C# 的异常机制存在显著差异,因此在跨语言交互中需要通过特定的手段来传递和处理错误。本文介绍了几种常用的错误传递方法:

  1. 使用错误码:简单直接,通过检查返回的整数值判断错误。
  2. 使用 HRESULT:在 Windows 环境中使用标准的错误代码。
  3. 自定义异常类型:在 C++ 中定义自定义异常,通过错误信息传递异常详情。
  4. 回调传递错误信息:使用回调机制将错误信息动态传递到 C#。

根据实际需求和项目复杂度,可以选择合适的错误传递方案,以确保 C++ 与 C# 之间的高效可靠的交互。

猜你喜欢

转载自blog.csdn.net/houbincarson/article/details/143276386