从 Action 指令创建 DynamicMethod

2024-02-28

我正在使用 DynamicMethod 并旨在执行以下操作:

我有一个 Action,从中可以使用字节形式获取 IL 代码GetILAsByteArray()。从这个字节我想创建一个动态方法并执行。这是我正在尝试做的事情的一个例子:

class Program
{
    static void Main(string[] args)
    {
        //Create action and execute
        Action<string> myAction = s =>
        {
            Console.WriteLine("Hello " + s);
        };
        myAction("World");
        //Get IL bytes
        byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray();
        DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) });
        DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo();
        dynamicIlInfo.SetCode(ilBytes, 100);
        dynamicCallback.Invoke(null, new object[] { "World" });
    }
}

当呼叫dynamicCallback.Invoke(null, new object[] { "World" })我们收到“抛出异常:mscorlib.dll 中的‘System.BadImageFormatException’”。

我不知道的一件事是我应该使用什么作为第二个参数SetCode(),“maxStackSize”应该使用什么?如何设置与初始操作相同的值?但我认为这不是例外的原因。

如何从 IL 字节正确创建动态方法?


Solution

这里我总结一下Dudi Keleti提供的完整解决方案:

static void Main(string[] args)
{
    Action<string> myAction = s =>
    {
        Console.WriteLine("Hello " + s);
    };
    MethodInfo method = myAction.GetMethodInfo();
    object target = myAction.Target;

    DynamicMethod dm = new DynamicMethod(
        method.Name,
        method.ReturnType,
        new[] {method.DeclaringType}.
            Concat(method.GetParameters().
                Select(pi => pi.ParameterType)).ToArray(),
        method.DeclaringType,
        skipVisibility: true);

    DynamicILInfo ilInfo = dm.GetDynamicILInfo();
    var body = method.GetMethodBody();
    SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper();
    foreach (LocalVariableInfo lvi in body.LocalVariables)
    {
       sig.AddArgument(lvi.LocalType, lvi.IsPinned);
    }
    ilInfo.SetLocalSignature(sig.GetSignature());
    byte[] code = body.GetILAsByteArray();
    ILReader reader = new ILReader(method);
    DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code);
    reader.Accept(visitor);
    ilInfo.SetCode(code, body.MaxStackSize);

    dm.Invoke(target, new object[] { target, "World" });

    Console.ReadLine(); //Just to see the result
}

注意:DynamicMethodHelper 是由 Haibo Luo 开发的类,并在博客文章 https://blogs.msdn.microsoft.com/haibo_luo/2006/11/07/turn-methodinfo-to-dynamicmethod/但也可以直接下载here https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/01/02/35/08/DynamicMethodHelper.cs.


你可以这样做:

byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);

ILReader是一门为你做艰苦工作的课程。您可以从以下位置复制它here https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/01/00/37/88/ILReader.zip.

Example:

MethodInfo method = ...
DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     method.GetParameters.Select(pi => pi.ParameterType).ToArray(),
     method.DeclaringType,
     skipVisibility: true\fasle - depends of your need);

DynamicILInfo ilInfo = dm.GetDynamicILInfo();
var body = method.GetMethodBody();
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper();
foreach(LocalVariableInfo lvi in body.LocalVariables)
{
    sig.AddArgument(lvi.LocalType, lvi.IsPinned);
}
ilInfo.SetLocalSignature(sig.GetSignature());
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);

如果您的方法是一个简单方法(不是通用的并且没有异常句柄),则 thid 应该可以工作。

如果您的方法是通用方法,则需要执行以下操作以将所有者类型传递给 DynamicMethod 构造函数:

var owner = method.DeclaringType.MakeGenericType(
             method.DeclaringType.GetGenericArguments());

还有一件事,如果它仍然不起作用,并且您的方法是实例方法,请将方法的实例类型传递到参数数组的第一个单元格中DynamicMethod构造函数。

Update

你无法通过null here dm.Invoke(**null**, new object[] { "World" });因为myAction不是静态方法。

myAction (Action<string>) 实际上是新生成的类中保存该方法的方法。

但我检查了一下,即使我通过了,异常也会抛出myAction.Target或该类型的新实例。异常(CLR 检测到无效程序)告诉您 IL 不完全正确。我现在无法确切地告诉您问题出在哪里,但如果这对您很重要,我可以在下周回去工作时检查一下。

无论如何,如果您只想查看 DynamicIlInfo.SetCode 的实际效果,您可以按原样使用代码,但只需更改方法信息即可:

class Program
{        
    static void Main(string[] args)
    {
        Action<string> myAction = s =>
        {
            Console.WriteLine("Hello " + s);
        };
        MethodInfo method = myAction.GetMethodInfo();

        //Rest of your code
    }
}

to this:

class Program
{
    static void M(string s)
    {
        Console.WriteLine("Hello " + s);
    }

    static void Main(string[] args)
    {
        MethodInfo method = typeof (Program).GetMethod("M", BindingFlags.Static | BindingFlags.NonPublic);

        //Rest of your code
    }
}

更新2:

显然我昨天很累,我没有意识到你的错误。

正如我在原来的答案中所写的,

还有一件事,如果它仍然不起作用,并且您的方法是实例方法,请将方法的实例类型传递到参数数组的第一个单元格中DynamicMethod构造函数。

所以你需要这样做:

DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     new[] {method.DeclaringType}.
        Concat(method.GetParameters().
        Select(pi => pi.ParameterType)).ToArray(),
     method.DeclaringType,
     skipVisibility: true);

并像这样调用动态方法:

dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" });

现在工作完美了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 Action 指令创建 DynamicMethod 的相关文章

