none
How to convert IStream(C++) to System::IO::Stream(c#) and vice versa RRS feed

  • Question

  • I have 2 libraries one is written in c++ and the other one is written in c#.

    Name of C++ library ->LibA

    Name of C# library ->LibB

    In LibA, 2 main APIs will be there:

    1. Serialize-> Serialize API will generate IStream as output with the given inputs.
    2. Deserialize-> Deserialize API will take IStream as input and deserializes the stream and gets actual data from it.
    struct GPosition
    {
        double x;
        double y;
        double z;
    };
    struct GUnitVector
    {
        double x;
        double y;
        double z;
    };
    struct GLine
    {
        GPosition m_rootPoint;    /* Point on the line, should be in region of interest */
        GUnitVector m_direction;    /* Gradient */
        double m_start;        /* Parameter of start point, must be <= m_end */
        double m_end;          /* Parameter of end point */
    };
    
    
    
    class GraphicSerializer
    {
    public:
    
        GUID objectID;
    
        uint32_t aspectID;
        uint32_t controlFlag;
        vector<const GLine *> geomVector;
    
        void Serialize(IStream &pStream);
        void Deserialize(IStream* pStream);
    };

    In LibB, 4 APIs will be there:

    1. Object GetObjectFromStream(Stream s)-> Takes stream as input and deserializes it and returns Objects
    2. PutToDB(Object A)-> persists the given object to DB
    3. Stream GetStreamFromObject(Object a)-> Takes object as input serializes it and returns it.
    4. Object GetFromDB(objectID)-> Gets the object from DB based on id
    public class CommonBase
        {
            public Guid id { get; set; };
            public byte[] Bytes { get; set; } //contains aspect, control flag, vector<GLine>
        };
    
        public interface IGraphicRepository
        {
            CommonBase Get(Guid guid);
            bool Put(CommonBase graphic);
        }
    
        public static class GraphicStreamUtility
        {
            public static CommonBase GetCommonBaseFromStream(Stream stream);
            public static void SerializeCommonBaseToStream(CommonBase obj, Stream stream);
        }

    Now I'm writing C++/CLI to use stream generated by libA in libB and vice versa. So that I can persist and retrieve the objects to and from DB.

    Can anyone please let me know how to convert IStream to .Net Stream and .Net stream to IStream.


    Wednesday, November 20, 2019 5:30 AM

Answers

  • Finally I wrote below code which worked for me:

    Stream^ ConvertIStreamToStream(IStream *pStream)
    	{
    		ULARGE_INTEGER ulSize{};
    		IStream_Size(pStream, &ulSize);
    		size_t size = ulSize.QuadPart;
    		auto buffer = std::make_unique<char[]>(size);
    		ULONG numRead{};
    		HRESULT hr = pStream->Read(buffer.get(), size, &numRead);
    
    		if (FAILED(hr) || size != numRead)
    		{
    			throw std::exception("Stream conversion failed");
    		}
    
    		cli::array<Byte>^ byteArray = gcnew cli::array<Byte>(numRead+2);
    
    		// convert native pointer to System::IntPtr with C-Style cast
    		Marshal::Copy((IntPtr)buffer.get(), byteArray, 0, numRead);
    
    		return gcnew System::IO::MemoryStream(byteArray);
    
    	}

    IStream* ConvertStreamToIStream(System::IO::Stream^ stream)
    	{
    		cli::array<Byte>^ byteArray = gcnew cli::array<Byte>(stream->Length);
    		stream->Read(byteArray, 0, stream->Length);
    
    		auto buffer = std::make_unique<char[]>(stream->Length);
    		System::Runtime::InteropServices::Marshal::Copy(byteArray, 0, (IntPtr)buffer.get(), stream->Length);
    
    		IStream *pStream = SHCreateMemStream(0, 0);
    		ULONG written{};
    		pStream->Write(buffer.get(), stream->Length, &written);
    
    		if(written!=stream->Length)
    			throw std::exception("Stream conversion failed");
    
    		LARGE_INTEGER offset;
    		offset.QuadPart = 0;
    		ULARGE_INTEGER newpos{};
    		pStream->Seek(offset, STREAM_SEEK_SET, &newpos);
    
    		return pStream;
    	}


    Tuesday, November 26, 2019 9:05 AM

All replies

  • These 2 types would not be compatible. The only option I see is to create a new .NET type deriving from Stream that takes your native stream as a parameter. Then internally read/write the native stream as you implement the .NET stream class.

    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, November 20, 2019 2:46 PM
    Moderator
  • Is IStream in C++ is compatible with .Net Stream? If yes, could you please provide me solution for that?

    I have seen that there is IStream interface in .NET in System.Runtime.InteropServices.ComTypes

    I tried using it for converting .Net Stream to C++ IStream but I ended up with some issues.

    public class IStreamWrapper : IStream
        {
            public IStreamWrapper(Stream stream)
            {
                this.stream = stream ?? throw new ArgumentNullException("stream", "Can't wrap null stream.");
            }
    
            Stream stream;
    
            public void Clone(out IStream ppstm)
            {
                ppstm = null;
            }
    
            public void Commit(int grfCommitFlags) { }
    
            public void CopyTo(IStream pstm,
              long cb, System.IntPtr pcbRead, System.IntPtr pcbWritten)
            { }
    
            public void LockRegion(long libOffset, long cb, int dwLockType) { }
    
            public void Read(byte[] pv, int cb, System.IntPtr pcbRead)
            {
                Marshal.WriteInt64(pcbRead, (Int64)stream.Read(pv, 0, cb));
            }
    
            public void Revert() { }
    
            public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPosition)
            {
                Marshal.WriteInt64(plibNewPosition, stream.Seek(dlibMove, (SeekOrigin)dwOrigin));
            }
    
            public void SetSize(long libNewSize) { }
    
            public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
            {
                pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
            }
    
            public void UnlockRegion(long libOffset, long cb, int dwLockType) { }
    
            public void Write(byte[] pv, int cb, System.IntPtr pcbWritten) { }
        }

    public class NativeClass
    	{
    	public:
    		IStream* NativeMethod(IUnknown* output)
    		{
    			IStream *outputStream;
    			HRESULT hr = output->QueryInterface(IID_IStream, (void**)&outputStream);
    			if (FAILED(hr) || outputStream == nullptr)
    			{
    				std::exception("NativeMethod failed");
    			}
    			return outputStream;
    		}
    	};

    public ref class ManagedClass
    	{
    	private:
    		NativeClass* nativeObject;
    	public:
    		ManagedClass() 
    		{
    			nativeObject = new NativeClass();
    		}
    		IStream * ManagedMethod(System::Runtime::InteropServices::ComTypes::IStream^ output)
    		{
    			System::IntPtr outputPtr = System::Runtime::InteropServices::Marshal::GetIUnknownForObject(output);
    			IUnknown* outputNativePtr = static_cast<IUnknown*>(outputPtr.ToPointer());
    			IStream *outputStream=nativeObject->NativeMethod(outputNativePtr);
    			return outputStream;
    		}
    	};

    public ref class CStreamCLI
    {
    void 
    serialize(System::Runtime::InteropServices::ComTypes::IStream^ outStream)
    {
    ManagedClass^ managedObject = gcnew ManagedClass();
    IStream *finalStream=managedObject->ManagedMethod(outStream);
    //I'm using this finalStream in my code directly
    }
    }



    void main()
    {
    var graphicsStream = new MemoryStream();
    IStreamWrapper streamWrapper = new IStreamWrapper(graphicsStream);
    
    cli.serialize(streamWrapper);
    }


    Thursday, November 21, 2019 5:07 AM
  • If you already know how to deal with stream in C++/CLI, then you can write a class and function that converts the native objects to equivalent managed objects (and vice versa), which will be visible in C#. (The classes defined with ‘public ref class’ in C++/CLR can be used in C#). Having these objects, maybe you do need the stream in C#.

    Thursday, November 21, 2019 5:58 AM
  • I tried writing that conversion class in c++/cli(I posted the code I wrote in c++/cli in reply). That is not working for me and I was blocked as I'm new to streams. So I'm trying to figure out the solution for that
    Thursday, November 21, 2019 6:12 AM
  • "Is IStream in C++ is compatible with .Net Stream"

    No they are not compatible. Stream is a common term in most languages but that doesn't make them compatible. IStream/ISequentialStream (in C++) is the Windows COM interface. The Stream class in .NET is an abstract base class from which other streams derive and they don't implement the COM interface and hence are not compatible.

    You have to do the marshaling yourself. Here's one article with a partial implementation but it looks like you might have already used it. And here's the question with a fuller implementation of said class (it appears).

    If your code isn't working then you're going to have to be more specific about what isn't working and provide samples we can try.


    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, November 21, 2019 2:44 PM
    Moderator
  • Finally I wrote below code which worked for me:

    Stream^ ConvertIStreamToStream(IStream *pStream)
    	{
    		ULARGE_INTEGER ulSize{};
    		IStream_Size(pStream, &ulSize);
    		size_t size = ulSize.QuadPart;
    		auto buffer = std::make_unique<char[]>(size);
    		ULONG numRead{};
    		HRESULT hr = pStream->Read(buffer.get(), size, &numRead);
    
    		if (FAILED(hr) || size != numRead)
    		{
    			throw std::exception("Stream conversion failed");
    		}
    
    		cli::array<Byte>^ byteArray = gcnew cli::array<Byte>(numRead+2);
    
    		// convert native pointer to System::IntPtr with C-Style cast
    		Marshal::Copy((IntPtr)buffer.get(), byteArray, 0, numRead);
    
    		return gcnew System::IO::MemoryStream(byteArray);
    
    	}

    IStream* ConvertStreamToIStream(System::IO::Stream^ stream)
    	{
    		cli::array<Byte>^ byteArray = gcnew cli::array<Byte>(stream->Length);
    		stream->Read(byteArray, 0, stream->Length);
    
    		auto buffer = std::make_unique<char[]>(stream->Length);
    		System::Runtime::InteropServices::Marshal::Copy(byteArray, 0, (IntPtr)buffer.get(), stream->Length);
    
    		IStream *pStream = SHCreateMemStream(0, 0);
    		ULONG written{};
    		pStream->Write(buffer.get(), stream->Length, &written);
    
    		if(written!=stream->Length)
    			throw std::exception("Stream conversion failed");
    
    		LARGE_INTEGER offset;
    		offset.QuadPart = 0;
    		ULARGE_INTEGER newpos{};
    		pStream->Seek(offset, STREAM_SEEK_SET, &newpos);
    
    		return pStream;
    	}


    Tuesday, November 26, 2019 9:05 AM