0

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

这是代码:

Fortran 子例程。我使用 Rtools 附带的 gfortran 编译器(http://cran.r-project.org/bin/windows/Rtools/),使用 -m64 选项将它编译成 dll(是的,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 和 call 相应更改。我究竟做错了什么?

4

2 回答 2

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]属性以满足您的需求。

于 2013-03-02T09:49:51.490 回答
-1

请查看 http://msdn.microsoft.com/en-en/library/chfa2zb8%28v=VS.80%29.aspx(不安全代码)并使用您的项目设置 Project/Properties/Build:allow不安全的代码。但要注意后果。:)

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

于 2013-03-02T09:31:56.127 回答