随机推荐

  • 正则表达式替换 sublime 中替换中包含部分匹配的值?

    我想出了这个正则表达式来查找所有以 并包含 下划线 w w 我基本上是在寻找变量 比如 var foo etc 如何使用正则表达式组替换内容 例如 如何删除下划线并使下一个字母大写 例如 varFoo 替换表达式为 1 u 2 1 2是捕获
  • 如何使用 Raphael.JS 根据鼠标移动绘制一条线

    我试图随着鼠标在纸上移动画一条线 我只知道如何使用画线path 但想知道是否有人有想法来画一条线mousemove 请帮我解决这个问题 这是使用 raphael 的此类示例的链接 与拉斐尔一起绘画 图片来源 Jonas http irunm
  • 本地域与本地主机的性能

    TCP 连接之间是否存在性能差异 本地主机 127 0 0 1 解析到本地计算机的域 或者更具体地说 后面的连接是通过环回设备还是通过实际网络 我问的原因是我正在考虑更改许多 PHP 应用程序中的数据库设置 以便它们使用完整的域而不是本地主
  • 如何在telnet客户端实现tab补全

    我有一个为 telnet 客户端打开连接的服务器 例如 例如 我运行服务器 server在另一个窗口中 我将 telnet 客户端作为 telnet localhost 9999 运行 当我运行 telnet 客户端时 我将收到新的 CLI
  • 转到 mod 供应商而不更新到最新版本

    我试图弄清楚是否可以在没有 go 工具更新我的 go mod 文件的情况下运行 go modvendor 我特别go get package subpackage commit并提交我的go mod使用正确的版本 然后我跑go mod ve
  • 获取 CLPlacemark 的正确缩放区域

    我在用着MKLocalSearch搜索某些地方 例如城市或城市中的街道 并将其显示在MKMapView 我像这样显示地标 let loc placemark location CLLocation of CLPlacemark var ma
  • 根目录中的新 git 存储库包含子目录中的现有存储库

    我正在开发一个目前不受任何修订控制的大型代码库 是的 确实如此 我一直在一个深层目录中开发一个组件 并创建了一个隐蔽的 git 存储库来跟踪我的更改 我现在想将所有代码添加到源代码管理中 并为整个目录树创建一个存储库 我希望新的外部存储库包
  • 使用 Web Audio Api 播放 Stream 中的点击声音

    我有一个奇怪的问题 我正在使用网络音频播放来自服务器的流 我通过以下方式做到这一点 var d2 new DataView evt data var data new Float32Array d2 byteLength Float32Ar
  • 如何使用 iTextSharp 缩放 PDF 页面,同时保持方向?

    如何在 itextsharp 中保持旋转的同时缩放 pdf 页面 我有以下内容 但我失去了旋转 public static void ScaleToLetter string inPDF string outPDF PdfReader re
  • 如何在不连续重置的情况下将esp8266从深度睡眠中唤醒

    我正在使用 esp8266 构建 IR 到 wifi 桥接器 基本上 我正在构建一个连接到红外遥控器内的 esp8266 的红外接收器 以通过 wifi 将接收到的红外远程按键转发到服务器 我使用的是 Wemos D1 Mini 我已经让
  • 如何在IE10中使用css线性渐变?

    在 IE10 中 我试图创建一个 css 线性渐变 从页面顶部到页面底部 这就是我到目前为止所拥有的
  • XML 解析错误:XML 或文本声明不在 php 中实体的开头

    您好 我正在 php 中生成 xml 文件 但出现错误 XML 解析错误 XML 或文本声明不在实体的开头 我的代码是
  • 某些设备在下班后收到 Gcm 通知

    我正在尝试使用 gcm 通知 我的服务器代码工作正常 并且我获得了成功作为确认 问题是通知发送正确 1 在大多数设备中 通知是立即收到的 在谷歌 Nexus 索尼手机上测试 2 其他设备也在几个小时后收到通知 是的 hours 在 Karb
  • Swift:IAP 更新交易未在 .Purchased 上调用

    我的代码有问题 updateTransactions 函数仅在交易进行时调用一次 交易完成后不再调用 func buyProduct product SKProduct let payment SKPayment product produ
  • Python PyQt - QTableWidget、JSON 和 emitSignal 导致空白单元格

    我将 PyQt 用于一个简单的应用程序 该应用程序从包含 JSON 格式字符串的日志文件中读取数据 并将它们很好地输出到表中 一切都按预期工作 除非我尝试从 加载 函数发出信号 该信号由主窗口拾取 位于一个设计为用新信息重新排列表的槽中 如
  • Lucene TermQuery 和 QueryParser

    我有 2 个 lucene 查询 1 Term term new Term Properties LUCENE APPARTMENT ADDRESS address Query termQuery new TermQuery term To
  • Flutter showDialog 未在 PopupMenuItem 点击上显示

    我在用着PopupMenuButton在我的应用程序中 我想要showDialog在点击一个PopupMenuItem My PopupMenuItem PopupMenuItem child Text Show dialog onTap
  • setState 方法导致 Reactjs 函数中的结果无限循环

    该代码显示数组中的用户记录 我还创建了一个updateCount 功能对内容显示的用户进行计数 我可以看到计数结果alerted并在控制台中 现在我想显示计数结果 为此 我初始化setState 内的方法updateCount功能 upda
  • PHP 和 Java 中的 SHA256

    我正在将一些 Java 代码移植到 PHP 代码 在Java中我有一个哈希SHA256代码如下 public static String hashSHA256 String input throws NoSuchAlgorithmExcep
  • 从 Action 指令创建 DynamicMethod

    我正在使用 DynamicMethod 并旨在执行以下操作 我有一个 Action 从中可以使用字节形式获取 IL 代码GetILAsByteArray 从这个字节我想创建一个动态方法并执行 这是我正在尝试做的事情的一个例子 class P