Передать объект .NET Bitmap в COM (фильтр DirectShow)

Я пытаюсь создать исходный фильтр, который создает прямой видеопоток на основе последовательности изображений. Для этого я делаю интерфейс IUnknown:

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("F18FC642-5BA2-460D-8D12-23B7ECFA8A3D")]
public interface IVirtualCameraFilter_Crop
{
    void SetCurrentImage(Bitmap img);
    ...
};

И в моей программе я получаю это:

pUnk = Marshal.GetIUnknownForObject(sourceFilter);
Marshal.AddRef(pUnk);
filterInterface = Marshal.GetObjectForIUnknown(pUnk) as IVirtualCameraFilter_Crop;

Когда я передаю простые типы, все работает нормально. Но когда я пытаюсь передать объект Bitmap C#, я получаю сообщение об ошибке unable to cast Com object to <my object type>. Или приложение закрывается с ошибкой APPCRUSH.

filterInterface.SetCurrentImage(frame);

Я понимаю, что это неправильный способ, но других возможных способов передачи параметров я не знаю. Я попытался передать IntPtr в BitmapData, а затем получил то же самое приложение. Итак, как я могу передать растровое изображение фильтру DirectShow?

Результат: Для полного представления о коде цитируем Создание интерфейса:

[ComImport, InterfaceType (ComInterfaceType.InterfaceIsIUnknown), Guid ("F18FC642-5BA2-460D-8D12-23B7ECFA8A3D")]
public interface IVirtualCameraFilter_Crop
{
    unsafe void SetPointerToByteArr (byte * array, int length);
};

выполнение:

unsafe public void SetPointerToByteArr (byte * array, int length)
{
    this.array = new byte [length];
    Marshal.Copy (new IntPtr (array), this.array, 0, length);
}

В приложении:

byte [] text = ... get data;
unsafe
{
    fixed (byte * ptr = & text [0])
     {
          filterInterface.SetPointerToByteArr (ptr, text.Length); 
     }
}

person no.Oby    schedule 21.08.2012    source источник


Ответы (2)


System.Drawing.Bitmap — это тип .net, а не COM-тип, и для него нет эквивалента в COM, поэтому вы не можете использовать его в качестве параметра COM-интерфейса.

Либо используйте COM-интерфейс IStream, что непросто использовать в C#, поскольку .net MemoryStream не реализует его, или использовать COM-интерфейс IPicture или просто массив байтов.

Также имейте в виду, что ваш фильтр DirectShow обычно будет вызываться в потоке, который не является потоком пользовательского интерфейса, поэтому вы должны позаботиться о том, чтобы установить надлежащие механизмы блокировки внутри вашего фильтра.

person yms    schedule 21.08.2012

Вы не можете передать объект Bitmap напрямую из .NET в машинный код. Вы можете передать BitmapData IntPtr, но вы должны скопировать эти данные в свой буфер внутри фильтра bcs, этот указатель будет недействительным после его разблокировки. Передача массива байтов должна работать нормально, вы можете сделать это следующим образом:

 // interface method declaration
 interface IVirtualCameraFilter_Crop
 {
    [PreserveSig]
    int SetImageData([In,MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] _array,[In] int size);
 }

 // Code implementation
 IVirtualCameraFilter_Crop _filter // your filter interface
 BitmapData _data = // your BitmapLock
 int _size = // your image size while you locking it Width * Height * BPP / 8
 byte[] _array = new byte[_size];
 Marshal.Copy(_data.Scan0,_array,0,_size);
 _filter.SetImageData(_array,_size);
 // Passing IntPtr way will be similar just the changes will be in method declaration in interface 
 [PreserveSig]
 int SetImageData([In] IntPtr _array,[In] int size);
 // This way you can just pass the _data.Scan0 value
 // The C++ interface declaration and handler will be same for both ways

 // C++ interface declaration
 DECLARE_INTERFACE(IVirtualCameraFilter_Crop,IUnknown)
 {
     [PreserveSig]
     STDMETHOD(SetImageData)(LPBYTE _array,long size)PURE;
 }
 //C++ implementation
 STDMETHODIMP CFilter::SetImageData(LPBYTE _array,long size)
 {
    CheckPointer(_array,E_POINTER);
    CopyMemory(_internalArray,_array,size);
    return NOERROR;
 }

Другие вещи во время сбоя вашего фильтра:
1 - вы не копируете данные из заданного буфера
2 - вы применяете данные к существующему буферу без блокировки потока выполнения
3 - вы неправильно вычисляете размер данных при копировании внутри метода C++
4 - полученный буфер не выделен.

На самом деле, я думаю, вы должны попытаться сделать свой фильтр полностью на С#.

Вот BaseClasses.NET с примерами:
Фильтры Pure NET DirectShow в Csharp
Вот реализация виртуальной камеры:
Фильтр источника виртуального видеозахвата DirectShow в C#

С уважением, Максим.

person Sonic    schedule 23.08.2012
comment
Комментарий к предыдущему комментарию. Попробуйте примеры там + попробуйте и посмотрите на расширенные примеры, проблемы COM и проблемы с многопоточностью решены там, поэтому все работает нормально; и намного проще для реализации фильтров. У тебя видимо мало опыта в COM и в DirectShow. - person Sonic; 24.08.2012
comment
Итак, какая разница будет с передачей .NET Bitmap в виде массива или потока и созданием фильтра целиком на C#? - person Sonic; 28.08.2012
comment
Насчет раздавливания приложения, если фильтр не перехватывает исключения, любые исключения раздавливают приложение, поэтому советуем использовать MessageBoxs. "You seems have not much experience in COM and in DirectShow" its true). - person no.Oby; 14.09.2012
comment
Этот комментарий был не для вас, по поводу опыта - там были другие комментарии от другого парня. Конечно, вы должны использовать статистику try catch в своем фильтре. MessageBox использовать не рекомендуется, вы можете вывести данные в отладчик и использовать ASSERT для проверки условий. - person Sonic; 17.09.2012