Handling .NET Events in Visual FoxPro
via COM Interop
By Rick Strahl
www.west-wind.com
rstrahl@west-wind.com
Last Update:
September 14, 2012
Other Links:
Download Examples for this article
Leave a Comment or
Question
Other articles in the series:
1.
Passing objects between FoxPro and
.NET COM Components
2.
Handling .NET Events in FoxPro via COM Interop
3.
Creating multi-threaded components
.NET for COM Interop with Visual FoxPro
This is Article 2 of a 3 part series on COM Interop between .NET and Visual
FoxPro.
Event Handling is an important feature both in Visual FoxPro
and .NET. But both .NET COM objects and Visual FoxPro require special handling
in order to deal with hooking up to COM events. In this article, Rick takes a
close look at how events work in Visual FoxPro and .NET and how they can be used
together across the COM Interop boundary.
Last month I started a series of articles that are looking
at a few advanced topics in using .NET COM Interop with Visual FoxPro. This
month, I look at handling .NET events through COM Interop and briefly introduce
creating and interacting with multi-threaded .NET components from your Visual
FoxPro applications.
Let’s start by comparing how events work in both Visual
FoxPro and .NET.
Events in Visual FoxPro
Events are usually used to hook up external code into
existing functionality of classes. A class implements an event and fires this
event usually to notify some sort of listener that a certain state has occurred
or been set. Events targets are usually expressed as methods that handle a
specific event.
Visual FoxPro includes many events in the various user
interface classes. Forms and Form Controls have many methods such as Click() and
Resize() and Init() and Destroy() all of which are events that fire as part of
the user interface operation and notify your code that something is happening.
In addition, VFP applications can also consume ActiveX control events which are
natively mapped into VFP methods in a way that is very similar to the way native
UI events work in FoxPro. In addition, starting with VFP 7.0 we are also able to
capture events from plain COM components by implementing the appropriate event
interfaces.
Most native VFP events work by firing as internal,
predefined method hooks. So a button includes a Click() method and this method
is fired when a Click occurs on the button. While this has the effect of an
‘event’ this really isn’t an event, but rather a hook method that fires the
Click() method. It’s predefined at the class level and when you implement the
method you are actually overriding the base class click method by with your own
code. You can then call back to the base class’ default behavior with DoDefault().
The functionality of this event behavior is essentially a designtime
implementation – you have to implement the method and that method will always be
called.
In VFP 8 we also got a new language feature called
BINDEVENT() which allowed externalizing event behaviors. BINDEVENT() allows
hooking a target method to a source ‘event’ or method. Once hooked an activation
of the source event/method also calls the target handler whenever the source
event/method fires. BINDEVENT() has made it possible to simulate firing events
from our own code. Essentially this gives the ability to dynamically hook up
code to an implementation at runtime.
All of the above deals with native Visual FoxPro event
handling. If you are dealing with COM objects the behaviors change a bit. VFP
does not automatically handle events published by COM components. In order to
handle events fired by COM components, VFP requires us to implement the full
event interface (using the IMPLEMENTS keyword on a class) and then use
EVENTHANDLER() to hook the event interface to the COM object. Once hooked up VFP
the fires the COM events into our implemented interface methods. I’ll show how
this works a little later on when we capture .NET COM events.
One thing that’s missing from Visual FoxPro is the ability
to publish COM events. If you create a COM component, there’s no easy way for
Visual FoxPro to publish events. If you want to publish events from your
application you’ll have to resort to using some sort of Interop with some other
COM engine that supports this functionality. You can do this with .NET and COM
Interop, but you could also use VB6 or, if you really want to get your hands
dirty, with C++ by creating a wrapper object that handles the event publishing.
In this article I’ll show how to both handle .NET COM events and use .NET to
publish COM events that we can use in another COM environment.
Events in .NET
Events are integral to .NET and used quite extensively in
the base .NET framework. Event driven interfaces are at the core of the Windows
Form and ASP.NET frameworks as well as most of the Base Class Library types.
.NET uses events to implement model/view controller and message driven patterns
in many places of the framework, so you find heavy event usage in places that
may not seem obvious. For example, the entire ASP.NET pipeline is implemented
through an event driven message architecture that fires events for each step of
the processing pipeline and dynamically hooks in the necessary modules to handle
the processing.
In .NET events work quite differently than in Visual
FoxPro. An event is implemented via the construct of a Delegate. Delegates act
as strongly typed function pointers that can be dynamically assigned at runtime.
A delegate in .NET is used in a similar context as BINDEVENT() in visual FoxPro
– it connects the event or Delegate to a specific implementation. But a Delegate
is more powerful as it includes a specific signature that the target method must
implement. This makes delegates strongly typed. So when a Delegate is created
with the method signature attached to it:
public
delegate void
delNotify(int Mode,string
Message);
An event is then implemented as this Delegate type:
public
event delNotify Notify;
The idea is that you define an event which is mapped to a
specific Delegate type and then assigned at runtime. To assign a handler method
you’d use:
Smtp.Notify += new
delNotify(this.OnMailNotification);
Smtp is the object that publishes the event and the
OnMailNotification method is used as the handler that gets fired in response to
the event firing. The implementation - for example in a Windows Forms form - of
the method might look like this:
protected
void OnMailNotification(int
Mode,string Message)
{
// *** do whatever you need
to...
MessageBox.Show(Message);
}
Note that the OnMailNotification method has the same
signature as the delNotify Delegate, which is required to provide type safety.
Visual Basic syntax works a little bit differently. VB.NET
provides the WithEvents keyword which automatically exposes events to any
handler methods you override.
Friend
WithEvents btnGo
As System.Windows.Forms.Button
This is a model somewhat similar to the way VFP manages
events and is used in WinForms applications to natively hook up UI events. The
compiler manages figuring out which events are implemented and behind the scenes
hooks up the appropriate event delegates. VB.NET can also use Delegates directly
just as C# does. Instead of the += sign which VB.NET doesn’t support, you have
to the use the AddHandler() function.
AddHandler(
me.Click, new
EventHandler(AddressOf(me.btnGo_Click))
I’ll use C# in this article – it should be straightforward
to transfer the concepts to VB.NET.
.NET COM Components and COM Events
If you create a .NET type (class) that exposes events and
you publish this type to COM as described in the last article the events are not
exposed. The reason is that the way events work in COM and .NET is essentially
very different. COM publishes events through separate event interfaces. These
interfaces are not automatically created by the .NET COM export routines when
TLBEXP.EXE is run. You might remember that .NET doesn’t do anything to your .NET
types when it exports to COM. It merely creates a type library and special
registry entries when the component is registered. When the type is instantiated
through COM the .NET Runtime is the actual COM object that executes and it
merely marshals any method calls or property access to your type implementation.
However, .NET provides a mechanism to publish events from
.NET components through COM, but you need to do bit of extra work and create a
separate event interface that exactly describes the events you want to publish.
You then hook the event interface to the exported COM class via attributes that
tell .NET to publish the COM Event interface and route the inbound interface
calls to the appropriate events of your class.
This scheme requires that you change the code for event
publishing components. Many components are COM enabled, but if the publisher
didn’t explicitly publish the COM Event interfaces you won’t have access to the
events.
Assuming you have control over the code though, it’s not
difficult to implement the event interface. The process consists essentially of
creating an interface that contains each of the events and their method
signatures.
An example – Sending email with Events
Let’s look at an example. To demonstrate, I’ll use an SMTP
component that implements SMTP email services using native TCP/IP Sockets in
.NET. The code below only shows the relevant parts of the class, but the full
code is included in the source code download for this article. The class
provides a simple property and method interface to send Email messages by
providing a mail server, title, message and sender and recipient information,
then calling the SendMail method to send a message. To export this component to
COM it adds the ClassInterface and ProgId attributes as shown in Listing 1.
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DotNetCom.wwSmtp")]
public
class wwSmtp
{
public
string MailServer =
"";
public string SenderEmail =
"";
public
string SenderName =
"";
public
string Subject =
"";
// … other properties &
methods left out for brevity
public bool SendMail(bool
AdminEmail)
{
if (
!this.Connect() )
return
false;
bool
Result = this.SendMessage();
this.Close();
if (
!Result )
return
false;
// *** exaggerate
time to send
Thread.Sleep(10000);
return
true;
}
}
The Component is then compiled in Visual Studio with the
project’s Register for COM Interop option (Project Properties | Configuration
Properties | Build | Outputs) set. Once compiled the component can be accessed
from VFP like this:
loSMTP = CREATEOBJECT("DotNetCom.wwSMTP")
loSMTP.MailServer="localhost"
loSMTP.Username = "rick"
loSMTP.Password = "supersecret"
loSMTP.SenderEmail = "rstrahl@west-wind.com"
loSMTP.SenderName = "Rick Strahl"
loSMTP.Recipient = "rickstrahl@hotmail.com"
loSMTP.Subject = ".NET Test Email"
loSMTP.Message = "Email
from .NET at FoxPro DevCon"
*** Synchronous
? loSMTP.SendMail(.f.)
? loSMTP.ErrorMsg
This process is very simple and works with synchronous
operation. You fill the properties and call SendMail and off it goes. Now
imagine for a minute that want to get some status information about the email
processing while the processing is happening.
So let’s add an event to the class called
SendMailMessages that can fire back status information to the calling
application. Listing 2 shows the .NET code to implement the Delegate, the event
and the code in the SendMail method that fires the event.
[ComSourceInterfaces( typeof(IwwSmtpEvents)
)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DotNetCom.wwSmtp")]
public
class wwSmtp
{
…
public
delegate void
delSendMailMessages(
SendMailMessageModes Mode,string
Message);
public
event delSendMailMessages SendMailMessages;
public
void SendMailWithEvents()
{
// *** Create EventArgs
to pass back to caller
SendMailEventArgs SMArgs =
new SendMailEventArgs();
bool Result
= false;
Result = this.Connect();
if
(!Result)
{
if (this.SendMailMessages
!= null)
{
// *** Send
a Failure Message
SMArgs.Mode =
SendMailMessageModes.SendMailFailed;
SMArgs.Message =
"Couldn't connect to server." +
this.ErrorMsg;
this.SendMailMessages(SMArgs.Mode,SMArgs.Message);
}
return;
}
if (this.SendMailMessages
!= null)
{
SMArgs.Message =
"Starting Message Send Operation";
this.SendMailMessages(SMArgs.Mode,SMArgs.Message);
}
Result = this.SendMessage();
this.Close();
if (
!Result )
{
if (this.SendMailMessages
!= null)
{
SMArgs.Mode =
SendMailMessageModes.SendMailFailed;
SMArgs.Message =
"Message Sending failed: " +
this.ErrorMsg;
this.SendMailMessages(SMArgs.Mode,SMArgs.Message);
}
return;
}
// *** Code inserted to
exaggerate the time it takes to send email
Thread.Sleep(10000);
///
Check whether we have handlers
if (this.SendMailMessages
!= null)
{
SMArgs.Mode =
SendMailMessageModes.SendMailCompleted;
SMArgs.Message =
"Message sending completed with no errors";
this.SendMailMessages(SMArgs.Mode,SMArgs.Message);
}
return;
}
}
// *** separate type
declarations
public
enum SendMailMessageModes
{
StatusMessage=4,
SendMailCompleted=1,
SendMailFailed=0,
SendMailCancelled =2
}
This code demonstrates the event interface by first
declaring a custom Delegate that implements a method signature that expects two
parameters – a MessageMode and a string for the actual message. Then an event is
declared using this Delegate as its underlying type.
The SendMailWithEvents method is used as a replacement for
the SendMail() method. The difference of this method is that it explicitly fires
events as it goes through the process of sending a message. It fires an event on
connection failure, the start of sending and when the sending is complete or
failed. It first checks to see that the event is not null (no event handlers
have been hooked up yet). If it isn’t the event delegate is called, which in
effect fires the event. If an event handler is hooked it is now called
immediately. The handler is passed a SendMailMessageModes flag which tells the
receiver what type of message is being sent and the message text. I used a
single event to keep things simple, but you could just as well implement
individual events for each of the messages.
Finally notice the SendMailMessageModes enumeration
definition which is used to identify the message type. Notice that I explicitly
assign an integer value to each enum value here. This is because when an
enum is returned over COM it is returned as an integer value – by explicitly
assigning the value you guarantee the value that is returned over COM is
consistent no matter which order the definitions.
Getting Events to export
If you look closely at the class definition and the
attributes at the top you’ll notice that there is a new attribute at the top:
[ComSourceInterfaces( typeof(IwwSmtpEvents)
)]
This attribute instructs .NET to attach the specified
interface – IwwSmtpEvents – to the class as a COM connection point interface. In
essence this exposes all of the events that are defined in the interface
specified and makes them COM visible. Only events explicitly mapped show up as
COM events, so if you add new events to the class make sure you also add them to
the event interface.
The implementation of this interface describes the names
and method signature of the events that are to be implemented. The names must
match the event names and Delegate signatures in the main class. Listing 3 shows
the event interface for the single SendMailMessages event.
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public
interface IwwSmtpEvents
{
void
SendMailMessages(SendMailMessageModes Mode,
string Message);
}
Picking up .NET COM events in Visual FoxPro
To pick up COM events in Visual FoxPro you need to do the
following:
- Create a matching event interface class
- Instantiate the COM object
- Instantiate the event interface class
- Use EVENTHANDLER() to map the interface to the COM
object
The first step is to create an event interface that matches
the interface definition. The easiest way to do this is to use the object
browser and open the TLB file (Dotnetcom.tlb for the samples). Select interfaces
and then find the event interfaces (lightning bolt). Once you’re at the
interface open a PRG file code window, and drag and drop the interface into the
PRG file at the cursor position. Figure 1 shows this process in action.
Figure 1 – Implementing the Event Interface. The
easiest way to create an interface implementation in VFP is to drag and drop the
interface from the object browser.
Once the interface is dropped, you’ll want to change the
name and the IMPLEMENTS directive which by default hardcodes a path. The final
definition should look like this:
DEFINE CLASS
IwwSmtpEvents AS
session OLEPUBLIC
IMPLEMENTS
IwwSmtpEvents IN
"DotNetCom.wwSMTP"
PROCEDURE
IwwSmtpEvents_SendMailMessages(Mode as Integer,
Message as String)
AS VOID
? Mode
?
Message
? ---
ENDPROC
ENDDEFINE
I changed the name of the class to match the interface and
I changed the IMPLEMENTS IN to point at the ProgId of the main COM object rather
than the hardcoded path on disk, which is obviously not portable if you deploy
your application on a client machine. You can use any of the public ProgIds of
this COM project.
Now you’re ready to call the component and fire events.
Here’s what the client code looks like for calling with events:
loSMTP = CREATEOBJECT("DotNetCom.wwSMTP")
loSMTP.MailServer="localhost"
loSMTP.Username = "rick"
loSMTP.Password = "supersecret"
loSMTP.SenderEmail = "rstrahl@west-wind.com"
loSMTP.SenderName = "Rick Strahl"
loSMTP.Recipient = "rickstrahl@hotmail.com"
loSMTP.Subject = ".NET Test Email"
loSMTP.Message = "Email
from .NET at FoxPro DevCon"
*** Hook up the event
interface
loEvents = CREATEOBJECT("IwwSmtpEvents")
EVENTHANDLER(loSmtp,loEvents)
DOEVENTS
? loSmtp.SendMailWithEvents()
The only difference here to the previous SendMail Fox
snippet is that the Event interface is created and then connected to the COM
object via the EVENTHANDLER() function.
Go ahead and run this code now. You should now see the
event message printing on the VFP desktop screen as the message send operation
starts, and then again as it completes. Notice that although you are running in
blocking mode and SendMailWithEvents is still processing it can fire events back
to your code and VFP can process those events outside of the main processing
operation.
Voila, there you have your first very simple event callback
interface from a .NET COM component.
Building a true Asynchronous Email Object
Now you’re probably asking, what is this doing for me?
Events are very useful, but in the last example the SendMailWithEvents call is
blocking. You are not really taking advantage of the event driven nature of the
class. To demonstrate this more clearly run the included SMTP.scx sample form
shown in Figure 2. Note before running this form demo, you should change the
Mail Server login information in the Init of the form. Then run the form and
click on the Send button, which sends an email synchronously.
When you click the Send button the email gets sent but VFP
goes dead while the process is running. If you try to move the form or otherwise
manipulate the UI in the VFP form you can’t and you may encounter redrawing
problems if windows are moved over the VFP form window as it won’t refresh.
While the call is going on the Windows message pump stops sending messages to
VFP.
Figure 2 – The sample form lets you send email
synchronously and asynchronously. Asynchronous operation allows your FoxPro
application to stay alive while the .NET code goes out and works on a separate
thread in the background.
Synchronous operation has its place, but if you’re working
with an event capable interface, you’ll want to take advantage of asynchronous
operation. In an asynchronous environment you call the Send method and
immediately get control back in your FoxPro code, while the component sends the
email in the background and fires events that you can capture. We know how to
capture events from .NET now, so let’s see how we can make the email component
asynchronous by adding some basic multi-threading in the .NET code.
To create multi-threaded code in .NET is very easy. Let’s
take the SendMailWithEvents() functions and make it asynchronous. Listing 4
shows a new method implementation that creates a new thread and then calls the
old SendMailWithEvents() method.
public
void
SendMailAsyncWithEvents()
{
ThreadStart delSendMail =
new
ThreadStart(this.SendMailWithEvents);
Thread myThread =
new
Thread(delSendMail);
myThread.Start();
}
Yup creating new threads in .NET is very easy, but it’s
very powerful and opens whole new horizons of what you can accomplish with your
FoxPro applications! The code above creates a new Delegate object and points it
at our existing SendMailWithEvents() method. Yes, that’s the same synchronous
method we used in the previous example and shown in Listing 2. Multi-threading
is all about creating a new thread and then calling existing synchronous code to
actually perform the work. If you recall, SendMailWithEvents already fires
events as it sends messages, so we’re set.
The ThreadStart delegate class is a generic, built-in
delegate in .NET with a signature that takes no parameters and has no return
value (void Function(void)), which happens
to match the signature for SendMailEvents. If the signature was different you’d
have to create or use a delegate that matches that specific signature.
Notice that SendMailWithEvents doesn’t receive any
parameters. So where does the method get its state information from to send the
email? From the object instance, of course. A new thread is created, but this
new thread still runs within the context of the current object – the wwSMTP
object. So although the code runs on a new thread it can still access the wwSmtp
object instance as this and has access to all of the information required
to send an email.
If you change the code to run SendMailAsyncWithEvents() in
the VFP code snippet, you should see the behavior change. The code runs, calls
SendMailAsyncWithEvents() and returns immediately to the VFP command window.
Then after a few seconds you should see the events firing on your Fox desktop in
background. In essence, the .NET code is now running in the background while
your FoxPro application is free to do other stuff.
The SMTP.scx form shown in Figure 2 again demonstrates this more clearly. Run
the form again and then click on the Async button. When running asynchronously
the call returns to VFP immediately and the user interface remains live and
active while the .NET code executes the email operation on another thread. Click
send and then move the form around. Unlike the Send button which freezes the
user interface, the user interface remains live and responsive while the email
is sent on the background thread.
This is a real simple example of multi-threading with .NET.
In the next article of the series we’ll take a closer look at how to build
multi-threaded .NET components and use them efficiently with Visual FoxPro.
What about publishing Events?
Earlier I mentioned that VFP does not support publishing
COM events natively. You can however, fire events in .NET initiated by Fox code
by creating wrapper methods that manage the event publishing for you.
The wwSMTP class already contains the SendMailMessages
event which can fire events. What’s missing is a mechanism for our VFP code to
fire this event. To do this you can add another method:
public
void RaiseSendMailMessages(
SendMailMessageModes Mode,
string
Message)
{
if (this.SendMailMessages)
this.SendMailMessages(Mode,Message);
}
And now your FoxPro code can fire events on this COM object
by calling this method. To try this out in VFP try this:
loSMTP = CREATEOBJECT("DotNetCom.wwSMTP")
loSMTP.MailServer="localhost"
loSMTP.Username = "rick"
loSMTP.Password = "supersecret"
loSMTP.SenderEmail = "rstrahl@west-wind.com"
loSMTP.SenderName = "Rick Strahl"
loSMTP.Recipient = "rickstrahl@hotmail.com"
loSMTP.Subject = ".NET Test Email"
loSMTP.Message = "Email
from .NET at FoxPro DevCon"
*** Hook up the event
interface
loEvents = CREATEOBJECT("IwwSmtpEvents")
EVENTHANDLER(loSmtp,loEvents)
DOEVENTS
? loSmtp.SendMailAsyncWithEvents()
loSMTP.RaiseSendMailMessages(0,"Hello from VFP")
You’ll see the FoxPro Message fired through the event
interface of the COM object. Of course, this is a contrived example that
demonstrates firing an event in .NET from FoxPro and then also capturing the
event in FoxPro. But consider that the event could be handled by any .NET code
now. This means if you are firing off a new thread in .NET other objects can
connect to the wwSMTP instance and get notified when a message is sent. You have
just published an event from FoxPro!
Firing COM Events
In addition, you can also use a .NET COM object to publish
events to COM, so a COM client like Visual Basic 6 can consume events. This is a
little bit more involved – the VB application would have to have a reference to
both your Fox component and the .NET COM Component. You’d then create a class
that contains the .NET COM object as a property and this child object can then
be used to fire events into the COM Client application. Listing 5 shows how you
can set up your VFP COM component for this.
DEFINE CLASS
SmtpEventPublisher as SESSION OLEPUBLIC
cRecipient = ""
cSenderName = ""
cSenderEmail = ""
cBody = ""
cSubject = ""
cMailServer = ""
*** .NET COM object with
events
PROTECTED oSMTP
oSMTP = null
FUNCTION INIT
this.oSmtp =
CREATEOBJECT("DotNetCom.wwSMTP")
ENDFUNC
*** Allow returning the .NET
Event Object
*** Using a method to be able to return
*** proper COM type – can’t do with property
FUNCTION GetSmtp() as
DOTNETCOM.wwSmtp
RETURN this.oSMTP
ENDFUNC
FUNCTION SendMessages
LPARAMETERS
Mode, Message
this.oEvent.RaiseSendMessages(Mode,Message)
ENDFUNC
ENDDEFINE
Note the GetSmtp method which returns a typed COM object. I
use a method here because VFP doesn’t support returning typed COM properties
even using COMATTRIB. The only way I found to return a specific COM type is by
using a method return value AS <ComType>. This ensures that the VB (or other
COM) client can see the object reference as a the proper type and also
automatically forces the .NET COM typelibrary to import as long as the VFP COM
object is referenced as it is a dependant COM object. The COM client can now
hook up to this COM object’s event interface and your FoxPro code can fire the
events by calling the SendMessagess() method.
Voila, FoxPro firing COM events. This is not trivial to do,
for sure, but if you have an urgent need to fire COM events with VFP this is one
way to accomplish the task. By the way you can also use the same approach with
Visual Basic as the COM Event Publisher – the approach works with any COM tool
that can publish COM events.
The approach described above can also be used to allow non
COM .NET Compoents connect to your events. They only need to import the type
library of your COM component to receive events from your FoxPro component.
Another thread yarn in the next article
Multi-threading and event handling in combination are a
very powerful feature that can open up entire new horizons for your Visual
FoxPro applications. With this functionality it’s possible to offload some heavy
processing to a background thread and have it do the processing in .NET. You can
even take this one step further and use .NET to call VFP COM components on new
threads which essentially gives you the ability to create multi-threaded VFP
applications!
We’ll pick up and re-join this thread in the next article
when I look at a few examples of what you can do with multi-threading including
running Windows Forms from Visual FoxPro, the aforementioned multithreaded VFP
server interface and more.
Comments or Questions
|