通过CLI从c#向c++传递一个fstream(或等价)
本文关键字:一个 CLI fstream c++ 通过 | 更新日期: 2023-09-27 18:01:35
如何通过CLI将fstream或等效的c#传递到非托管的c++ DLL?
应用概要:
- c#应用程序从数据库读取二进制文件
- 非托管c++ dll用于"解码"此文件并返回其中包含的信息 我可以修改任何c#代码。CLI包装器是c++端唯一可以修改的部分。
我目前将二进制文件保存到磁盘,并将其路径传递给CLI包装器,在那里它作为fstream打开。这对于测试目的来说很好,但由于显而易见的原因,它不适用于生产。
我还研究了传递字节数组到DLL,但我无法找到一种方法将其转换为除GlobalAlloc之外的fstream,我不希望使用。
任何帮助或想法都将不胜感激。
谢谢。
您可以将托管二进制数组传递给c++/CLI DLL。固定阵列。然后可以将其转换为STL字符串对象。然后,您可以将STL字符串传递给继承自iostream的STL stringstream对象。把stringstream看作一个。net MemoryBuffer对象。将stringstream传递给非托管c++。这可能可以在<10行代码。缺点是数据将被复制到内存中,这是低效的。对于许多应用程序,我怀疑这将是一个问题。
或者,您可以编写自己的类,继承stream_buffer来包装。net流对象。(最好从这个继承,而不是像其他人建议的那样从iostream继承)。这将是最有效的方法,因为没有内存会被不必要地复制,但如果第一个方法足够快,我就不会麻烦这样做了。
如果你的c++ DLL接受通用的iostream对象(而不仅仅是fstreams),创建一个iostream实现来包装System. js。IO流并将其传递给DLL。然后,非托管端可以直接处理托管流。
您不能通过CLI传入
Memorystream
。你能做的最好的事情就是传递一个"指针"(IntPtr)到字节缓冲区。
请参阅如何使用p/Invoke将MemoryStream数据传递给非托管的c++ DLL ?查看更多详细信息
我能够得到一个基于这篇文章(PInvoke和IStream)工作的例子。基本上你需要用c#实现IStream接口。然后,您可以将自定义MemoryStream
作为c++端的LPSTREAM
传递。下面是一个代码示例,它接受流并获取大小(只是一个简单的示例来展示它是如何完成的):
#ifndef LPWINDLL_H
#define LPWINDLL_H
extern "C" {
__declspec(dllexport) int SizeOfLpStream(LPSTREAM lpStream);
}
#endif
c++ LpWin32Dll.cpp #include "stdafx.h"
#include <ocidl.h>
#include "LpWin32Dll.h"
// Provides DllMain automatically
[module(dll, name = "LpWin32Dll")];
__declspec(dllexport) int SizeOfLpStream(LPSTREAM lpStream)
{
STATSTG stat_info;
lpStream->Stat(&stat_info, STATFLAG_NONAME);
return stat_info.cbSize.LowPart;
}
c# PInvoke定义
[DllImport("LpWin32Dll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern int SizeOfLpStream(IStream iStream);
c# IStream实现(必须实现IStream接口)。我刚刚为MemoryStream
类创建了一个包装器类。
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class IMemoryStream : MemoryStream, IStream {
public IMemoryStream() : base() { }
public IMemoryStream(byte[] data) : base(data) { }
#region IStream Members
public void Clone(out IStream ppstm) { ppstm = null; }
public void Commit(int grfCommitFlags) { }
public void CopyTo(
IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { }
public void LockRegion(long libOffset, long cb, int dwLockType) { }
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
long bytes_read = base.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero)
Marshal.WriteInt64(pcbRead, bytes_read);
}
public void Revert() { }
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long pos = base.Seek(dlibMove, (SeekOrigin)dwOrigin);
if (plibNewPosition != IntPtr.Zero)
Marshal.WriteInt64(plibNewPosition, pos);
}
public void SetSize(long libNewSize) { }
public void Stat(
out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg,
int grfStatFlag)
{
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
pstatstg.cbSize = base.Length;
}
public void UnlockRegion(long libOffset, long cb, int dwLockType) { }
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
base.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero)
Marshal.WriteInt64(pcbWritten, (long)cb);
}
#endregion
}
c#使用IMemoryStream ms = new IMemoryStream(new byte[] { 0x45, 0x23, 0x67, 0x34 });
int size = LpTest.SizeOfLpStream(ms);
您的c++/CLI层可以为c#端提供一个简单的接口,也许可以将字节数组对象传递到流库中。
基本上是句柄/体的习惯用法,c++/CLI层包装流并传递一个不透明的句柄给c#使用。
创建临时文件。让操作系统为您分配一个临时名称来解决多个应用程序(linux可以做到,我希望windows可以)。
临时文件对于业务应用程序中的多工具链是可以接受的,并且用于解决您的问题。如果文件不是太大,它们会留在缓存中,如果你关闭和删除它的速度足够快,它们甚至不会被写入磁盘。