Skip to content

LibraryImportAttribute for p/invoke source generation #46822

Closed
@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

Background and Motivation

The DllImportAttribute used in P/Invoke scenarios relies on a built-in system of dynamic IL generation to support marshalling of arguments to to a native environment. A new attribute that would permit the static IL generation
is proposed. Scenario details and overall design can be found here.

Proposed API

The proposed shape for LibraryImportAttribute is based on the existing DllImportAttribute, but looks to remove the inconsistent or platform-specific aspects.

This attribute is intended to be emitted by the p/invoke source generator rather than added to the runtime libraries.

Related proposal: #46838

namespace System.Runtime.InteropServices
{
    /// <summary>
    /// Specifies how strings should be marshalled
    /// </summary>
    public enum StringMarshalling
    {
        None = 0,
        Utf8,   // UTF-8
        Utf16,  // UTF-16, machine-endian
    }

    /// <summary>
    /// Attribute used to indicate a Source Generator should create a function for marshaling
    /// arguments instead of relying on the CLR to generate an IL Stub at runtime.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class LibraryImportAttribute : Attribute
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="libraryName">Name of the library containing the import</param>
        public LibraryImportAttribute(string libraryName);

        /// <summary>
        /// Library to load.
        /// </summary>
        public string LibraryName { get; }

        /// <summary>
        /// Indicates the name or ordinal of the entry point to be called.
        /// </summary>
        /// <see cref="System.Runtime.InteropServices.DllImportAttribute.EntryPoint"/>
        public string? EntryPoint { get; set; }

        /// <summary>
        /// Indicates how to marshal string parameters to the method.
        /// </summary>
        /// <remarks>
        /// If this field is specified, <see cref="MarshalStringsUsing" /> must not be specified.
        /// </remarks>
        public StringMarshalling StringMarshalling { get; set; }

        /// <summary>
        /// Indicates how to marshal string parameters to the method.
        /// </summary>
        /// <remarks>
        /// If this field is specified, <see cref="StringMarshalling" /> must not be specified.
        /// The type should be one that conforms to usage with the attributes:
        /// <see cref="System.Runtime.InteropServices.MarshalUsingAttribute"/>
        /// <see cref="System.Runtime.InteropServices.NativeMarshallingAttribute"/>
        /// </remarks>
        public Type? MarshalStringsUsing { get; set; }

        /// <summary>
        /// Indicates whether the callee sents an error (SetLastError on Windows or errorno
        /// on other platforms) before returning from the attributed method.
        /// </summary>
        /// <see cref="System.Runtime.InteropServices.DllImportAttribute.SetLastError"/>
        public bool SetLastError { get; set; }
    }
}

Alternate names for the attribute:

  • ForeignFunctionAttribute
  • GeneratedDllImportAttribute
  • InteropImportAttribute
  • NativeLibraryImportAttribute
  • PInvokeImportAttribute
    ...

Usage Examples

Usage of LibraryImportAttribute would be similar to that of DllImportAttribute, but would also require a source generator be referenced during build. The attributed method and its containing types must also be partial. The source generator can be found in DllImportGenerator.

[LibraryImport("NativeBinary")]
internal static partial int Sum(int a, int b);

[LibraryImport("NativeBinary", StringMarshalling = StringMarshalling.Utf16)]
internal static partial string GetValue(string name);

[LibraryImport("NativeBinary", MarshalStringsUsing = typeof(MyStringMarshal.Wtf8String))]
internal static partial string GetValue(string name);

namespace MyStringMarshal
{
    struct Wtf8String { ... }
}

Analyzer/Generator diagnostics

Risks

Attributes are typically static metadata so this will increase the amount of metadata for a compiled assembly. The DllImportAttribute is actually a pseudo-attribute and does not have the same metadata cost as a typical .NET Attribute like the one proposed - note that the resulting generated code will vastly out-weigh the cost of this new metadata.

In terms of UX, a user would hit a build failure if one uses the attribute but doesn't reference a source code generator. There are also differences in compatibility/support between the built-in system and the proposed source generator - documented here - which users may hit. Users will get a generator diagnostic if trying to use the generator in these cases.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions