Мое приложение состоит из кода С# с неуправляемыми вызовами C dll. В моем коде С# у меня есть объект/класс, где его свойства являются как системными типами, такими как строка и int и другими объектами, которые я определил.
Я хотел бы передать этот комплексный объект (Graph.cs) в мой код C (dll), какую реализацию вы предложили бы здесь?
Я попытался переместить structs, но я не могу сделать это с чем-то другим, кроме string и int.
Спасибо.
код:
public Class Grpah {
TupleCollection m_TupleCollection;
int m_nGeneralIndex;
bool m_bPrintWRF;
string m_sLink;
}
public Class TupleCollection {
IList<Tuple> _collection;
}
public Class Tuple {
Globals.TupleType m_ToppleType;
ArrayList m_Parameters;
}
public class TupleArgs {
public string Value { get; set; }
public Globals.PAS PAS;
public RefCollection RefCollection { get; set; }
public int Time{ get; set; }
}
public class RefCollection {
public List<int> SynSet{ get; set; }
public Globals.PAS PAS;
}
Try:
Практическое руководство. Маршал-структуры с использованием PInvoke
Я думаю, что самый простой способ добиться успеха - изменить собственный код, давая ему возможность работать с типами CLR.
Теперь вы почти наверняка используете Visual Studio, и, надеюсь, это VS2005 или новее. Это означает, что, хотя ваш существующий собственный код находится на C, у вас есть возможность вникать в небольшой С++. И не только это - у вас также есть С++/CLI.
Итак, я бы создал новую С++/CLI DLL и связал вашу библиотеку C с ней, чтобы она могла вызвать код C. Затем напишите тонкий слой перевода в библиотеке С++/CLI: он выведет истинные классы CLR (написанные с помощью ref class
) и вызовет собственный C-код.
например. в заголовке C:
void do_something_with_foo_data(int a, int b, int c);
В С++/CLI:
public ref class FooWrapper
{
static void DoSomethingWithFoo(Foo ^foo)
{
// pick apart the Foo class and pass the pieces on to the C function
do_something_with_foo_data(foo->A, foo->B, foo->C);
}
};
В С#:
public class Foo
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
...
var foo = new Foo { A = 1, B = 2, C = 3 };
FooWrapper.DoSomethingWithFoo(foo);
Когда я это сделал, я использовал Marshal. Это было со стандартом C для собственного кода. Я не хотел преобразовывать свой C-код в "управляемый С++" или как он его называет: -)
Жесткая и утомительная часть состоит в том, что вы должны вручную маршевать структуру данных во что-то, что напрямую связано с принимающей функцией C. В моем случае мне приходилось создавать отдельные структуры для каждого документа, несущего класс С#, который я хотел отправить. В сущности, вы должны преобразовать свою красивую иерархию объектов С# в более базовую форму, состоящую из структур, которые затем маршалируете в блок памяти, который затем используется в качестве аргумента в вашем родном вызове.
Вы должны использовать метод Marshal.AllocHGlobal
для выделения памяти и Marshal.StructureToPtr
для копирования вашей структуры С# в эту память, а затем Marshal.FreeHGlobal
, чтобы освободить ее.
Как только у вас есть IntPtr (от StructureToPtr
), вы сможете просто вызвать свою C-dll с указанным указателем в качестве аргумента. Обратите внимание, что структура, которую вы отправляете на свою C-функцию, должна иметь тот же макет, что и родная C-структура, или вы получите очень нечетные результаты.
Возвращаемые данные - почти то же самое, но вместо этого вы используете противоположные функции (PtrToStructure
и т.д.).
В любом случае это основное.
Ваша модель выглядит довольно сложной и неполной: Tuple.m_parameters - это ArrayList, поэтому он может содержать почти что угодно, а тип Globals.PAS здесь не определен.
Возможно, вам стоит подумать о другой стратегии: в своей DLL создайте небольшую модель, которая содержит все, что вам нужно в вашем C-коде, и сделайте ее максимально простой (но не простой!).
Затем изучите все, что вам нужно, чтобы вывести эту модель C из управляемого кода и заполнить модель из вашего класса Graph.cs. Предпочтительно, Graph.cs не должен нести ответственность за это, ваш код маршаллинга должен быть.