Skip to content

Commit

Permalink
Merge pull request #6205 from dotnet/main
Browse files Browse the repository at this point in the history
  • Loading branch information
BillWagner authored Sep 22, 2023
2 parents 4542687 + b410f7d commit 4450b4a
Show file tree
Hide file tree
Showing 12 changed files with 665 additions and 0 deletions.
6 changes: 6 additions & 0 deletions core/interop/Streams/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<!-- Shared properties -->
<PropertyGroup>
<DefaultTargetFramework>net8.0</DefaultTargetFramework>
</PropertyGroup>
</Project>
29 changes: 29 additions & 0 deletions core/interop/Streams/ManagedLib/ManagedLib.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(DefaultTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOsPlatform('Windows'))">$(TargetFrameworks);net48</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<!-- COM source generator -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>

<!-- DNNE -->
<EnableDynamicLoading>true</EnableDynamicLoading>
<DnneAddGeneratedBinaryToProject>true</DnneAddGeneratedBinaryToProject>
</PropertyGroup>

<ItemGroup>
<Compile Include="../Shared/*.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="DNNE" Version="2.0.5" />

<PackageReference Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" Include="System.Memory" Version="4.5.5" />
</ItemGroup>

</Project>
116 changes: 116 additions & 0 deletions core/interop/Streams/ManagedLib/StreamAsCExports.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace InteropWithStream;

