?
Solved

Add an event handler to a loaded dll from outside project

Posted on 2009-04-19
13
Medium Priority
?
1,265 Views
Last Modified: 2013-12-17
Hi,

I have an application that loads plugins which are external dll's within my exe directory using

            Dim A As Assembly = Assembly.LoadFrom(fFile.FullName)
            Dim Plugin As Object = A.CreateInstance(fFile.Name.ToString.Replace(fFile.Extension, "") & ".SuperClass")


where fFile.Fullname is the path to my DLL.

Within this dll I know it has a public event called RaiseMessage, and within my application I want to be able to handle this event when the dll raises the event. the event in the DLL is declared such


Public Event RaiseMessage(ByVal hEventMessages As Hashtable)


What I have tried are the following to try and add a handler to this event, but it hasnt worked, so any ideas would be great:


-------------------------
Using the method of the object (of which my project is unaware):

     AddHandler Plugin.RaiseError, AddressOf RaiseMessage

Error:
RaiseError is not an event of Object

-----
AND
-----

Using reflection:

    Dim tModule As Type = Plugin.GetType()
    Dim mi As MethodInfo = tModule.GetMethod("RaiseMessage", BindingFlags.Public Or BindingFlags.Instance)

    AddHandler Plugin.GetType.GetMethod("RaiseMessage"), AddressOf RaiseMessage

Error:

'AddHandler' or 'RemoveHandler' statement event operand must be a dot-qualified expression or a simple name.



0
Comment
Question by:bhermer
  • 6
  • 6
13 Comments
 
LVL 10

Expert Comment

by:Jason Evans
ID: 24178552
Hi there.
I'm not sure how to help with this question, but I can recommend looking at this article:
http://damianblog.com/2008/07/02/net-code-injection/
This guy shows how to write .NET code to inject into another .NET  process, and one of things he does is subscribe to an event in the other process, similar to what you want to do. It looks like he's using a DynamicMethod object to do this.
I'm not sure how much this will help you, but it might give you an idea.
Cheers.
Jas.
0
 
LVL 4

Author Comment

by:bhermer
ID: 24178629
thanks for that, I understand the gist of what he is doing there, but it seems way over the top for what should be a simple thing to do (that doesnt mean it is possble to do simply ;)

I will leave the Q open on the hopes someone knows an easier method.
0
 
LVL 39

Expert Comment

by:abel
ID: 24178747
Code injection should not be necessary, actually, Emit techniques are not necessary at all. Though I haven't got a fully working sample yet, but the basic idea is explained in this thread, through Reflection: http://www.pcreview.co.uk/forums/thread-3723456.php

More background information and source code can be found here: http://www.codeproject.com/KB/cs/WeakEvents.aspx. Of interest to you is the Solution 3: SmartWeakEvent, but you'll have to view the source for how he does that (explanation on the page is rather short).
0
Fill in the form and get your FREE NFR key NOW!

Veeam is happy to provide a FREE NFR server license to certified engineers, trainers, and bloggers.  It allows for the non‑production use of Veeam Agent for Microsoft Windows. This license is valid for five workstations and two servers.

 
LVL 4

Author Comment

by:bhermer
ID: 24184980
Hi Abel,

In the first link, from what it looks like, he is pointing an already existing method to his own custom method?

I am presuming tx is a textbox and he is extracting a methog called tx.Name + "_MouseClick" and assigning that methid to the mousclick event which is always attached to all textbox's,

 I need to add a handler to a method that wouldn't appear on intellisense as my class (dll) is typed as an object. I could, I think, using that code, override a public standard method of the object class, but that sounds like a bodge.

For the second link, it went over my head a little when scan reading, so I will have a better look at report back here.

Thanks
0
 
LVL 39

Expert Comment

by:abel
ID: 24185125
If your DLL is a .NET assembly, which I assume it is. then you can retrieve any handler, hidden or not, using reflection. Which is actually what that link shows:

MethodInfo mi = t.GetMethod(sMethodName);

where sMethodName is the name of your event. Intelisense is not of an issue here, because you are using reflection. And reflection is the technique of finding methods / events / properties / fields which are unknown to the compiler beforehand.
0
 
