目录

一:普通写法

二:注入定义

三: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. 1964-NP

    描述 Problems in Computer Science are often classified as belonging to a certain class of problems (e. ...

  2. Android 图片缩放

    以下演示将一个ImageView的高度设置为两倍: 布局文件main.xml <?xml version="1.0" encoding="utf-8"?& ...

  3. BZOJ 3925 ZJOI2015 地震后的幻想乡

    假设我们用了边权前i小的边使得图连通,那么对答案的贡献为i/m+1 又因为期望的线性性质,我们只需要求用了i条边就可以了 不妨设g(S)(i)表示用了i条边使得点集S连通的概率 设f(S)(i)表示用 ...

  4. [mock]12月27日

    一开始介绍项目,最后的反馈是,还是说得不清楚,需要再准备准备. 然后两道题,第一题是有个数组,有2*n个数字,从1~n.比如n=3的数组,{1,2,2,3,1,3}.然后两两相同的数字删除,每次删除得 ...

  5. 非常好的Demo网站

    http://www.xdemo.org/

  6. Android工具:延展图片NinePatch

    NinePatch能够对.png图片进行处理,生成一个.9.png格式的图片,图像拉伸操作时,图片就会有失真,而.9.png是Android里所支持的一种特殊的图片格式,可以实现部分拉伸. 制作图片方 ...

  7. 启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误的解决方法!

    启动 Eclipse 弹出"Failed to load the JNI shared library jvm.dll"错误的解决方法 http://blog.csdn.net/z ...

  8. win7下VS.NET中通过LinqToSQL连接oracle数据库

    .NetFramework3.5提供了LinqToSQL组件,为我们访问数据库提供了方便.我用的是VS+Oracle开发工具.也想体验一下快捷方便的感觉. 1.连接Oracle数据库 在连接Oracl ...

  9. SGU111 Very simple problem

    多少个平方数小于等于X,二分. PS:java BigInteger. import java.util.*; import java.math.*; public class Solution { ...

  10. CentOS5.5上安装git

    1.尝试用yum安装git失败 [root@localhost usr]# yum install gitLoaded plugins: fastestmirrorLoading mirror spe ...