目录
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 |