使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络
目录
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.
For example, we want to add some method defined in the |
使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络的更多相关文章
- 日志系统实战(二)-AOP动态获取运行时数据
介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...
- 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)
原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...
- 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 ...
- 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 ...
- JVM虚拟机-运行时数据区概述
目录 运行时数据区域 总览 概念扫盲 什么是栈帧(Stack Frame) JVM常见出现两种错误 程序计数器 虚拟机栈 结构 局部变量表 方法是如何调用的 本地方法栈 堆 浅堆和深堆 堆的细分 方法 ...
- JVM内存区域(运行时数据区)划分
前言: 我们每天都在编写Java代码,编译,执行.很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文 ...
- JVM 内存区域 (运行时数据区域)
JVM 内存区域 (运行时数据区域) 链接:https://www.jianshu.com/p/ec479baf4d06 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内 ...
- JVM 运行时数据区域划分
目录 前言 什么是JVM JRE/JDK/JVM是什么关系 JVM执行程序的过程 JVM的生命周期 JVM垃圾回收 JVM的内存区域划分 一.运行时数据区包括哪几部分? 二.运行时数据区的每部分到底存 ...
- 《精通C#》第十六章-动态类型和动态语言运行时-第一节至第四节
在.Net4.0中引入了一个关键字dynamic,这是一个动态类型关键字.Net中还有一个关键字是var,这是一个隐式类型,可以定义本地变量,此时var所代表的实际的数据类型有编译器在初次分配时决定, ...
随机推荐
- 动态链接库中分配内存引起的问题-- windows已在XX.exe中触发一个断点
动态链接库中分配内存引起的 本文主要是探讨关于在动态链接库分配的内存在主程序中释放所产生的问题,该问题是我在刚做的PJP工程中所遇到的,由于刚碰到之时感动比较诡异(这也是学识不够所致),所以将它写下来 ...
- android dialog 原来dialog对话框也有自己的按键监听事件 onKeyDown方法
探讨在一个activity中按menu键时弹出自己定义的dialog(自定义菜单对话框)时,再按一次手机的menu键发现这个自定义的dialog菜单并没有关闭,原来是这个dialog内部也有onKey ...
- maven新建Spring MVC + MyBatis + Oracle的Web项目中pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- mybatis知识点
1.Mybatis比IBatis比较大的几个改进是什么 a.有接口绑定,包括注解绑定sql和xml绑定Sql , b.动态sql由原来的节点配置变成OGNL表达式, c. 在一对一,一对多的时候引进了 ...
- SQL数据库还原时备份集中的数据库备份与现有的数据库不同的解决办法
SQL Server 2005数据库还原出错错误具体信息为:备份集中的数据库备份与现有的A数据库不同 具体操作如下:第一次:新建了数据库A,数据库文件放在E:\DB\A目录下,选中该数据库右键-任务- ...
- D3D游戏编程系列(一):DXLib的介绍
这篇文章里我准备向大家介绍下我封装的一个基础D3D库:DXLib.有了这样一个类库,可以减少很多无用功以及繁琐的工作,使我们的效率大大提高. DXLib.h #define DIRECTINPUT_V ...
- JavaScript DOM高级程序设计 4.3控制事件流和注册事件侦听器--我要坚持到底!
一.事件流 我们通过下面一个实例,进行说明. <body> <h1>Event Flow</h1> <ul id="nav"> &l ...
- python中的commands模块,执行出错:'{' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
最近发现了python的commands模块,查看了下源码,使用的popen封装的,形成三个函数getstatus(), getoutput(), getstatusoutput() 源码如下: de ...
- git 日常使用
git clone git checkout git 删除 本地分支: git branch -d <本地分支名> git branch -D <本地分支名>(大写表 ...
- 走迷宫(DFS)
题目:http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=2449&cid=1181 目前dfs 里的递归还是不很懂,AC代码如下: #incl ...