使用 Reflection.Emit 动态发出 `[MarshalAs(...)]`

unk*_*656 5 .net c# cil reflection.emit marshalling

我需要[MarshalAs(UnamangedType.LPWStr)]在运行时使用System.Reflection.Emit. 这需要我创建一个继承类型MulticastDelegate(目前没有问题)、一个适当的构造函数和Invoke带有特殊编组参数的-method。

到目前为止我的 C# 代码:

TypeBuilder type_builder = ...;

MethodBuilder method_builder = type_builder.DefineMethod(
    "Invoke",
    MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Public,
    CallingConventions.Standard,
    typeof(int),
    new[] { typeof(string) } // only an example - this array could hold any number of parameter types
);
ParameterBuilder parameter_1 = invoke.DefineParameter(1, ParameterAttributes.HasFieldMarshal, "arg0");

parameter_1.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(MarshalAsAttribute).GetConstructor(new[] { typeof(UnmanagedType) }),
    new object[] { UnmanagedType.LPWStr }
));

// ...

type_builder.CreateType();
Run Code Online (Sandbox Code Playgroud)

我可以生成动态程序集,但是由于无效的 IL 指令,应用程序在调用“Invoke”方法时崩溃。

在使用我检测到的peverifyildasm-tools进行仔细检查后,我上面的代码发出以下 IL:

.method public hidebysig newslot virtual instance int32 Invoke (
        string arg0
    ) runtime managed
{
    .param [1]
    .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.MarshalAsAttribute::.ctor(
            class [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedType) = (
        01 00 15 00 00 00 05 00 53 08 0c 41 72 72 61 79 /* ...huge binary blob... */ 00 00 00 00
    )

    // ...
}
Run Code Online (Sandbox Code Playgroud)

而以下应该是正确的 IL 代码:

.method public hidebysig newslot virtual instance int32 Invoke (
        string marshal(lpwstr) arg0
    ) runtime managed
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

(即 -marshal(lpwstr)修饰符arg0!)

我的问题

我如何更改上面的代码以发出正确的编组信息?

MSDN 文档没有帮助,因为它总是指过时的方法MethodBuilder.SetMarshal(UnmanagedMarshal)。此外,文档只告诉我“MarshalAs改为发出自定义属性” - 这非常无用,因为这正是我正在做的。

注意:我使用的是 .NET Core 5

编辑:

为委托创建 IL 代码不是问题。我已经对其进行了测试,并且可以完美运行。我遇到的唯一麻烦是正确实施marshal(...).

mon*_*506 2

这个问题已有两年多了,但我我可能已经在这里找到了问题。

据我所知, 用来ParameterAttributes.HasFieldMarshal指示默认的封送行为将用于封送参数,它可能具有需要额外显式封送的字段struct,例如 a包含具有以下属性的字段MarshalAs

public struct MyStruct
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string s; // field has explicit marshaling
}

public delegate void Foo(MyStruct s); // param uses default marshaling behavior
Run Code Online (Sandbox Code Playgroud)

当您创建ParameterBuilder(with DefineParameter) 时,您将确定默认封送行为将用于参数及其字段(相对于MarshalAs每个字段上设置的属性)。然后,对 的调用SetCustomAttribute将被忽略,因为参数的封送行为已经定义。

ParameterAttributes.None正确的行为(如您所描述的)是在定义参数时传递,您稍后将调用该参数SetCustomAttribute来设置封送行为。

(顺便说一句,我最终来到这里是因为我正在尝试同样的事情,但由于误读了文档而传递了错误的参数索引DefineParameter。)