Я, наверное, смущаюсь, но раньше этого не делал, и немного было бы очень полезно.
Я пытаюсь вызвать код C
из приложения C#
. Я попытался использовать PInvoke
но PInvoke
что это немного сложно. Я думал, что попробую сделать C++\CLI
.
Существуют несколько сложных структур, которые имеют двойные массивы переменной длины, с которыми трудно было справиться с PInvoke
.
Я читал немного о том, как это делается, но я не могу понять это. Большая часть того, что я нашел, относится к обертке C++
вместо C
C
код уже экспортирует свои функции, которые работают уже с Java-приложения и его службы JNA
. У меня есть C
код, заголовки, библиотека и dll, но скорее бы не вносил изменений ни в что существующее, чтобы не нарушать другие потребляющие приложения. Приложение C#
вызывающее его, будет 64-битным, большинство примеров создадут библиотеки win32, не так ли?
UPDATE: добавление кода ниже:
ПРИМЕЧАНИЕ. Это всего лишь одна функция нескольких и, возможно, самая простая, но все они довольно схожи.
C HEADER:
typedef struct myStruct_t
{
double prefix[8];
int length;
double array[1];
}
myStruct;
C:
extern "C" __declspec( dllexport ) myStruct *doSomething(const myStruct *input, double a)
{
myStruct *output;
//doSomething
return output;
}
Если Java может вызывать ваш код C через jna, тогда не должно быть проблем с С# через PInvoke. В то время как C++ interop (с использованием C++/Cli) является одним из типов PInvoke (Implicit PInvoke), использование DllImport является явным PInvoke.
Неявный PInvoke полезен, когда вам не нужно указывать, как будут параметризованы параметры функции, или любую другую информацию, которая может быть указана при явном вызове DllImportAttribute, но вам понадобится добавить дополнительную C++/CLI Dll.
В обоих случаях вам приходится иметь дело с собственными типами данных маршала для управляемых, это неизбежно и болезненно.
в С#, структура может быть объявлена как:
[StructLayout(LayoutKind.Sequential)]
public struct myStruct {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
double prefix[] intersects;
public int length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public double[] array;
}
Но для этой функции DLLImport не может справиться с этим случаем, потому что С# не может удалить память для неуправляемого указателя, возвращаемого функцией, вы можете создать еще одну функцию переноса в C, чтобы она использовала параметр out для возврата результата, в этом случае код С#:
[DllImport("...")]
public static extern void doSomething([In, Out] myStruct[] results, myStruct[] input, int len);
Или вы можете использовать C++/CLI interop, так как он может обрабатывать как собственные, так и управляющие типы, поэтому вызывающая последовательность:
Код С# вызывает эту функцию C++/CLI с управляемыми типами данных:
ManagedmyStruct [] doSomething (ManagedmyStruct [] input, double a)
В функции C++/CLI domSomething она вызывает функцию native, этапы:
ManagedmyStruct [] doSomething (ManagedmyStruct [] input, double a) {
//convert the ManagedmyStruct[] input to native type myStruct* input
myStruct ret* = doSomething(input, a);
//convert ret to managed type ManagedmyStruct[] rets
return rets;
}
Там действительно очень мало различий между оберткой C и C++. Вам необходимо создать библиотеку классов C++/CLI. Затем вы записываете функции в управляемый класс C++ ref, который переносит собственный код.
Например, предположим, что DLL экспортирует эту функцию:
int sqr(int x)
Затем в вашей библиотеке классов вы включите заголовочный файл:
#include <mynativelibrary.h>
Вам также необходимо предоставить библиотеку импорта в компоновщик.
Затем вы можете открыть функцию. Самый простой способ - обернуть функции как статические методы класса ref. Например:
public ref class Class1
{
public:
static int sqr(int x)
{
return ::sqr(x);
}
};
Затем вы можете использовать эту сборку в своем коде С#, как и в любой другой сборке.
Я создал несколько проектов в VisualStudio 2012, которые обертывают старую MFC-библиотеку с управляемым кодом. Я сделал это так:
Не забудьте создать Unittesting для управляемого кода C++. (Я всегда забываю... :))
Удачи.