将IWICStream / IStream从非托管C++DLL返回到托管C#并读取它
本文关键字:读取 返回 C++DLL IStream IWICStream | 更新日期: 2023-09-27 17:56:54
在C#程序中,我希望接收图像数据(可能以IStream
的形式),从从非托管C++DLL导入的函数返回。
我已经阅读了几个关于这个一般主题的类似问题和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
被编码为IWICStream
的.tiff
。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");
}
}
有三件事我需要帮助。
目前,C++ 函数返回
*IStream
尽管导入的 C# 声明已System.Runtime.InteropServices.ComTypes.IStream
为返回类型。显然,IStream
指针和System.Runtime.InteropServices.ComTypes.IStream
不匹配。我尝试从函数返回 IStream 对象,但 intellisense 警告"不允许函数返回抽象类"。如何返回IStream
并正确封送为托管 C#System.Runtime.InteropServices.ComTypes.IStream
?在 C# 中读取
IStream
时,如何识别流中图像数据的长度,以便将读取缓冲区设置为正确的长度?
在 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# 编译器会自动处理引用计数。它插入对AddRef
和Release
的适当调用。