目录

一:普通写法

二:注入定义

三:Weave函数

四:参数构造

五:业务编写

六:注入调用

7.  怎么调用别的程序集的方法示例

8. [is declared in another module and needs to be imported的解决方法:

一:普通写法

1
2
3
4
public static string GetPoint(int x, int y)
 {
    var value=x;
}

哇 好简单啊。其实动态获取和我们普通这样写代码是一样的,我们把要注入的代码,生成一个接收的变量就可以了。 

就像上面value 一样接收,然后传递给我们自己函数就可以了。

二 :注入定义

  public class WeaveService : Attribute
{
}
public class WeaveAction : Attribute
{
}
public class Log : WeaveAction
{
public static void OnActionBefore(MethodBase mbBase, object[] args)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine(string.Format("{0}方法,第{1}参数是:{2}",mbBase.Name,i, args[i]));
}
}
}

WeaveService WeaveAction 2个Attribute是注入的标记,方便我们在注入查找快速定位。

OnActionBefore是我们的接收函数,arg就是函数运行时的参数。

三 :Weave函数

这块代码在上一篇已经有详细注释了,这里不多描述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static void Weave(string[] assemblyPath)
       {
           foreach (var item in assemblyPath)
           {
               var assembly = AssemblyDefinition.ReadAssembly(item);
 
               var types = assembly.MainModule.Types.Where(n => n.CustomAttributes.Any(y => y.AttributeType.Resolve().Name == "WeaveService"));
 
               foreach (var type in types)
               {
                   foreach (var method in type.Methods)
                   {
                       var attrs =
                           method.CustomAttributes.Where(y => y.AttributeType.Resolve().BaseType.Name == "WeaveAction");
                       foreach (var attr in attrs)
                       {
                           var resolve = attr.AttributeType.Resolve();
                           var ilProcessor = method.Body.GetILProcessor();
                           var firstInstruction = ilProcessor.Body.Instructions.First();
                           var onActionBefore = resolve.GetMethods().Single(n => n.Name == "OnActionBefore");
                           var mfReference = assembly.MainModule.Import(typeof(System.Reflection.MethodBase).GetMethod("GetCurrentMethod"));
                           ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, mfReference));
 
                           MakeArrayOfArguments(method, firstInstruction, ilProcessor, 0, method.Parameters.Count, assembly);
                           ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, onActionBefore));
                       }
                   }
               }
               if (types.Any())
               {
                   assembly.Write(item);
               }
           }
       }

四 :参数构造

动态获取函数参数的函数,代码有详细注释。

 1    /// <summary>
2 /// 构建函数参数
3 /// </summary>
4 /// <param name="method">要注入的方法</param>
5 /// <param name="firstInstruction">函数体内第一行指令认 IL_0000: nop</param>
6 /// <param name="writer">mono IL处理容器</param>
7 /// <param name="firstArgument">默认第0个参数开始</param>
8 /// <param name="argumentCount">函数参数的数量,静态数据可以拿到</param>
9 /// <param name="assembly">要注入的程序集</param>
10 public static void MakeArrayOfArguments(MethodDefinition method, Instruction firstInstruction, ILProcessor writer, int firstArgument,
11 int argumentCount, AssemblyDefinition assembly)
12 {
13 //实例函数第一个参数值为this(当前实例对象),所以要从1开始。
14 int thisShift = method.IsStatic ? 0 : 1;
15
16 if (argumentCount > 0)
17 {
18 //我们先创建个和原函数参数,等长的空数组。
19 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldc_I4, argumentCount - firstArgument));
20 //然后实例object数组,赋值给我们创建的数组
21 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Newarr,
22 assembly.MainModule.Import(typeof(object))));
23
24 //c#代码描述
25 //object[] arr=new object[argumentCount - firstArgument]
26 for (int i = firstArgument; i < argumentCount; i++) //遍历参数
27 {
28 var parameter = method.Parameters[i];
29
30 //在堆栈上复制一个值
31 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Dup));
32 //将常量 i - firstArgument 进行压栈,数组[i - firstArgument] 这个东东。
33 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldc_I4, i - firstArgument));
34 //将第i + thisShift个参数 压栈。
35 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldarg, (short)(i + thisShift)));
36 //装箱成object
37 ToObject(assembly, firstInstruction, parameter.ParameterType, writer);
38 //压栈给数组 arr[i]赋值
39 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Stelem_Ref));
40
41 //c#代码描述
42 // arr[i]=value;
43 }
44 }
45 else
46 {
47 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldnull));
48 }
49 }
50 public static void ToObject(AssemblyDefinition assembly, Instruction firstInstruction, TypeReference originalType, ILProcessor writer)
51 {
52 if (originalType.IsValueType)
53 {
54 //普通值类型进行装箱操作
55 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Box, originalType));
56 }
57 else
58 {
59 if (originalType.IsGenericParameter)
60 {
61 //集合装箱
62 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Box, assembly.MainModule.Import(originalType)));
63 }
64
65 }
66 }

