Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make exported DllGetActivationFactory zero-alloc #1399

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Authoring/WinRT.Host.Shim/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static unsafe int GetActivationFactory(IntPtr hstrTargetAssembly, IntPtr
{
return REGDB_E_READREGDB;
}
var GetActivationFactory = type.GetMethod("GetActivationFactory");
var GetActivationFactory = type.GetMethod("GetActivationFactory", new Type[] { typeof(string) });
if (GetActivationFactory == null)
{
return REGDB_E_READREGDB;
Expand Down
2 changes: 1 addition & 1 deletion src/Authoring/WinRT.SourceGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public static int DllGetActivationFactory(void* activatableClassId, void** facto

try
{
IntPtr obj = GetActivationFactory(MarshalString.FromAbi((IntPtr)activatableClassId));
IntPtr obj = GetActivationFactory(MarshalString.FromAbiUnsafe((IntPtr)activatableClassId));

if ((void*)obj is null)
{
Expand Down
16 changes: 16 additions & 0 deletions src/Tests/UnitTest/TestComponentCSharp_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,22 @@ public unsafe void TestFactoryCast()
Assert.Equal("ComImports", MarshalString.FromAbi(hstr));
}

[Fact]
public unsafe void TestFactoryCast_Unsafe()
{
IntPtr hstr;

// Access nonstatic class factory
var instanceFactory = Class.As<IStringableInterop>();
instanceFactory.ToString(out hstr);
Assert.Equal("Class", MarshalString.FromAbiUnsafe(hstr).ToString());

// Access static class factory
var staticFactory = ComImports.As<IStringableInterop>();
staticFactory.ToString(out hstr);
Assert.Equal("ComImports", MarshalString.FromAbiUnsafe(hstr).ToString());
}

[Fact]
public void TestFundamentalGeneric()
{
Expand Down
39 changes: 35 additions & 4 deletions src/WinRT.Runtime/Marshalers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public unsafe IntPtr GetAbi()
{
IntPtr hstring;
Debug.Assert(_header == IntPtr.Zero);
_header = Marshal.AllocHGlobal(Unsafe.SizeOf<HSTRING_HEADER>());
_header = Marshal.AllocHGlobal(sizeof(HSTRING_HEADER));
Marshal.ThrowExceptionForHR(Platform.WindowsCreateStringReference(
(ushort*)chars, value.Length, (IntPtr*)_header, &hstring));
return hstring;
Expand Down Expand Up @@ -171,6 +171,37 @@ public static unsafe string FromAbi(IntPtr value)
return new string(buffer, 0, (int)length);
}

/// <summary>
/// Marshals an input <c>HSTRING</c> value to a <see cref="ReadOnlySpan{T}"/> value.
/// </summary>
/// <param name="value">The input <c>HSTRING</c> value to marshal.</param>
/// <returns>The resulting <see cref="ReadOnlySpan{T}"/> value.</returns>
/// <remarks>
/// <para>
/// This method is equivalent to <see cref="FromAbi"/>, but it does not create a new <see cref="string"/> instance.
/// Doing so makes it zero-allocation, but extra care should be taken by callers to ensure that the returned value
/// does not escape the scope where the source <c>HSTRING</c> is valid.
/// </para>
/// <para>
/// For instance, if this method is invoked in the scope of a method that receives the <c>HSTRING</c> value as one of
/// its parameters, the resulting <see cref="ReadOnlySpan{T}"/> is always valid for the scope of such method. But, if
/// the <c>HSTRING</c> was created by reference in a given scope, the resulting <see cref="ReadOnlySpan{T}"/> value
/// will also only be valid within such scope, and should not be used outside of it.
/// </para>
/// </remarks>
public static unsafe ReadOnlySpan<char> FromAbiUnsafe(IntPtr value)
{
if (value == IntPtr.Zero)
{
return "".AsSpan();
}

uint length;
char* buffer = Platform.WindowsGetStringRawBuffer(value, &length);

return new(buffer, (int)length);
}

public static unsafe IntPtr FromManaged(string value)
{
if (value is null)
Expand Down Expand Up @@ -218,7 +249,7 @@ public static unsafe MarshalerArray CreateMarshalerArray(string[] array)
try
{
var length = array.Length;
m._array = Marshal.AllocCoTaskMem(length * Marshal.SizeOf<IntPtr>());
m._array = Marshal.AllocCoTaskMem(length * sizeof(IntPtr));
m._marshalers = new MarshalString[length];
var elements = (IntPtr*)m._array.ToPointer();
for (int i = 0; i < length; i++)
Expand Down Expand Up @@ -286,7 +317,7 @@ public static unsafe (int length, IntPtr data) FromManagedArray(string[] array)
try
{
var length = array.Length;
data = Marshal.AllocCoTaskMem(length * Marshal.SizeOf<IntPtr>());
data = Marshal.AllocCoTaskMem(length * sizeof(IntPtr));
var elements = (IntPtr*)data;
for (i = 0; i < length; i++)
{
Expand Down Expand Up @@ -1194,7 +1225,7 @@ private static Func<T, IObjectReference> BindCreateMarshaler()

#if EMBED
internal
#else
#else
public
#endif
static class MarshalInspectable<
Expand Down
3 changes: 2 additions & 1 deletion src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,5 @@ TypesMustExist : Type 'WinRT.StructTypeDetails<T, TAbi>' does not exist in the r
MembersMustExist : Member 'public void WinRT.WindowsRuntimeTypeAttribute..ctor(System.String, System.String)' does not exist in the reference but it does exist in the implementation.
MembersMustExist : Member 'public System.String WinRT.WindowsRuntimeTypeAttribute.GuidSignature.get()' does not exist in the reference but it does exist in the implementation.
TypesMustExist : Type 'WinRT.WinRTExposedTypeAttribute' does not exist in the reference but it does exist in the implementation.
Total Issues: 121
MembersMustExist : Member 'public System.ReadOnlySpan<System.Char> WinRT.MarshalString.FromAbiUnsafe(System.IntPtr)' does not exist in the reference but it does exist in the implementation.
Total Issues: 122
12 changes: 10 additions & 2 deletions src/cswinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -9450,14 +9450,16 @@ namespace WinRT
{
% static partial class Module
{
public static unsafe IntPtr GetActivationFactory(String runtimeClassId)
public static unsafe IntPtr GetActivationFactory(% runtimeClassId)
{%
return IntPtr.Zero;
}
%
}
}
)",
internal_accessibility(),
settings.netstandard_compat ? "string" : "ReadOnlySpan<char>",
bind_each([](writer& w, TypeDef const& type)
{
w.write(R"(
Expand All @@ -9473,7 +9475,13 @@ bind<write_type_name>(type, typedef_name_type::CCW, true)
);
},
types
));
),
settings.netstandard_compat ? "// No ReadOnlySpan<char> overload available" : R"(
public static IntPtr GetActivationFactory(string runtimeClassId)
{
return GetActivationFactory(runtimeClassId.AsSpan());
}"
)");
}

void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics)
Expand Down
Loading