CsWinRT
CsWinRT copied to clipboard
Marshalling blittable arrays should just pin them, not use GCHandle-s
Proposal: marshalling blittable arrays should just pin them, not use GCHandle-s
Summary
If you have an API taking an array, like this:
void Foo(byte[] body);
Today CsWinRT will generate something like this:
public static unsafe void Foo(IObjectReference _obj, byte[] body)
{
var ThisPtr = _obj.ThisPtr;
MarshalBlittable<byte>.MarshalerArray __body = default;
int __body_length = default;
IntPtr __body_data = default;
try
{
__body = MarshalBlittable<byte>.CreateMarshalerArray(body);
(__body_length, __body_data) = MarshalBlittable<byte>.GetAbiArray(__body);
global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]<IntPtr, int, IntPtr, int>**)ThisPtr)[6](ThisPtr, __body_length, __body_data));
}
finally
{
MarshalBlittable<byte>.DisposeMarshalerArray(__body);
}
}
This is unnecessarily expensive, as the marshalling code is creating a temporary pinned GCHandle.
Wherever we're just marshalling SZ arrays (which should pretty much always be the case?) we can just pin them.
Essentially, arrays could be marshalled similarly to string parameters. Something like this:
public static unsafe void Foo(IObjectReference _obj, byte[] body)
{
var ThisPtr = _obj.ThisPtr;
int __body_length = body?.Length ?? 0;
fixed(byte* ___body = __body)
{
global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]<IntPtr, int, byte*, int>**)ThisPtr)[6](ThisPtr, __body_length, ___body));
}
}
Rationale
- Results in faster code
- No longer needs to allocate and dispose a
GCHandle - No longer needs the
finallyblock for cleanup (ie. to release the marshaller)
Alternatives
It may be fine to keep using the marshaller (like in the case of string parameters), but we should at least find a way (maybe a different overload?) to make it not allocate/dispose GCHandle values, and rather just emit a fixed statement in callers.