Passing fstream (or equivalent) from C # to C ++ via CLI

How to pass fstream or equivalent from C # via CLI to an unmanaged DLL in C ++?

Rough application diagram:

  • C # application reads binary file from database
  • Unmanaged C ++ dll is used to "decode" this file and return the information contained in it
  • I can change any C # code. The CLI wrapper is the only part of C ++ that I can change.

I am currently saving the binary to disk and passing its path to the CLI shell, where it opens as fstream. This is good for testing purposes, but will not work for production for obvious reasons.

I also considered passing a byte array to a DLL, but I could not find a way to convert it to fstream, except with GlobalAlloc, which I would prefer not to use.

Any help or ideas would be appreciated.

Thanks.

+8
c ++ c # command-line-interface
source share
5 answers

You can pass a managed binary array to the C ++ / CLI DLL. Select an array. This can then be converted to an STL string object. You can then pass the STL string to an STL string stream object that inherits from iostream. Think of stringstream as a .NET MemoryBuffer object. Pass the string string to unmanaged C ++. This can probably be done in <10 lines of code. The disadvantage is that the data will be copied to memory, which is inefficient. For many applications, I doubt this will be a problem.

Alternatively, you can write your own class that inherits from stream_buffer, which wraps a .NET stream object. (It’s better to inherit from this instead of iostream, as others suggest). This would be the most efficient way, because the memory would not be accurately copied, but I would not do it if the first method is fast enough.

+2
source share

If your C ++ DLL accepts common iostream objects (not just streams), create an iostream implementation that wraps System.IO streams and passes it to the DLL. Then the unmanaged side can work directly with the controlled flow.

+2
source share

Your C ++ / CLI level can provide a simple interface for using the C # side, possibly for passing byte array objects for a stream to a stream library.

Basically, the idiom is handle / body, where the C ++ / CLI layer wraps the stream and passes the opaque handle back to C #.

+1
source share

You cannot go through the CLI Memorystream . The best you can do is pass a pointer (IntPtr) to the byte buffer.

See How to transfer MemoryStream data to an unmanaged C ++ DLL using P / Invoke? for more details

I was able to get an example of work based on this post ( PInvoke and IStream ). Basically you need to implement the IStream interface in C #. You can then pass the custom Memorystream as LPSTREAM on the C ++ side. Here is an example of code that takes a stream and gets the size (just a trivial example to show how this is done):

C ++ LpWin32Dll.h

 #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 Definition

 [DllImport("LpWin32Dll.dll", CallingConvention=CallingConvention.StdCall)] public static extern int SizeOfLpStream(IStream iStream); 

Implementation of C # IStream (must implement the IStream interface). I just created a wrapper class for the Memorystream class.

 [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 # use

 IMemoryStream ms = new IMemoryStream(new byte[] { 0x45, 0x23, 0x67, 0x34 }); int size = LpTest.SizeOfLpStream(ms); 
+1
source share

Create a temporary file. let the operating system allocate a temporary name for several applications (linux can do this, I hope windows can).
Temporary files are acceptable for multi-tool chains in business applications and are used to solve only your problem. If the files are not too large, they will remain in the cache, and if you close and delete them quickly enough, they will not be written to disk.

-one
source share

All Articles