2013-03-02 3 views
0

Я пытаюсь обернуть библиотеку glmnet (http://cran.r-project.org/web/packages/glmnet/index.html), поэтому я могу решить модели редкие общие линейные модели в C#. Однако исходная функция имеет несколько 20 параметров, поэтому я начал (совершенно новый для Fortran) с крошечной подпрограммой для тестирования того, как передавать данные. К сожалению, я всегда получаю AccessViolationException.AccessViolationException вызов Fortran из C#

Вот код:

Фортран подпрограммой. Я скомпилирую его в dll, используя компилятор gfortran, который поставляется с Rtools (http://cran.r-project.org/bin/windows/Rtools/), используя опцию -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)]. Ничто не сработало для меня.

Есть ли у вас какие-либо предложения, с чего начать или что изменить?

Update 1: Даже прохождение только поплавка и ИНТ уже вызывает исключение:

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

C# PInvoke и вызова изменяется соответственно. Что я делаю не так?

+1

Возможно ли, что ваша библиотека Fortan получает параметры указателем? Кроме того, почему вы все тестируете сразу. Почему вы не упростили? Можете ли вы передать один параметр типа 'int'? Тип 'float'? Затем попробуйте массив. Не проверяйте сначала самую сложную вещь. Как вы можете сказать, какая часть терпит неудачу? –

+2

ОК, ваше обновление и мой комментарий перешли. Хорошо сделано для упрощения. Попробуйте передать в качестве указателей: 'public static extern void Test (ref float f, ref int i)' –

+0

Как только вы пройдете блокировку на простых типах, мы можем показать вам, как делать массивы. И нет необходимости в 'IntPtr'.Мы можем заставить маркерщика pinvoke подключить массивы и сделать код вызова очень простым. –

ответ

2

Основная проблема заключается в том, что ваша библиотека Fortran ожидает, что скалярные параметры будут передаваться по ссылке. Поэтому вам нужно объявить ваш p ​​/ invoke для соответствия.

Параметры массива могут передаваться достаточно просто, как массивы, и маршаллер 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] атрибуты для удовлетворения ваших потребностей.

+0

Вы должны вернуть CalingConvention обратно, Fortran почти всегда stdcall. Подробнее о gfortran [здесь] (http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gfortran/Interoperability-with-C.html#Interoperability-with-C) –

+0

@HansPassant 'StdCall' является значением по умолчанию поэтому я удалил его. Или я неверно истолковал вас? –

+0

Да, ты прав, я пошарил, извини. Ссылка, которую я оставил, должна указывать OP, что вызывает соглашение, и требуется ли * ref * из исходного кода Fortran. Операторы VALUE и USE имеют значение. –

-1

Пожалуйста, взгляните на http://msdn.microsoft.com/en-en/library/chfa2zb8%28v=VS.80%29.aspx (небезопасный код) и поиграйте со своими настройками проекта. Проект/Свойства/Сборка: разрешить небезопасный код. Но имейте в виду последствия. :)

Обновление: не «играть» - я имел в виду: «Проверьте« небезопасные »функции». «Небезопасный» не означает «опасный».

+1

Нет, пожалуйста, не делайте этого. Нет необходимости в небезопасном коде. И «играть со своими настройками» не дает ответа. –

+0

Да, извините. Я имел в виду: «Попробуйте обернуть область с помощью небезопасного кода и разрешить небезопасный код один раз», чтобы узнать, находится ли там проблема. Это способ обработки указателей, особенно при работе с ext. не-net, если «ref» не работает. Но ты прав. Это не нужно, потому что работает простой «ref». – Michael

Смежные вопросы