介绍下mono InsertBefore这个函数。 这个函数是在某个指令之前插入指令。来张图

通过图我们看出,第一行指令是IL_0000: nop 。 第一行追加了 ldc.i4 2 指令,第二行我们还是nop 之前追加。 自上而下

五:业务编写

我定义个要注入的用户类,然后标记下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[WeaveService]
  public static class UserManager
  {
 
      [Log]
      public static string GetUserName(int userId, string memberid)
      {
          return "成功";
      }
      [Log]
      public static string GetPoint(int x, int y)
      {
          var sum = x + y;
 
          return "用户积分: " + sum;
      }
  }

我们平常的业务写法,不需要多余操作。

1
2
3
4
5
6
7
8
9
public static void Main(string[] args)
       {
          
           UserManager.GetUserName(1,"v123465");
     
           UserManager.GetPoint(2, 3);
 
           Console.ReadLine();
       }

六:注入调用

我把业务类编译输入到D盘,用我们前面的Weave函数进行注入。

  CodeInject.Weave(new string[] { @"D:\test\Test.exe" });

运行结果如下

反编译后的c#

-------------------------------------------------------------------------------------------------------------------------

7. 以下这段是怎么调用别的程序集的方法示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil; namespace BlogSample
{
class Program
{
static void Main(string[] args)
{
AssemblyDefinition assembiy = AssemblyFactory.GetAssembly(args[0]);
foreach (Mono.Cecil.TypeDefinition item in assembiy.MainModule.Types)
{
foreach (MethodDefinition method in item.Methods)
{
if (method.Name.Equals("Main"))
{ var ins = method.Body.Instructions[0];
var worker = method.Body.CilWorker; 
worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, "Method start…")); //定义一个变量
worker.InsertBefore(ins, worker.Create(OpCodes.Call,
assembiy.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })))); //调用Console.WriteLine() 方法
ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, "Method finish…"));
worker.InsertBefore(ins, worker.Create(OpCodes.Call,
assembiy.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));
break;
}
} } AssemblyFactory.SaveAssembly(assembiy, "IL_" + args[0]);
Console.Read();
}
}
}

