Как передать в С++ библиотеку указатель на объект из C# и затем получить данные обратно?

139
01 мая 2019, 11:30

Дело такое: Написал несколько функций на С++ и хочу их использовать в C# коде, но не знаю как правильно объявить эти функции в C#.

C функциями int CreateNewConverter() и void DestroyConverter(/*in*/ int id) вроде бы понятно, но вот void Convert(int converterID, IDeckLinkVideoFrame *srcFrame, byte **dstArray)) вызывает сомнения в правильности оформления и удобства использования в C#, потому что у меня тут данные в библиотеку передаются и забираются через указатели.

Отмечу, что IDeckLinkVideoFrame *srcFrame это указатель на COM (Component Object Model) объект реализующий интерфейс `IDeckLinkVideoFrame, так что, я полагаю, наверное, можно передать его с помощью IntPtr, но я не знаю как.

Прошу помочь: укажите на ошибки, посоветуйте как правильнее, помогите решить.

H-файл:

#ifdef DLFC_EXPORTS
#define DLFC_API __declspec(dllexport) 
#else
#define DLFC_API __declspec(dllimport) 
#endif
using namespace std;
static unordered_map<int, VideoConverterARGB32*> Dictionary;
static int ID = 0;
extern "C" {
    DLFC_API int CreateNewConverter();
    DLFC_API void DestroyConverter( /*in*/ int id);
    DLFC_API void Convert( /*in*/  int converterID,
                           /*in*/  IDeckLinkVideoFrame *srcFrame,
                           /*out*/ byte **dstArray);
}

CPP-файл:

using namespace std;
int  CreateNewConverter() {
    ID++;
    VideoConverterARGB32* conv = new VideoConverterARGB32();
    Dictionary[ID] = conv;
    return ID;
}
void DestroyConverter(/*in*/ int id) {
    VideoConverterARGB32* conv = Dictionary[id];
    Dictionary.erase(id);
    delete conv;
}
void Convert(/*in*/  int converterID,
             /*in*/  IDeckLinkVideoFrame *srcFrame,
             /*out*/ byte **dstArray)
{
    VideoConverterARGB32* conv = Dictionary[converterID];
    conv->Convert(srcFrame, dstArray);
}    

С#:

using System.Runtime.InteropServices;
using DeckLinkAPI;
namespace AR.CaptureDevices.DeckLink {
    public class DeckLinkConverter {
        [DllImport(dllName: "DeckLinkConverter")]
        public static extern int CreateNewConverter();
        [DllImport(dllName: "DeckLinkConverter")]
        public static extern void DestroyConverter([Out] int id);
        [DllImport(dllName: "DeckLinkConverter")]
        public static extern void Convert([In]int converterID, [In] IDeckLinkVideoFrame* srcFrame, [Out] byte** dstArray);
    }
}
Answer 1

Есть несколько вариантов.

  1. Самый простой вариант - получение ссылки

    List<string> list1 = new List<string>();
    GCHandle handle1 = GCHandle.Alloc(list1);
    IntPtr parameter = (IntPtr) handle1;
    // call WinAPi and pass the parameter here
    // then free the handle when not needed:
    handle1.Free();
    // back to object (in callback function):
    GCHandle handle2 = (GCHandle) parameter;
    List<string> list2 = (handle2.Target as List<string>);
    list2.Add("hello world");

Полученная ссылка для чистого массива - будет указывать на массив (скорее всего).

Плюс в Marshal есть много полезностей, например: ReadByte, WriteByte, PtrToStringAnsi, StringToBSTR, Copy и другие.

Ccылка http://stackoverflow.com/questions/17339928/c-sharp-how-to-convert-object-to-intptr-and-back

  1. Использование COM (ActiveX). Создаём COM-совместимый класс, и работаем в с++ как обычно работают с подобными обьектами. Если наследовать один из извесных интерфейсов - то будет совместимость с ним. "Тунели" с# сам создаёт при использовании COM-совместимых типов.

Для передачи указателя на интерфейс, в Marshal есть GetComInterfaceForObject.

ссылка http://www.codeproject.com/Articles/612604/Best-Practice-in-Writing-a-COM-Visible-Assembly-Cs

Так же, если есть интерфейс в с++, то можно его подхватить на с# по ссылке, используя Marshal.GetTypedObjectForIUnknown.

Так же можно передавать COM автоматически, для этого нужно, что б интерфейс был помечен как COM

[ComVisible(true)]
public interface IDeckLinkVideoFrame :IUnknown {
  //...
  }

И процедура вызова с с++ была в с# обьявлена примерно так

 [DllImport(dllName: "DeckLinkConverter")]
 void Convert(
    [In][MarshalAs(UnmanagedType.I4)]int converterID, 
    [In][MarshalAs(UnmanagedType.Interface)]IDeckLinkVideoFrame srcFrame, 
    [Out][MarshalAs(UnmanagedType.LPArray)] byte **dstArray);
  1. C++-CLI. Данная модель позволяет обьеденить интерфейсы с# и с++, что то среднее что одной ногой в c# а другой в с++. https://habr.com/post/47732/
READ ALSO
Как пролистать Panel через AutoScroll кнопкой?

Как пролистать Panel через AutoScroll кнопкой?

Подскажите, как при нажатии на кнопку пролистать Panel через AutoScroll, таким образом, чтобы пролистывание уходило в самый конец Panel

145
Ошибка при получении пользователя из Active Directory c#

Ошибка при получении пользователя из Active Directory c#

Подскажите, выходит ошибка при получении пользователя из AD: "Ссылка на объект не указывает на экземпляр объекта"Хотя на компе где пишется...

140
Парсинг json полей с точками как классов

Парсинг json полей с точками как классов

Имеется json примерно такого типа:

132
Как убрать вывод изображений с помощью счетчика в определенном промежутке

Как убрать вывод изображений с помощью счетчика в определенном промежутке

Сейчас вывод изображений осуществляется с помощью счетчика в 3 промежутках:

180