/// <summary>
/// Stream data type projected as an opaque handle and interacted via C exports.
/// </summary>
public static unsafe partial class StreamExports
{
#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static IntPtr C_CreateStream()
{
MemoryStream stream = new();
return GCHandle.ToIntPtr(GCHandle.Alloc(stream));
}

#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static void C_DeleteStream(IntPtr streamRaw)
{
if (streamRaw != IntPtr.Zero)
{
GCHandle.FromIntPtr(streamRaw).Free();
}
}

#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static int C_PrintStream(IntPtr streamRaw)
{
try
{
var stream = (MemoryStream)GCHandle.FromIntPtr(streamRaw).Target!;
var data = stream.ToArray();
const int row = 4;
for (int i = 0; i < data.Length; ++i)
{
char delim = (i % row == row - 1) ? '\n' : ' ';
Console.Write($"{data[i]}{delim}");
}
Console.WriteLine();
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}

//
// APIs for interacting with Streams via C export
//

#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static int C_Stream_Read(IntPtr streamRaw, nint length, byte* dataRaw, nint* dataRead)
{
Debug.Assert(length <= int.MaxValue);
try
{
var stream = (Stream)GCHandle.FromIntPtr(streamRaw).Target!;
Span<byte> data = new(dataRaw, (int)length);
#if NETFRAMEWORK
var buffer = new byte[data.Length];
*dataRead = (nint)stream.Read(buffer, 0, buffer.Length);
buffer.CopyTo(data);
#else
*dataRead = (nint)stream.Read(data);
#endif // !NETFRAMEWORK
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}

#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static int C_Stream_Write(IntPtr streamRaw, nint length, byte* dataRaw)
{
Debug.Assert(length <= int.MaxValue);
try
{
var stream = (Stream)GCHandle.FromIntPtr(streamRaw).Target!;
ReadOnlySpan<byte> data = new(dataRaw, (int)length);
#if NETFRAMEWORK
stream.Write(data.ToArray(), 0, data.Length);
#else
stream.Write(data);
#endif // !NETFRAMEWORK
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}
}
172 changes: 172 additions & 0 deletions core/interop/Streams/ManagedLib/StreamAsIStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System.Runtime.InteropServices;

#if NETFRAMEWORK
using IStream = System.Runtime.InteropServices.ComTypes.IStream;
using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
#else
using System.Runtime.InteropServices.Marshalling;
using IStream = InteropWithStream.IStream;
using STATSTG = InteropWithStream.STATSTG;
#endif // !NETFRAMEWORK

namespace InteropWithStream;

/// <summary>
/// Stream data type projected as IStream instance.
/// </summary>
public static unsafe partial class StreamExports
{
#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static int IStream_CreateStream(void** istream)
{
try
{
MemoryStream stream = new();
#if NETFRAMEWORK
*istream = (void*)Marshal.GetIUnknownForObject(new StreamWrapper(stream));
#else
*istream = ComInterfaceMarshaller<StreamWrapper>.ConvertToUnmanaged(new StreamWrapper(stream));
#endif // !NETFRAMEWORK
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}

#if NETFRAMEWORK
[DNNE.Export]
#else
[UnmanagedCallersOnly]
#endif // !NETFRAMEWORK
public static int IStream_PrintStream(void* streamMaybe)
{
try
{
#if NETFRAMEWORK
var streamWrapper = (StreamWrapper)Marshal.GetObjectForIUnknown((IntPtr)streamMaybe);
#else
var streamWrapper = ComInterfaceMarshaller<StreamWrapper>.ConvertToManaged(streamMaybe);
#endif // !NETFRAMEWORK
if (streamWrapper is null)
{
throw new NotSupportedException();
}

var stream = streamWrapper.Stream as MemoryStream;
if (stream is null)
{
throw new NotSupportedException();
}

var data = stream.ToArray();
const int row = 4;
for (int i = 0; i < data.Length; ++i)
{
char delim = (i % row == row - 1) ? '\n' : ' ';
Console.Write($"{data[i]}{delim}");
}
Console.WriteLine();
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}

#if NETFRAMEWORK
[ComVisible(true)]
sealed partial class StreamWrapper : IStream
{
public StreamWrapper(Stream s)
{
this.Stream = s;
}

public Stream Stream { get; private set; }

// ISequentialStream
public void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] byte[] pv, int cb, IntPtr pcbRead)
{
*(int*)pcbRead = Stream.Read(pv, 0, pv.Length);
}

public void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbWritten)
{
Stream.Write(pv, 0, pv.Length);
*(int*)pcbWritten = pv.Length;
}

// IStream
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
=> throw new NotImplementedException();
public void SetSize(long libNewSize)
=> throw new NotImplementedException();
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
=> throw new NotImplementedException();
public void Commit(int grfCommitFlags)
=> throw new NotImplementedException();
public void Revert()
=> throw new NotImplementedException();
public void LockRegion(long libOffset, long cb, int dwLockType)
=> throw new NotImplementedException();
public void UnlockRegion(long libOffset, long cb, int dwLockType)
=> throw new NotImplementedException();
public void Stat(out STATSTG pstatstg, int grfStatFlag)
=> throw new NotImplementedException();
public void Clone(out IStream ppstm)
=> throw new NotImplementedException();
}
#else
[GeneratedComClass]
sealed partial class StreamWrapper : IStream
{
public StreamWrapper(Stream s)
{
this.Stream = s;
}

public Stream Stream { get; private set; }

// ISequentialStream
public void Read(byte* pv, uint cb, out uint pcbRead)
{
Span<byte> data = new(pv, (int)cb);
pcbRead = (uint)Stream.Read(data);
}

public void Write(byte* pv, uint cb, out uint pcbWritten)
{
ReadOnlySpan<byte> data = new(pv, (int)cb);
Stream.Write(data);
pcbWritten = (uint)data.Length;
}

// IStream
public void Seek(long dlibMove, uint dwOrigin, ulong plibNewPosition)
=> throw new NotImplementedException();
public void SetSize(ulong libNewSize)
=> throw new NotImplementedException();
public void CopyTo(IStream pstm, ulong cb, out ulong pcbRead, out ulong pcbWritten)
=> throw new NotImplementedException();
public void Commit(uint grfCommitFlags)
=> throw new NotImplementedException();
public void Revert()
=> throw new NotImplementedException();
public void LockRegion(ulong libOffset, ulong cb, uint dwLockType)
=> throw new NotImplementedException();
public void UnlockRegion(ulong libOffset, ulong cb, uint dwLockType)
=> throw new NotImplementedException();
public void Stat(out STATSTG pstatstg, uint grfStatFlag)
=> throw new NotImplementedException();
public void Clone(out IStream ppstm)
=> throw new NotImplementedException();
}
#endif // !NETFRAMEWORK
}
Loading

0 comments on commit 4450b4a

Please sign in to comment.