简体   繁体   English

将IWICStream / IStream从非托管C ++ DLL返回到托管C#并读取它

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

Within a C# program I would like to receive image data (probably in the form of IStream ), returned from a function imported from an unmanaged C++ DLL. 在C#程序中,我希望从从非托管C ++ DLL导入的函数返回图像数据(可能以IStream的形式)。

I have read several similar questions and msdn docs on this general topic but so far have been unable to figure out a complete working solution. 我已经在这个一般主题上阅读了几个类似的问题和msdn文档,但到目前为止还无法找到一个完整的工作解决方案。

C++ function exported for consumption in managed C#:- 导出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;

}

I have emitted code for the sake of clarity. 为了清楚起见,我发布了代码。 What is important is that an IWICBitmap is encoded as a .tiff to an IWICStream . 最重要的是,一个IWICBitmap编码为.tiffIWICStream The IWICStream was initialized from an IStream . IWICStream是从IStream初始化的。 Then the function returns *IStream . 然后该函数返回*IStream

C# Imported function declaration:- C#导入函数声明: -

using System.Runtime.InteropServices.ComTypes.IStream;

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

C# code consuming GetImage() function and attempting to read IStream :- 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");
        }
    }

There are three things I need help with. 有三件事我需要帮助。

  1. Currently the C++ function is returning *IStream although the imported C# declaration has System.Runtime.InteropServices.ComTypes.IStream as return type. 目前,C ++函数返回*IStream尽管导入的C#声明具有System.Runtime.InteropServices.ComTypes.IStream作为返回类型。 Obviously an IStream pointer and System.Runtime.InteropServices.ComTypes.IStream do not match. 显然, IStream指针与System.Runtime.InteropServices.ComTypes.IStream不匹配。 I have tried returning an IStream object from the function but intellisense warns 'function returning abstract class is not allowed'. 我已经尝试从函数返回一个IStream对象但是intellisense警告'函数返回抽象类是不允许的'。 How can an IStream be returned and correctly marshalled to be a managed C# System.Runtime.InteropServices.ComTypes.IStream ? 如何将IStream返回并正确编组为托管C# System.Runtime.InteropServices.ComTypes.IStream

  2. When reading the IStream in C#, how can the length of image data in the stream be discerned so that the read buffer can be set to the correct length? 在C#中读取IStream时,如何识别流中图像数据的长度,以便将读取缓冲区设置为正确的长度?

  3. What is the correct way to release the IStream in C++ or C# or both so that there isn't a memory leak? 在C ++或C#中释放IStream的正确方法是什么,以便没有内存泄漏? Can the stream be released in C++ as soon as it is returned or does there need to be a second function exported to C# that is called after the C# has finished using the stream? 可以在返回后立即在C ++中释放流,还是需要将第二个函数导出到C#,在C#完成使用流后调用?

Thank you for your help! 谢谢您的帮助!

Your current code to return IStream* works fine. 您当前返回IStream*代码运行正常。 Here is a simple demonstration that shows that it works fine: 这是一个简单的演示,表明它工作正常:

C++ 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# 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));

How do you find the size of the stream? 你如何找到流的大小? Well the code above shows how to do that using the Seek method. 那么上面的代码显示了如何使用Seek方法做到这一点。 You'd want to wrap that up. 你想把它包起来。 For instance: 例如:

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

Or a bit more simple with unsafe code if you prefer: 或者如果您愿意,可以使用不安全的代码更简单:

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

Finally, you ask how to release the IStream interface in C#. 最后,您要问如何在C#中释放IStream接口。 You don't need to. 你不需要。 The C# compiler takes care of the reference counting automatically. C#编译器自动处理引用计数。 It inserts appropriate calls to AddRef and Release . 它会向AddRefRelease插入适当的调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在非托管c ++ dll中使用托管c#dll - Using a managed c# dll inside an unmanaged c++ dll 从非托管C Dll加载托管C ++ Dll? - Load Managed C++ Dll from Unmanaged C Dll? 通过托管C ++包装程序从非托管C ++ dll运行C#函数 - Run a C# function from an unmanaged C++ dll through a managed C++ Wrapper 将托管C#列表返回到非托管C ++代码 - Returning Managed C# List to Unmanaged C++ code 将非托管 dll 嵌入托管 C# dll - Embedding unmanaged dll into a managed C# dll 从C#windows服务调用C ++ DLL(非托管代码)(用托管代码编写) - Calling a C++ dll (unmanaged code) from a C# windows service (written in managed code) 在从托管C#应用程序进行的非托管C ++ Dll调用中,如何处理数据类型之间的冲突 - In Unmanaged C++ Dll call From Managed C# Application, How to handle conflicts between Datatypes 从C#调用非托管C ++库(dll):如何在托管代码和非托管代码之间正确传递数据 - Calling unmanaged C++ library (dll) from C# : How to properly pass data between managed and unmanaged code 如何将结构从C#托管结构传递到C ++非托管DLL并得到结果结构? - How can I pass struct from C# managed to C++ unmanaged DLL and get struct in result? 如何从 C++ 非托管/本机 DLL 中的免注册 COM C# 托管 DLL 创建 COM 对象 - How to create a COM object from registration free COM C# managed DLL in a C++ unmanaged/native DLL
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM