为啥包含枚举的 C++ 方法会导致 SWIG/C# 中的 AccessViolationExceptions?

Posted

技术标签:

【中文标题】为啥包含枚举的 C++ 方法会导致 SWIG/C# 中的 AccessViolationExceptions?【英文标题】:why are c++ methods containing enums causing AccessViolationExceptions in SWIG/C#?为什么包含枚举的 C++ 方法会导致 SWIG/C# 中的 AccessViolationExceptions? 【发布时间】:2015-03-31 16:39:06 【问题描述】:

我正在使用 SWIG 生成原生 32 位 c++ dll 的包装器。 SWIG 生成一个 C++ 包装文件和大量生成的 C# 代码,这些代码被编译成一个 dll(C++/CLI 和 C# 项目都构建为 x86),并且生成的函数可以通过 C# 很好地调用,但包含枚举的函数除外。一个例子:

SWIG 生成的 interface_wrap.cxx 文件:

SWIGEXPORT int SWIGSTDCALL CSharp_myMethod(long jarg1, long jarg2, void * jarg3, void * jarg4) 
int jresult ;
long arg1 ;
long arg2 ;
myEnum arg3 ;
double *arg4 = 0 ;
myEnum const *argp3 ;
int result;

arg1 = (long)jarg1; 
arg2 = (long)jarg2; 
argp3 = (myEnum *)jarg3; 
if (!argp3) 
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null myEnum const", 0);
return 0;

arg3 = *argp3; 
arg4 = (double *)jarg4;
if (!arg4) 
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "double & type is null", 0);
return 0;
 
result = (int)myMethod(arg1,arg2,arg3,*arg4);
jresult = result; 
return jresult;

C#外部函数委托定义:

[global::System.Runtime.InteropServices.DllImport("ProjectWrapper", EntryPoint="CSharp_myMethod")]
public static extern int myMethod(int jarg1, int jarg2, [MarshalAs(UnmanagedType.U4)]myEnum jarg3, out double jarg4);

Interface.i 文件提取:

%module ProjectWrapper
%
#include "myEnumDefinition.h"
%

%include "enums.swg"

// %typemap(csbase) myEnum "short" // is something like this needed??

%typemap(cstype, out="myEnum") myEnum&, const myEnum& "ref myEnum"
%typemap(cstype, out="myEnum") myEnum, const myEnum "myEnum"
%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.U4)]", outattributes="[return: MarshalAs(UnmanagedType.U4)]", out="myEnum") myEnum&, const myEnum& "ref myEnum"
%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.U4)]", outattributes="[return: MarshalAs(UnmanagedType.U4)]") myEnum, const myEnum "myEnum"
%typemap(csin) myEnum&, const myEnum& "ref $csinput"
%typemap(csin) myEnum, const myEnum "$csinput"

int myMethod(const long start,const long end,const myEnum enumvalue, double& result);

枚举的C#定义:

public enum myEnum 
  myEnum_value1,
  myEnum_value2,
  myEnum_value3

枚举的 C++ 定义(来自 myEnumDefinition.h)

enum myEnum

  myEnum_value1,
  myEnum_value2,
  myEnum_value3
;

当它调用 C# myMethod extern 委托时,它每次都会抛出 AccessViolationException。如果我尝试在没有任何枚举作为参数的情况下调用其他方法,它工作正常。

我不明白这里有什么问题。我尝试以不同的方式MarshalAs 枚举,但我无法避免抛出此异常。生成包装器时,我是否遗漏了 SWIG 接口文件中的某些内容?

【问题讨论】:

swig.org/Doc1.3/CSharp.html 项目符号的项目都在谈论它。 请您更具体地说明哪些要点? 我认为这是您需要使用 %exception 的 18.4.2 C# 异常示例的部分 感谢您的帮助,但担心我看不到您指向我的部分的相关性。接受的答案反而解决了我的问题。 【参考方案1】:

根据这一行,看起来 Swig 期待一个指向枚举值的指针:

argp3 = (myEnum *)jarg3;

看起来 C++ 方法在其第三个参数中期望 myEnum* 值。因此,您可能应该将枚举参数作为 ref 传递:

public static extern int myMethod(int jarg1, int jarg2, ref myEnum jarg3, out double jarg4);

【讨论】:

非常感谢,这确实是问题所在,通过 ref 传递枚举解决了它!万一其他人遇到这种情况,我尝试在 SWIG 中为我的枚举使用 %val 关键字,但这不起作用并且可能已被弃用。最后,我将%typemap(imtype) 行更改为"ref myEnum",这通过引用传递了枚举,但其他所有内容保持不变。

以上是关于为啥包含枚举的 C++ 方法会导致 SWIG/C# 中的 AccessViolationExceptions?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C++ 中的以下结构声明会导致退出 127?

为啥 MPI_Barrier 在 C++ 中会导致分段错误

为啥 C++ 允许通过指针访问枚举?

将新对话框添加到旧版 C++ 应用程序 (VS2017) 会导致许多错误。为啥?

为啥只能在 C++ 类中初始化整数或枚举类型?

为啥 semget() 在 *创建* 信号量时会导致 EACCES 错误?