So
using System;
using Telerik.CodeWeaver.Hook;
namespace ConsoleApplication2
{
sealed class MyClass
{
public void SayHello() { Console.WriteLine("Hello!"); }
public int SayHello(string name)
{
Console.WriteLine("Hello {0}!", LastName = name);
return (name ?? string.Empty).Length;
}
public string LastName { get; set; }
public int Increment10(ref int i)
{
Console.WriteLine("LastName={0}", LastName);
Console.WriteLine("i={0}", i = i + 10);
return 0;
}
public int ThrowException(ref int i)
{
throw new NotImplementedException();
}
}
class Program
{
[WeaverInject]
delegate int MyPrologueDelegate(ref int i,
MyClass target,
out bool skipOldMethodBody);
[WeaverInject]
delegate int MyEpilogueDelegate(ref int i,
MyClass target,
int ret);
static int Incerement10Prologue(ref int i,
MyClass target,
out bool skipOldMethodBody)
{
skipOldMethodBody = false;
i = 100;
target.LastName = "John Smith";
return 0; // ignored because of skipOldMethodBody = false;
}
static int ThrowExceptionEpilogue( ref int i,
MyClass target,
int ret)
{
Console.WriteLine("from epilogue: current i={0}", i);
Console.WriteLine("from epilogue: current return value={0}", ret);
return 2 * (i += 10);
}
static void Main(string[] args)
{
Weaver<
MyClass
>
.AtEndOfAction(_ => _.SayHello())
.Inject(() =>
{
Console.WriteLine("Hello again!");
return true; // return value is ignored
});
var c = new MyClass();
c.SayHello();
Weaver<
MyClass
>
.AtEndOfFunc<
string
, int>((_, name) => _.SayHello(name))
.Inject((name, ret) =>
{
Console.WriteLine("Hello again {0}!", name);
return ret * ret;
});
Console.WriteLine(c.SayHello("John"));
Weaver<
MyClass
>
.AtStartOfFunc<
int
, int>((_, i) => _.Increment10(ref i))
.InjectDelegate<
MyPrologueDelegate
>(Incerement10Prologue);
Weaver<
MyClass
>
.AtEndOfFuncSafe<
int
, int>((_, i) => _.ThrowException(ref i))
.InjectDelegate<
MyEpilogueDelegate
>(ThrowExceptionEpilogue);
Console.WriteLine("LastName={0}", c.LastName);
int val = 0;
int returnValue = c.Increment10(ref val);
Console.WriteLine("val={0} returnValue={1}", val, returnValue);
returnValue = c.ThrowException(ref val);
Console.WriteLine("val={0} returnValue={1}", val, returnValue);
}
}
}
I defined MyClass type that contains the target methods. The type is sealed and does not
Weaver<
MyClass
>
.AtEndOfAction(_ => _.SayHello())
.Inject(() =>
{
Console.WriteLine("Hello again!");
return true; // the return value is ignored
});
As we know from my previous post we define the target type via Weaver<> generic class. In this first
So
Weaver<
MyClass
>
.AtEndOfFunc<
string
, int>((_, name) => _.SayHello(name))
.Inject((name, ret) =>
{
Console.WriteLine("Hello again {0}!", name);
return ret * ret;
});
In this scenario the signature of the target method is:
int SayHello(string name)
so we have to use AtEndOfFunc<string, int>(...) generic method. We define the type parameters as we do with System.Func<...> framework delegate. Compared to AtStartOfFunc scenario this time Inject method accepts slightly different lambda. There is one more parameter that I named “
So
The solution is InjectDelegate<TDelegate>(...) method instead Inject(...) method.
int Increment10(ref int I)
The delegate signature for prologue method should be:
[WeaverInject]
delegate int MyPrologueDelegate(ref int i, MyClass target, out bool skipOldMethodBody);
We see a couple of new
Lets see our prologue injection implementation:
static int Incerement10Prologue(ref int i,
MyClass target,
out bool skipOldMethodBody)
{
skipOldMethodBody = false;
i = 100;
target.LastName = "John Smith";
return 0; // ignored because of skipOldMethodBody = false;
}
and the way we define it for the target method.
Weaver<
MyClass
>
.AtStartOfFunc<
int
, int>((_, i) => _.Increment10(ref i))
.InjectDelegate<
MyPrologueDelegate
>(Incerement10Prologue);
The code looks straightforward. It looks even cleaner than the lambda version. As we can see we are able to change the state of the object instance through “target” parameter.
In the last scenario the target method is
public int ThrowException(ref int i)
{
throw new NotImplementedException();
}
The delegate signature for epilogue method should be:
[WeaverInject]
delegate int MyEpilogueDelegate(ref int i, MyClass target, int ret);
Again, ignore the custom attribute and
Weaver<
MyClass
>
.AtEndOfFuncSafe<
int
, int>((_, i) => _.ThrowException(ref i))
.InjectDelegate<
MyEpilogueDelegate
>(ThrowExceptionEpilogue);
Basically AtEndOfFuncSafe will wrap the target method in try{...}catch{}finally{...} and will put the epilogue code in finally clause.
There are limitations with AtEndOfXYZ methods. In some cases C# and VB.NET compilers emit MSIL instructions that require a heavy processing how exactly to inject the epilogue code. Thus for performance reasons AtEndOFXYZ methods make assumptions about the method work flow. We've tested the epilogue functionality with a lot of assemblies and it worked quite stable but there's a slight possibility that there could be issues, for example with heavily obfuscated assemblies. Should you find such issues, please let us know.
So far we covered how to inject prologue and epilogue code in non-generic methods. We saw how to inject epilogue code in methods that throw exceptions as well. Keep in mind that weaver API is still evolving and there are expected changes. Feel free to let me know your opinion about current weaver API.
Next time: injecting prologue/epilogue in generic methods.
Mehfuz Hossain works as the Technical Advisor in DevTools Division . He is passionate playing around with the latest bits. He has been a Microsoft MVP, author of OS projects like LinqExtender and LINQ to flickr and most recently lighter (nodejs + mongo blogging engine). Prior to working at Telerik , Mehfuz worked as a core member in many high volume web applications including Pageflakes that is acquired by Live Universe in 2008. He is a frequent blogger and was also a contributor and site developer at dotnetslackers.com.