LVL 4

Author Comment

by:bhermer
ID: 24185480
Hi Abel,

I am already using reflection to call a method called LoadControl within my dll (which is NET, and written by me)

From my main application, I have no problems calling methids within my dll, the problem I have is getting my dll to talk back to my main application

Effectivly the dll is a user control that can be loaded into a control panel in my app, I need a way for that dll (user control) to raise events within the main application.

Normally, if I have the dll as a class file within my project I can use

AddHandler Object.MethodName(), AddressOf MyApp.MethodName()

so when the dll raises an event called MethodName() the event is fired in the main application called MethodName()


he problem is, using reflection, you cannot add an event handler to a method as stated in my original Q:


    Dim tModule As Type = Plugin.GetType()
    Dim mi As MethodInfo = tModule.GetMethod("RaiseMessage", BindingFlags.Public Or BindingFlags.Instance)

    AddHandler Plugin.GetType.GetMethod("RaiseMessage"), AddressOf RaiseMessage


As to why I need this, others will be writinging user controls to plug into this app, and we dont want to re-compile the application every time we want to update or add a user control, we just load all available controls from a dll directory

0
 
LVL 39

Expert Comment

by:abel
ID: 24185771
I think you are mixing a few things up. The code in that example page shows to do just that. All you need is, of course, the event handler (which is in your current calling application) and then you register that to the dynamically loaded event.

I'll try to workout a simple example that suits your situation, but that may take some time....
0
 
LVL 4

Author Comment

by:bhermer
ID: 24213503
Hi Abel,  Any luck working out a solution to this?
0
 
LVL 39

Expert Comment

by:abel
ID: 24220142
Yes, actually. It wasn't easy to find out, and I wasn't completely correct with my assumptions, apparently, you do need some extra steps here to get it right.

The event handler, according to Microsoft, must be static (Shared in VB) and I haven't found a way to do this differently, as of yet. Probably the easiest work around is to add a reference to your current object and send that through the event. But those are implementation details, I am sure you'll manage.

Test scenario
Below is a button_click event on a form that I used to start a timer object that I created. The timer object is below as well and is called OneOffTimer (and uses a Timer, surprise, surprise). It has one event, OnTimeOut. The timer starts when the method SetTimeOutAndStart is called with the number of seconds.

