2016-12-05 7 views
2

Я использовать Fortran DLL, которая содержит тип со следующими сигнатуру (упрощенно):Работа с Fortran типа, содержащего указатель из управляемого кода

TYPE MyType 
    INTEGER(4) :: ii 
    REAL(8) :: rr 
    INTEGER(4) :: n_a0 
    INTEGER(4) :: n_a1 
    INTEGER(4), POINTER :: a0(:) 
    REAL(8) , POINTER :: a1(:) 
    END TYPE 

Очевидно, что этот тип содержит указатели на целые числа и парном разряде. Я обращаюсь к этому типу с помощью следующего метода из другого кода Fortran плавно.

SUBROUTINE MySub(x) 
TYPE(MyType) :: x 

Моя цель состоит в том, чтобы работать с этим MyType и MySub через C# код. Для этого, я определил структуру в моем C# кода следующим образом:

struct MyType 
{ 
    public int ii; 
    public double rr; 

    public int n_a0; 
    public int n_a1; 

    public int[] a0; 
    public double[] a1; 
} 

и доступ к нему со следующим способом:

[DllImport("my_test.dll", CallingConvention = CallingConvention.Cdecl)] 
    public static extern void MySub(ref MyType t); 

Проблема заключается в том, когда MySub доступ пользователей без массива все в порядке , Но когда он пытается получить доступ к элементам массива, я получаю AccessViolationException. Что мне делать, чтобы решить эту проблему?

+0

Я также попытался изменить тип 'a0' и' a1' на 'IntPtr' и выделить их с помощью' Marshal.AllocHGlobal'. Но в этом случае я получил то же исключение. – melmi

ответ

6

Указатели Fortran не являются указателями C. Они не совместимы с C-указателями.

Хотя указатели Fortran для скаляров часто являются просто адресами под капотом, это не так для указателей массива. Указатели массива используют дескрипторы массива, поскольку массивы могут быть несмежными, а указатель также содержит информацию о нижней и верхней границах.

Вы должны создать другой тип Fortran, который равен bind(C) и который содержит C-указатели как type(c_ptr) (определенный в встроенном модуле iso_c_binding).

Затем вы должны создать преобразование между типом оригинала и новым типом и установить указатели С помощью

c_interoperable_type%c_a0 = c_loc(old_type%a0(1)) 

Я использую первый элемент здесь, потому что массив не может быть совместим. Однако вы должны убедиться, что массив просто смежный.


Если вы действительно не можете изменить код Fortran, у вас возникли проблемы. Конечно, можно узнать, где находится адрес массива в дескрипторе массива и использовать его непосредственно из C. Однако я не могу его рекомендовать, он будет специфичным для компилятора и, возможно, даже для конкретной версии.