diff --git a/core/interop/source-generation/ComWrappersGeneration/.gitignore b/core/interop/source-generation/ComWrappersGeneration/.gitignore
new file mode 100644
index 00000000000..3195f839657
--- /dev/null
+++ b/core/interop/source-generation/ComWrappersGeneration/.gitignore
@@ -0,0 +1 @@
+OutputFiles
diff --git a/core/interop/source-generation/ComWrappersGeneration/Client/Client.csproj b/core/interop/source-generation/ComWrappersGeneration/Client/Client.csproj
new file mode 100644
index 00000000000..f35c6e9ddde
--- /dev/null
+++ b/core/interop/source-generation/ComWrappersGeneration/Client/Client.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/core/interop/source-generation/ComWrappersGeneration/Client/Program.cs b/core/interop/source-generation/ComWrappersGeneration/Client/Program.cs
new file mode 100644
index 00000000000..11fcf8a6b72
--- /dev/null
+++ b/core/interop/source-generation/ComWrappersGeneration/Client/Program.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+[assembly: DisableRuntimeMarshalling]
+
+namespace Tutorial;
+
+public class Program
+{
+ public static unsafe void Main(string[] args)
+ {
+ var clsid = new Guid(Clsids.Calculator);
+ var iid = new Guid(ICalculator.IID);
+ Console.WriteLine($"Client: Requesting a Calculator (CLSID {clsid}) with ICalculator (IID {iid})");
+ int hr = Ole32.CoCreateInstance(ref clsid, /* No aggregation */ 0, (uint)Ole32.CLSCTX.CLSCTX_INPROC_SERVER, ref iid, out object comObject);
+ Marshal.ThrowExceptionForHR(hr);
+ ICalculator calculator = (ICalculator) comObject;
+
+ int a = 5;
+ int b = 3;
+ int c;
+ c = calculator.Add(a, b);
+ Console.WriteLine($"Client: {a} + {b} = {c}");
+ c = calculator.Subtract(a, b);
+ Console.WriteLine($"Client: {a} - {b} = {c}");
+ }
+}
+
+internal static unsafe partial class Ole32
+{
+ // https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
+ [LibraryImport(nameof(Ole32))]
+ public static partial int CoCreateInstance(
+ ref Guid rclsid,
+ nint pUnkOuter,
+ uint dwClsContext,
+ ref Guid riid,
+ // The default ComInterfaceMarshaller will unwrap a .NET object if it can tell the COM instance is a ComWrapper.
+ // This causes issues when casting to ICalculator, since the Server's Calculator class doesn't implement the Client's interface.
+ // UniqueComInterfaceMarshaller doesn't try to unwrap the object and always creates a new COM object.
+ [MarshalUsing(typeof(UniqueComInterfaceMarshaller