Subscribing to an event using reflection
In the form button_click handler there's the core of this technique. I put comments in there to help you out understanding what's going on. The main point is creating a delegate (an existing normal delegate won't work, because it is bound to the form) and to lookup the AddMethod of the event. The event itself is found using GetEvent.

All these steps on themselves are pretty basic, but putting it together in the right order can be daunting. I had quite some struggles with the delegates, until I found out that they had to be static (Shared).

-- Abel --

' demonstation of calling an event dynamically '
Private WithEvents myOneOffTimer As New OneOffTimer
Private Sub Q_24335269_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Q_24335269.Click
 
    ' Find the event and its AddMethod '
    Dim infoTimeOutEvt As EventInfo = myOneOffTimer.GetType().GetEvent("OnTimeOut")
    Dim infoTimeOutEvtAddMethod As MethodInfo = infoTimeOutEvt.GetAddMethod()
    'Dim concreteTimeOutDelegate As OnTimeOutDelegate = New OnTimeOutDelegate(AddressOf OnTimeOutHandler)
 
    ' Find the handler that we want to use to receive the events '
    Dim infoTimeoutHandler As MethodInfo = _
    Me.GetType().GetMethod("OnTimeOutHandler", _
    BindingFlags.NonPublic Or BindingFlags.Static)
 
 
    ' Create a delegate for our handler '
    Dim delegType As Type = infoTimeOutEvt.EventHandlerType
    Dim myDelegate As [Delegate] = [Delegate].CreateDelegate(delegType, infoTimeoutHandler)
 
    ' Add the event handler to the event chain of OnTimeOut '
    infoTimeOutEvtAddMethod.Invoke(myOneOffTimer, New Object() {myDelegate})
 
    ' set the timeout to raise the event after some seconds'
    myOneOffTimer.SetTimeOutAndStart(3)
 
    '
    'AddHandler myOneOffTimer.OnTimeOut, AddressOf OnTimeOutHandler
End Sub
 
' this is the callback for the event that will be called if the event is raised '
Private Shared Sub OnTimeOutHandler()
    Debug.WriteLine("timeout expired")
End Sub
 
 
 
' just some object that raises an event '
 
Public Class OneOffTimer
    Private _timer As Timer
    Public Sub New()
        _timer = New Timer(AddressOf HandleTimeOut)
    End Sub
 
    Public Sub SetTimeOutAndStart(ByVal timeOutValue As Integer)
        _timer.Change(timeOutValue * 1000, Timeout.Infinite)
    End Sub
 
    Private Sub HandleTimeOut(ByVal state As Object)
        _timer.Dispose()
        RaiseEvent OnTimeOut()
    End Sub
 
    Public Event OnTimeOut()
End Class

Open in new window

0
 
LVL 39

Accepted Solution

by:
abel earned 2000 total points
ID: 24220162
ah, I hate that, I made a copy and paste error. Here's the corrected code:


    ' demonstation of calling an event dynamically '
    Private WithEvents myOneOffTimer As New OneOffTimer
    Private Sub Q_24335269_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Q_24335269.Click
 
        ' Find the event and its AddMethod '
        Dim infoTimeOutEvt As EventInfo = myOneOffTimer.GetType().GetEvent("OnTimeOut")
        Dim infoTimeOutEvtAddMethod As MethodInfo = infoTimeOutEvt.GetAddMethod()
 
        ' Find the handler that we want to use to receive the events '
        Dim infoTimeoutHandler As MethodInfo = _
                Me.GetType().GetMethod("OnTimeOutHandler", _
                BindingFlags.NonPublic Or BindingFlags.Static)
 
 
        ' Create a delegate for our handler '
        Dim delegType As Type = infoTimeOutEvt.EventHandlerType
        Dim myDelegate As [Delegate] = [Delegate].CreateDelegate(delegType, infoTimeoutHandler)
 
        ' Add the event handler to the event chain of OnTimeOut '
        infoTimeOutEvtAddMethod.Invoke(myOneOffTimer, New Object() {myDelegate})
 
        ' set the timeout to raise the event after some seconds'
        myOneOffTimer.SetTimeOutAndStart(3)
 
    End Sub
 
    ' this is the callback for the event that will be called if the event is raised '
    Private Shared Sub OnTimeOutHandler()
        Debug.WriteLine("timeout expired")
    End Sub
 
 
 
' just some object that raises an event '
Public Class OneOffTimer
    Private _timer As Timer
    Public Sub New()
        _timer = New Timer(AddressOf HandleTimeOut)
    End Sub
 
    Public Sub SetTimeOutAndStart(ByVal timeOutValue As Integer)
        _timer.Change(timeOutValue * 1000, Timeout.Infinite)
    End Sub
 
    Private Sub HandleTimeOut(ByVal state As Object)
        _timer.Dispose()
        RaiseEvent OnTimeOut()
    End Sub
 
    Public Event OnTimeOut()
End Class

Open in new window

0
 
LVL 4

Author Comment

by:bhermer
ID: 24220353
thanks abel, will test tomorrow, thanks for your effort
0
 
LVL 4

Author Closing Comment

by:bhermer
ID: 31571940
Thanks Abel, sorry for delay, back on this today, managed to make good progress with your code
0
 
LVL 39

Expert Comment

by:abel
ID: 24375246
(from grading comment)
> managed to make good progress with your code

glad to hear that. It was a bit of work to get it all together, but a nice learning experience too.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Simulator games are perfect for generating sample realistic data streams, especially for learning data analysis. It is even useful for demoing offerings such as Azure stream analytics, PowerBI etc.
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
In a question here at Experts Exchange (https://www.experts-exchange.com/questions/29062564/Adobe-acrobat-reader-DC.html), a member asked how to create a signature in Adobe Acrobat Reader DC (the free Reader product, not the paid, full Acrobat produ…
Suggested Courses
Course of the Month16 days, 1 hour left to enroll

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question