8. [is declared in another module and needs to be imported的解决方法:

报错:

C:\dev\MongoConnect\WeavingTaskTest\Weaving\CodeWeaving.targets(32,5): error MSB4018: System.ArgumentException: Member 'System.Void MongoConnect.BaseDataObject::SetProperty(System.String,System.Object)' is declared in another module and needs to be imported

解决方法: 就是把你要调用的方法 和你调用的方法放到一个一个程序集下可以解决这个问题

解决方法原文:

4down voteaccepted

I've found the solution. The reason was really funny.

Module.Import() method must be called from current module we want to modify, not the module where method is defined. It is not clear from original docs.

For example, we want to add some method defined in the Referenced.dll assembly to our Main.dll assembly. Then we have to find main module of our Main.dll assembly and then call MainModule.Import(methodFromReferencedAssembly);

   

使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络的更多相关文章

  1. 日志系统实战(二)-AOP动态获取运行时数据

    介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...

  2. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  3. windows上zend server安装 报The server encountered an internal error or misconfiguration and was unable to complete your request -解决方法 摘自网络

    windows上zend server安装完成后报如下错误:   Internal Server Error The server encountered an internal error or m ...

  4. MySql Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 解决方法 -摘自网络

    错误:Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 原因: 同一个i ...

  5. JVM虚拟机-运行时数据区概述

    目录 运行时数据区域 总览 概念扫盲 什么是栈帧(Stack Frame) JVM常见出现两种错误 程序计数器 虚拟机栈 结构 局部变量表 方法是如何调用的 本地方法栈 堆 浅堆和深堆 堆的细分 方法 ...

  6. JVM内存区域(运行时数据区)划分

    前言: 我们每天都在编写Java代码,编译,执行.很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文 ...

  7. JVM 内存区域 (运行时数据区域)

    JVM 内存区域 (运行时数据区域) 链接:https://www.jianshu.com/p/ec479baf4d06 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内 ...

  8. JVM 运行时数据区域划分

    目录 前言 什么是JVM JRE/JDK/JVM是什么关系 JVM执行程序的过程 JVM的生命周期 JVM垃圾回收 JVM的内存区域划分 一.运行时数据区包括哪几部分? 二.运行时数据区的每部分到底存 ...

  9. 《精通C#》第十六章-动态类型和动态语言运行时-第一节至第四节

    在.Net4.0中引入了一个关键字dynamic,这是一个动态类型关键字.Net中还有一个关键字是var,这是一个隐式类型,可以定义本地变量,此时var所代表的实际的数据类型有编译器在初次分配时决定, ...

随机推荐

  1. Samza文档翻译 : Architecture

    http://samza.incubator.apache.org/learn/documentation/0.7.0/introduction/architecture.html Samza由三层组 ...

  2. 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

    http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...

  3. pflua:用Lua编写的高性能网络包过滤工具箱

    http://www.csdn.net/article/2014-09-05/2821568-pflua

  4. 【转】五种开源协议的比较(BSD, Apache, GPL, LGPL, MIT)

    当 Adobe.Microsoft.Sun 等一系列巨头开始表现出对”开源”的青睐时,”开源”的时代即将到来! 现今存在的开源协议很多,而经过 Open Source Initiative 组织通过批 ...

  5. nginx负载均衡 - session失效

    最近迷上了Nginx,真实麻雀虽小,五脏俱全..功能实在强大.. nginx不单可以作为强大的web服务器,也可以作为一个反向代理服务器,而且nginx还可以按照调度规则实现动态.静态页面的分离,可以 ...

  6. 【HDOJ】4343 Interval query

    最大不相交集合的数量.思路是dp[i][j]表示已经有i个不相交集合下一个不相交集合的最右边界.离散化后,通过贪心解. /* 4343 */ #include <iostream> #in ...

  7. 【HDOJ】4328 Cut the cake

    将原问题转化为求完全由1组成的最大子矩阵.挺经典的通过dp将n^3转化为n^2. /* 4328 */ #include <iostream> #include <sstream&g ...

  8. 【HDOJ】4317 Unfair Nim

    基本的状态压缩,想明白怎么dp还是挺简单的.显然对n个数字进行状态压缩,dp[i][j]表示第i位状态j表示的位向高位产生了进位. /* 4317 */ #include <iostream&g ...

  9. UVa 1395 (最小生成树) Slim Span

    题意: 规定一棵生成树的苗条度为:最大权值与最小权值之差.给出一个n个顶点m条边的图,求苗条度最小的生成树. 分析: 按照边的权值排序,枚举边集的连续区间[L, R]的左边界L,如果这些区间刚好满足一 ...

  10. PHP "gdImageCreateFromXpm()"空指针间接引用漏洞

    漏洞版本: PHP PHP 5.5.10 PHP PHP 5.4.26 漏洞描述: CVE ID: CVE-2014-2497 PHP是一种HTML内嵌式的语言. PHP 5.4.26.5.5.10版 ...