[英]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
编码为.tiff
到IWICStream
。 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. 有三件事我需要帮助。
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
?
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
时,如何识别流中图像数据的长度,以便将读取缓冲区设置为正确的长度?
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
. 它会向
AddRef
和Release
插入适当的调用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.