Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / August 2007

Tip: Looking for answers? Try searching our database.

Using reflection to unsubscribe an event

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
pedrito - 30 Aug 2007 23:35 GMT
I have a library I'm using that has a lapsed listener issue. I figured I'd
just sneak in and do the unsubscription myself since I don't expect it to be
fixed any time soon.

So, I have the control whose event its subscribed to and it's subscribed to
the VisibleChanged event and has an event handler called
ControlVisibleChanged.

So basically, what I want to do is:

myControl.VisibleChanged -= new
EventHandler(myControl.ControlVisibleChanged);

The problem is myControl.ControlVisibleChanged is private.

So, what do I pass to the EventHandler constructor?

Thanks
Mattias Sjögren - 31 Aug 2007 04:57 GMT
>The problem is myControl.ControlVisibleChanged is private.
>
>So, what do I pass to the EventHandler constructor?

If you hav the right permissions you can get a MethodInfo for a
private method with Reflection as well. Just make sure you include
BindingFlags.NonPublic when retrieving it.

Mattias

Signature

Mattias Sjögren [C# MVP]  mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

pedrito - 31 Aug 2007 13:33 GMT
> >The problem is myControl.ControlVisibleChanged is private.
>>
[quoted text clipped - 5 lines]
>
> Mattias

I can get the MethodInfo, but I can't pass the MethodInfo to new
EventHandler() because it doesn't take a method info. It normally takes the
method itself and I certainly need to pass the class instance associated
with the method, somehow.
Jon Skeet [C# MVP] - 31 Aug 2007 13:37 GMT
> I can get the MethodInfo, but I can't pass the MethodInfo to new
> EventHandler() because it doesn't take a method info. It normally takes the
> method itself and I certainly need to pass the class instance associated
> with the method, somehow.

Is Delegate.CreateDelegate what you're after?

Jon
pedrito - 31 Aug 2007 13:48 GMT
>> I can get the MethodInfo, but I can't pass the MethodInfo to new
>> EventHandler() because it doesn't take a method info. It normally takes
[quoted text clipped - 5 lines]
>
> Jon

Jon,

That may just do it. Thanks. I've never actually tried this before, so we'll
see how it goes. Thanks.

Pete
pedrito - 31 Aug 2007 14:31 GMT
>>> I can get the MethodInfo, but I can't pass the MethodInfo to new
>>> EventHandler() because it doesn't take a method info. It normally takes
[quoted text clipped - 12 lines]
>
> Pete

Jon,

Not having any luck. This is what I did:

parentControl.VisibleChanged -=
           Delegate.CreateDelegate(typeof(EventHandler), myControl,
"ControlVisibleChanged") as EventHandler;

ControlVisibleChanged is the event handler. myControl is the control that
subscribed to parentControl.VisibleChanged.

When I do this, I get an ArgumentException with the message, "Error binding
to target method", which I can only assume is ControlVisibleChanged because
it's private.

I'm using .NET 1.1, so I don't get the overrides of CreateDelegate that take
a MethodInfo and an object instance, not that it looks like it would matter.
It appears that the exception being thrown is coming from an internal
implementation given that the callstack is:

System.ArgumentException: Error binding to target method.
  at System.Delegate.InternalCreate(Object target, String method, Boolean
ignoreCase)
  at System.Delegate.CreateDelegate(Type type, Object target, String
method)

and

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern void InternalCreate(object target, string method, bool
ignoreCase);

Any idea on how I might around this?
Jon Skeet [C# MVP] - 31 Aug 2007 14:56 GMT
<snip>

> I'm using .NET 1.1, so I don't get the overrides of CreateDelegate that take
> a MethodInfo and an object instance, not that it looks like it would matter.

No, I think that's absolutely what matters. I don't see any reason why
it shouldn't work if you can create the delegate. Unfortunately,
without the right overloads, I'm not sure of the best way of doing
this.

What you *could* do is create a class to encapsulate the method info
and target, and expose a method which has the appropriate signature to
be used as an event handler - that could then call MethodInfo.Invoke.
It would be relatively slow, but it should work.

Jon
pedrito - 31 Aug 2007 18:17 GMT
> <snip>
>
[quoted text clipped - 14 lines]
>
> Jon

Jon,

You're right. The "Not that it matters" was wrong because the problem is
that it's looking for a public reference.

Here's what I did and I should probably be thrown in jail for doing this,
but it almost seems to have worked.

MethodInfo ovc = typeof(MyControlClass).GetMethod("OnVisibleChanged",
BindingFlags.NonPublic | BindingFlags.Instance);
Delegate ovcDelegate = Delegate.CreateDelegate(typeof(EventHandler), this,
"VScrollBarValueChanged") as EventHandler;

FieldInfo fi = typeof(Delegate).GetField("_target", BindingFlags.NonPublic |
BindingFlags.Instance);
fi.SetValue(ovcDelegate, myControl);
fi = typeof(Delegate).GetField("_method", BindingFlags.NonPublic |
BindingFlags.Instance);
fi.SetValue(ovcDelegate, ovc);
fi = typeof(Delegate).GetField("_methodPtr", BindingFlags.NonPublic |
BindingFlags.Instance);
fi.SetValue(ovcDelegate, ovc.MethodHandle.Value);
fi = typeof(Delegate).GetField("_methodPtrAux", BindingFlags.NonPublic |
BindingFlags.Instance);
fi.SetValue(ovcDelegate, ovc.MethodHandle.Value);
parentControl.VisibleChanged -= ovcDelegate as EventHandler;

Basically, what I did was I created a dummy delegate that had the same
signature (though I don't think that really matters).

Then, using reflection, I filled in the all the fields of the Delegate with
the information for the event handler I really wanted a delegate for.

Now, as I said, this "almost" worked.

It did appear to successfully remove the old reference. But somehow, it
created an entirely new reference with a call stack leading back to

parentControl.VisibleChanged -= ovcDelegate as EventHandler;

This is according to .NET profiler... The relevant portion of the call stack
is:

MulticastDelegate.RemoveImpl(Delegate)
Delegate.Remove(Delegate, Delegate)
EventHandlerList.RemoveHandler(object, Delegate)
Control.remove_VisibleChanged(EventHandler)

How does that create a reference? Unless it's walking the stack to see where
the call is coming from and saying, "Aha, this is a different class trying
to unsubscribe the original subscription, so let's keep a reference to the
new one..." I don't know. My head is about to pop.
pedrito - 31 Aug 2007 18:20 GMT
> <snip>

> What you *could* do is create a class to encapsulate the method info
> and target, and expose a method which has the appropriate signature to
> be used as an event handler - that could then call MethodInfo.Invoke.
> It would be relatively slow, but it should work.

In reference to this, I'm not sure I understand what you mean. Doesn't the
unsubscription need to have the same underlying function pointer (it appears
that's technically how it's happening underneath with the
Delegate._methodPtr)...

Unless I'm misunderstanding, your method would unsubscribe a different
_methodPtr...
Jon Skeet [C# MVP] - 31 Aug 2007 19:29 GMT
> > What you *could* do is create a class to encapsulate the method info
> > and target, and expose a method which has the appropriate signature to
[quoted text clipped - 8 lines]
> Unless I'm misunderstanding, your method would unsubscribe a different
> _methodPtr...

You'd have to override Equals in your "extra" class, but then it should
be okay, so long as both subscription and unsubscription were done in
the same manner (i.e. both using the proxy).

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

pedrito - 31 Aug 2007 19:49 GMT
Ahh, see, that wouldn't work. I don't do the subscription. The library I'm
using does the subscription. It just never unsubscribes under any condition,
otherwise I'd find whatever method it uses to unsubscribe and call that. But
since it doesn't, I'm trying to figure out a way to force the
unsubscription. The lapsed listener is hanging onto a reference of my
control and I can't get rid of it...

>> > What you *could* do is create a class to encapsulate the method info
>> > and target, and expose a method which has the appropriate signature to
[quoted text clipped - 14 lines]
> be okay, so long as both subscription and unsubscription were done in
> the same manner (i.e. both using the proxy).

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.