Skip to content

[API Proposal]: Provide BSTR marshaller for LibraryImport source generator #69021

@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

Background and motivation

Add BSTR marshaller in addition to new string marshallers in #67635. Since this marshaller is for adoption purposes it is similar to the existing ANSI marshaller and will require users to manually define it using MarshalUsing attribute or can rely on the UnmanagedType.BStr value in a MarshalAs.

API Proposal

Implementation PR - #69213

namespace System.Runtime.InteropServices.Marshalling
{
    /// <summary>
    /// Marshaller for BSTR strings
    /// </summary>
    [CLSCompliant(false)]
    [CustomTypeMarshaller(typeof(string), BufferSize = 0x100,
        Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)]
    public unsafe ref struct BStrStringMarshaller
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BStrStringMarshaller"/>.
        /// </summary>
        /// <param name="str">The string to marshal.</param>
        public BStrStringMarshaller(string? str);

        /// <summary>
        /// Initializes a new instance of the <see cref="BStrStringMarshaller"/>.
        /// </summary>
        /// <param name="str">The string to marshal.</param>
        /// <param name="buffer">Buffer that may be used for marshalling.</param>
        /// <remarks>
        /// The <paramref name="buffer"/> must not be movable - that is, it should not be
        /// on the managed heap or it should be pinned.
        /// <seealso cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/>
        /// </remarks>
        public BStrStringMarshaller(string? str, Span<ushort> buffer);

        /// <summary>
        /// Returns the native value representing the string.
        /// </summary>
        /// <remarks>
        /// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
        /// </remarks>
        public void* ToNativeValue();

        /// <summary>
        /// Sets the native value representing the string.
        /// </summary>
        /// <param name="value">The native value.</param>
        /// <remarks>
        /// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
        /// </remarks>
        public void FromNativeValue(void* value);

        /// <summary>
        /// Returns the managed string.
        /// </summary>
        /// <remarks>
        /// <seealso cref="CustomTypeMarshallerDirection.Out"/>
        /// </remarks>
        public string? ToManaged();

        /// <summary>
        /// Frees native resources.
        /// </summary>
        /// <remarks>
        /// <seealso cref="CustomTypeMarshallerFeatures.UnmanagedResources"/>
        /// </remarks>
        public void FreeNative();
    }
}

API Usage

The following is an example from aspnetcore where this new marshaller could be used.

[LibraryImport(AspNetCoreModuleDll)]
private static partial int http_get_server_variable(
    NativeSafeHandle pInProcessHandler,
    [MarshalAs(UnmanagedType.LPStr)] string variableName,
    [MarshalUsing(typeof(BStrStringMarshaller))] out string value);

// or

[LibraryImport(AspNetCoreModuleDll)]
private static partial int http_get_server_variable(
    NativeSafeHandle pInProcessHandler,
    [MarshalAs(UnmanagedType.LPStr)] string variableName,
    [MarshalAs(UnmanagedType.BStr)] out string value);

Alternative Designs

No response

Risks

None. This is a new opt-in API.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions