Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to Remove a VB.NET EventHandler that your class didn't Add?

Tags:

c#

.net

vb.net

In my VB.NET application, the event AppDomain.CurrentDomain.AssemblyResolve has a handler subscribed to it. The ResolveEventHandler which is subscribed to this event was added upstream from my code (for all I know, System.AppDomain has its own Private Method subscribed to the Event)... is it possible to Remove all handlers from this event, so that I can add my own handler and ensure it's the only one?

Essentially I'm trying to do this:

RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ClassX.MethodX

But I don't know what ClassX or MethodX are in this example because I haven't added a handler to this event yet, and this handler was added by upstream code. I'm using the method described here to check if any handler is subscribed event: https://stackoverflow.com/a/2953318/734914

Edit: I was able to figure out which method is subscribed to the event, using the Immediate Window while debugging.

?  DirectCast(gettype(System.AppDomain).GetField("AssemblyResolve", BindingFlags.Instance or BindingFlags.NonPublic).GetValue(AppDomain.CurrentDomain) , ResolveEventHandler)
{System.ResolveEventHandler}
    _methodBase: Nothing
    _methodPtr: 157334028
    _methodPtrAux: 1827519884
    _target: {System.ResolveEventHandler}
    **Method: {System.Reflection.Assembly ResolveAssembly**(System.Object, System.ResolveEventArgs)}
    Target: Nothing

Now I'm trying to Remove it, like this, because it's not a Public method:

RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf GetType(Reflection.Assembly).GetMethod("ResolveAssembly")

But that gives a compiler error saying: "AddressOf parameter must be the name of a method". So I'm not sure how to specify a non-Public method here

like image 982
plukich Avatar asked Feb 03 '15 00:02

plukich


1 Answers

From the original comment:

Here's a topic about removing delegates from events you don't have the delegate signature to .. pretty handy, but at the end the poster stated

"It looks like AppDomain.CurrentDomain.AssemblyResolve doesn't support removal of events at all so the code I posted won't work."

Even .NET complains that the event System.AppDomain.AssemblyResolve can only appear on the left hand side of += or -= (+=/-= operators are the C# AddHandler/RemoveHandler) .. so it's looking like a 'no' :/

Additionally, after your edits, the reason you can't do the following:

RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf GetType(Reflection.Assembly).GetMethod("ResolveAssembly")

Is because the AddressOf keyword is an operator keyword, similar to a + or - being an operator, the AddressOf operates on the procedure given and it must be a procedure name it can deduce at compile time. And the GetMethod function returns a MethodInfo type and not a known function address.

All is not lost however!

The AddHandler and RemoveHandler functions actually call for an Object and Delegate to be passed in (AddressOf gets compiled into a Delegate when used), so you don't need the AddressOf to do what you want, you need a Delegate.

To do this we can use the CreateDelegate method which does take a MethodInfo object. It should be noted that since you're trying to access private methods, you'll need to specify the BindFlags of Reflection.BindingFlags.Instance and Reflection.BindFlags.NonPublic (I say and but you'll do a bitwise Or on them), example:

GetType(Reflection.Assembly).GetMethod("ResolveAssembly", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)

So calling CreateDelegate on this will give us the following code

[Delegate].CreateDelegate(GetType(Reflection.Assembly), GetType(Reflection.Assembly).GetMethod("ResolveAssembly", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic))

But that's rather verbose, so lets break it down:

Dim type As Type = GetType(Reflection.Assembly)
Dim mi As Reflection.MethodInfo = type.GetMethod("ResolveAssembly", (Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic))

And now we can simply call the RemoveHandler on AppDomain.CurrentDomain.AssemblyResolve via the following:

RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, [Delegate].CreateDelegate(type, mi)

Except AppDomain.CurrentDomain.AssemblyResolve expects delegate/event signatures to match, so we need to cast our created delegate to the appropriate type:

RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, CType([Delegate].CreateDelegate(type, mi), System.ResolveEventHandler)

And you can now remove a private method through reflection from the event handler.

WARNING!! While this code will compile and run, I make no guarantees of it's validity or accuracy since you're mucking with frame and stack pointers (essentially), and so while it might run and it might work, it also might spawn baby kittens .. so just be careful.

Hope that can help!

like image 51
txtechhelp Avatar answered Sep 19 '22 14:09

txtechhelp