从C#调用Fortran的AccessViolationException

时间:2013-03-02 08:59:08

标签: c# fortran pinvoke marshalling access-violation

我正在尝试包装glmnet库(http://cran.r-project.org/web/packages/glmnet/index.html),这样我就可以在C#中解决 models 稀疏通用线性模型。但是,原始函数有20个参数,所以我开始使用一个很小的子例程来测试如何传递数据(Fortran全新)。不幸的是我总是得到一个AccessViolationException。

以下是代码:

Fortran子程序。我使用带有Rtools(http://cran.r-project.org/bin/windows/Rtools/)的gfortran编译器将其编译成dll,使用-m64选项(是的,64位是必需的,因为我处理了相当大的数据块)。是的,使用i可能导致越界......但这只是为了测试。

subroutine testaaa  (f,i,fa,ia)
real fa(i)                                                      
integer ia(i)
ia(1) = 1337
ia(i) = 666
fa(1) = 8.15
fa(i) = 333
end subroutine testaaa

C#PInvoke代码:

[DllImport("ftest.dll", EntryPoint = "testaaa_", CallingConvention = CallingConvention.StdCall)]
public static extern void Test(
    [MarshalAs(UnmanagedType.R4)] float f,
    [MarshalAs(UnmanagedType.I4)] int i,
    IntPtr fa,
    IntPtr ia);

以下是它的名称:

var fa = new float[4];
var ia = new int[4];
IntPtr faPtr = Marshal.AllocHGlobal(fa.Length * sizeof(float));
Marshal.Copy(fa, 0, faPtr, fa.Length);

IntPtr iaPtr = Marshal.AllocHGlobal(ia.Length * sizeof(float));
Marshal.Copy(ia, 0, iaPtr, ia.Length);

GlmnetDllWrapper.Test(0.4f, 4,faPtr,iaPtr);

我也试过直接传递数组并给它们[MarshalAs(UnmanagedType.LPArray)]属性。没有什么对我有用。

你有什么建议从哪里开始或改变什么?

更新1: 即使只传递float和int也会导致异常:

subroutine testbbb  (f,i)
i = 815
f = 8.15
return
end subroutine testbbb

C#Pinvoke和呼叫会相应更改。我做错了什么?

2 个答案:

答案 0 :(得分:2)

主要问题是您的Fortran库期望标量参数通过引用传递。所以你需要声明你的p / invoke匹配。

数组参数可以非常简单地作为数组传递,p / invoke marshaller会为你固定它们。

所以,你的p / invoke声明应该是这样的:

[DllImport("ftest.dll", EntryPoint = "testaaa_")]
public static extern void Test(
    ref float f,
    ref int i,
    [In, Out] float[] fa,
    [In, Out] int[] ia
);

您可以调整[In, Out]属性以满足您的需求。

答案 1 :(得分:-1)

请看一下 http://msdn.microsoft.com/en-en/library/chfa2zb8%28v=VS.80%29.aspx(不安全的代码) 并使用您的项目设置项目/属性/构建:允许不安全的代码。 但要注意后果。 :)

更新:不要“玩弄” - 我的意思是:“查看”不安全“功能”。 “不安全”并不意味着“危险”。