Как я могу маршаллить вектор <int>из DLL C ++ в приложение C #? - PullRequest
7 голосов
/ 01 мая 2010

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

Пока что я кодирую прямоугольники так:

struct ImagePatch{ 
   int xmin, xmax, ymin, ymax;
}

и затем кодирование некоторых векторов:

void MyFunc(..., std::vector<int>& rectanglePoints){
   std::vector<ImagePatch> patches; //this is filled with rectangles
   for(i = 0; i < patches.size(); i++){
       rectanglePoints.push_back(patches[i].xmin);
       rectanglePoints.push_back(patches[i].xmax);
       rectanglePoints.push_back(patches[i].ymin);
       rectanglePoints.push_back(patches[i].ymax);
   }
}

Заголовок для взаимодействия с C # выглядит (и работает для множества других функций):

extern "C" {
    __declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}

Есть ли какие-нибудь ключевые слова или другие вещи, которые я могу сделать, чтобы вытащить этот набор прямоугольников? Я нашел эту статью для сортировки объектов в C #, но она кажется слишком сложной и слишком недооцененной. Является ли вектор целых чисел правильным способом сделать это, или есть какой-то другой прием или подход?

Ответы [ 3 ]

4 голосов
/ 01 мая 2010

STL - это библиотека, специфичная для C ++, поэтому вы не можете напрямую передать ее как один объект в C #.

Единственное, что гарантируется в std :: vector, это то, что & v [0] указывает на первый элемент, а все элементы лежат в памяти линейно (другими словами, это похоже на массив C с точки зрения разметки памяти)

Такой маршал, как массив int ... который не должен быть сложным - в сети много примеров.

Добавлена ​​

Предполагается, что вы только передаете данные из C ++ в C #:

C # не может обрабатывать векторный объект C ++, поэтому не пытайтесь передавать его по ссылке: вместо этого ваш код C ++ должен возвращать указатель на массив целых чисел ...

Если вы не собираетесь использовать эту функцию из нескольких потоков, вы можете использовать статическое хранилище:

int *getRects(bool bClear)
{
    static vector<int> v; // This variable persists across invocations
    if(bClear)
    {
        v.swap(vector<int>());
    }
    else
    {
        v.clear();
        // Fill v with data as you wish
    }

    return v.size() ? &v[0] : NULL;
}

вызовите getRects (true), если возвращаемые данные имеют значительный размер, поэтому вы освобождаете память в v.

Для простоты вместо того, чтобы выдавать размер векторных данных, просто поместите значение часового в конце (как, скажем, -1), чтобы код C # мог определить, где заканчиваются данные.

1 голос
/ 15 апреля 2013

Да. Вы можете. На самом деле, не только std::vector, std::string, std::wstring, любой стандартный класс C ++ или ваши собственные классы могут быть маршалированы или созданы и вызваны из C # /. NET.

Завершение std::vector<any_type> в C # действительно возможно только с обычным P / Invoke Interop, хотя это сложно. даже std::map любого типа можно сделать в C # /. NET.

public class SampleClass : IDisposable
{    
    [DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi,          CallingConvention=CallingConvention.ThisCall)]
    public extern static void SampleClassConstructor(IntPtr thisObject);

    [DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi,          CallingConvention=CallingConvention.ThisCall)]
    public extern static void SampleClassDestructor(IntPtr thisObject);

    [DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
    public extern static void DoSomething(IntPtr thisObject);

    [DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
    public extern static void DoSomething(IntPtr thisObject, int x);

    IntPtr ptr;

    public SampleClass(int sizeOfYourCppClass)
    {
        this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
        SampleClassConstructor(this.ptr);  
    }

    public void DoSomething()
    {
        DoSomething(this.ptr);
    }

    public void DoSomethingElse(int x)
    {
        DoSomethingElse(this.ptr, x);
    }

    public void Dispose()
    {
        if (this.ptr != IntPtr.Zero)
        {
            // The following 2 calls equals to "delete object" in C++
            // Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
            SampleClassDestructor(this.ptr);

            // Free the memory allocated from .NET.
            Marshal.FreeHGlobal(this.ptr);

            this.ptr = IntPtr.Zero;
        }
    }
}

Пожалуйста, смотрите ссылку ниже,

C # /. NET PInvoke Interop SDK

(я автор инструмента SDK)

Когда у вас есть готовый класс-оболочка C # для вашего класса C ++, можно легко реализовать ICustomMarshaler, чтобы вы могли маршалировать объект C ++ из .NET.

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx

0 голосов
/ 01 мая 2010

Я почти уверен, что ты не сможешь этого сделать. Вы должны быть в состоянии преобразовать код C ++ непосредственно в класс C #, так что вам, по крайней мере, придется реплицировать внутренние компоненты векторного класса, чтобы правильно его маршалировать. Я также уверен, что вы не сможете перемещать ссылки через границу, вам придется использовать IntPtr (необработанные указатели). Подход, который, как я знаю, работает, состоит в том, чтобы собрать исходный массив структур.

...