Pass .NET Bitmap object to COM (DirectShow filter)

994 views Asked by At

I'm trying to create a source filter that makes a live video stream based on a sequence of pictures. To do this, I make an interface of IUnknown:

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

And in my programm I get it:

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

When I pass simple types everything works fine. But when I try to pass a C# Bitmap object I get an error unable to cast Com object to <my object type>. Or application shutting down with error APPCRUSH.

filterInterface.SetCurrentImage(frame);

I understand that it`s not the correct way but I do not know other possible ways of passing parameters. I tried to pass IntPtr to BitmapData and then I get the same application crush. So How can I pass the bitmap to the DirectShow filter?

Result: For a complete picture of the code cite Create an interface:

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

implementation:

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

In application:

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

There are 2 answers

0
yms On BEST ANSWER

System.Drawing.Bitmap is a .net type, not COM type, and there is no equivalent for it in COM, so you cannot use it as a parameter of a COM interface.

Either use the COM interface IStream, which is not easy to use in C# since .net MemoryStream does not implements it, or use the COM interface IPicture, or just an array of bytes.

Also be aware that your DirectShow filter will usually be called in a thread that is not the UI thread, so you are supposed to take care of putting the proper lock mechanisms inside your filter.

4
Sonic On

You can't pass the Bitmap object directly from .NET into native code. You can pass the BitmapData IntPtr but you should copy that data into your buffer inside filter bcs that pointer will be not valid once you unlock it. Passing an array of bytes should work fine, you can do it this way:

 // 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;
 }

Another things while your filter crashing:
1 - you not copy data from given buffer
2 - you apply data to existing buffer without locking execution thread
3 - you not proper calculating data size while copy inside C++ method
4 - your resulted buffer not allocated.

Actually I think you should try to make your filter entierly in C#

Here is the BaseClasses.NET with an examples:
Pure NET DirectShow Filters in Csharp
Here is virtual camera implementation:
DirectShow Virtual Video Capture Source Filter in C#

Regards, Maxim.