簡體   English   中英

將IWICStream / IStream從非托管C ++ DLL返回到托管C#並讀取它

[英]Returning IWICStream / IStream from unmanaged C++ DLL to managed C# and reading it

在C#程序中,我希望從從非托管C ++ DLL導入的函數返回圖像數據(可能以IStream的形式)。

我已經在這個一般主題上閱讀了幾個類似的問題和msdn文檔,但到目前為止還無法找到一個完整的工作解決方案。

導出C ++函數以便在托管C#中使用: -

extern "C" __declspec(dllexport) IStream* GetImage(){

    IWICBitmap *pBitmap = NULL;

    //... Code emitted for clarity
    //... Bitmap creation and Direct2D image manipulation
    //...

    IWICStream *piStream = NULL;
    IStream *stream;
    CreateStreamOnHGlobal(NULL, true, &stream);
    HRESULT hr = piStream->InitializeFromIStream(stream);

    //... pBmpEncoder is IWICBitmapEncoder, piBitmapFrame is IWICBitmapFrameEncode
    //... pFactory is IWICImagingFactory

    hr = pFactory->CreateEncoder(GUID_ContainerFormatTiff, NULL, &pBmpEncoder);
    hr = pBmpEncoder->Initialize(piStream, WICBitmapEncoderNoCache);
    //...
    hr = pBmpEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
    //..
    piBitmapFrame->WriteSource(pBitmap, &rect);
    //...
    piBitmapFrame->Commit();
    pBmpEncoder->Commit();

    return stream;

}

為了清楚起見,我發布了代碼。 最重要的是,一個IWICBitmap編碼為.tiffIWICStream IWICStream是從IStream初始化的。 然后該函數返回*IStream

C#導入函數聲明: -

using System.Runtime.InteropServices.ComTypes.IStream;

[DllImport(@"D:\mydlls\getimagedll.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IStream GetImage();

C#代碼消耗GetImage()函數並嘗試讀取IStream : -

public ActionResult DeliverImage()
    {
        IStream stream = GetImage();

        unsafe
        {
            byte[] fileBytes = new byte[4000];
            int bytesRead = 0;
            int* ptr = &bytesRead;
            stream.Read(fileBytes, fileBytes.Length, (IntPtr)ptr);

        return File(fileBytes, System.Net.Mime.MediaTypeNames.Image.Tiff, "image.tiff");
        }
    }

有三件事我需要幫助。

  1. 目前,C ++函數返回*IStream盡管導入的C#聲明具有System.Runtime.InteropServices.ComTypes.IStream作為返回類型。 顯然, IStream指針與System.Runtime.InteropServices.ComTypes.IStream不匹配。 我已經嘗試從函數返回一個IStream對象但是intellisense警告'函數返回抽象類是不允許的'。 如何將IStream返回並正確編組為托管C# System.Runtime.InteropServices.ComTypes.IStream

  2. 在C#中讀取IStream時,如何識別流中圖像數據的長度,以便將讀取緩沖區設置為正確的長度?

  3. 在C ++或C#中釋放IStream的正確方法是什么,以便沒有內存泄漏? 可以在返回后立即在C ++中釋放流,還是需要將第二個函數導出到C#,在C#完成使用流后調用?

謝謝您的幫助!

您當前返回IStream*代碼運行正常。 這是一個簡單的演示,表明它工作正常:

C ++

extern "C" __declspec(dllexport) IStream* __stdcall GetStream()
{
    IStream *stream;
    CreateStreamOnHGlobal(NULL, true, &stream);
    int i = 42;
    stream->Write(&i, sizeof(int), NULL);
    return stream;
}

C#

[DllImport(dllname)]
private static extern IStream GetStream();
....
IStream stream = GetStream();
IntPtr NewPosition = Marshal.AllocHGlobal(sizeof(long));
stream.Seek(0, STREAM_SEEK_END, NewPosition);
Console.WriteLine(Marshal.ReadInt64(NewPosition));
stream.Seek(0, STREAM_SEEK_SET, IntPtr.Zero);
byte[] bytes = new byte[4];
stream.Read(bytes, 4, IntPtr.Zero);
Console.WriteLine(BitConverter.ToInt32(bytes, 0));

你如何找到流的大小? 那么上面的代碼顯示了如何使用Seek方法做到這一點。 你想把它包起來。 例如:

static long GetStreamSize(IStream stream)
{
    IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(long));
    try
    {
        stream.Seek(0, STREAM_SEEK_CUR, ptr);
        long pos = Marshal.ReadInt64(ptr);
        stream.Seek(0, STREAM_SEEK_END, ptr);
        long size = Marshal.ReadInt64(ptr);
        stream.Seek(pos, STREAM_SEEK_SET, IntPtr.Zero);
        return size;
    }
    finally
    {
        Marshal.FreeCoTaskMem(ptr);
    }
}

或者如果您願意,可以使用不安全的代碼更簡單:

unsafe static long GetStreamSize(IStream stream)
{
    long pos;
    stream.Seek(0, STREAM_SEEK_CUR, new IntPtr(&pos));
    long size;
    stream.Seek(0, STREAM_SEEK_END, new IntPtr(&size));
    stream.Seek(pos, STREAM_SEEK_SET, IntPtr.Zero);
    return size;
}

最后,您要問如何在C#中釋放IStream接口。 你不需要。 C#編譯器自動處理引用計數。 它會向AddRefRelease插入適當的調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM