White Papers                  Home |  White Papers  |  Message Board |  Search |  Products |  Purchase | News |  Web Log |
 
            
 

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.

 

Listing 1 – Basic wwSMTP COM implementation

[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.

 

Listing 2 – Adding an event to the wwSMTP class to notify of progress

[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.

 

Listing 3: Exposing events to COM requires a separate event interface

[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.
 

Listing 4 – Creating asynchronous email processing with a new Thread in .NET

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.

 

Listing 5 – Using a .NET object to publish COM events

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

If you find this article useful, consider making a small donation to show your support  for this Web site and its content.

 

By Rick Strahl
Rick Strahl is president of West Wind Technologies on Maui, Hawaii. The company specializes in Web and distributed application development and tools with focus on Visual FoxPro and .NET. Rick is author of West Wind Web Connection, a powerful and widely used Web application framework for FoxPro, West Wind HTML Help Builder and West Wind Web Store for both FoxPro and .NET. He's also a Microsoft MVP, a frequent speaker at international developer conferences and a frequent contributor to magazines and books. He is co-publisher of Code magazine. For more information please visit: http://www.west-wind.com/ or contact Rick at rstrahl@west-wind.com.

 

 

If you find this article useful, consider making a small donation to show your support  for this Web site and its content.


 



 

 

 
  White Papers                  Home |  White Papers  |  Message Board |  Search |  Products |  Purchase | News |