Share Data Between Apps Without COM

Here's an easy way to share data between 2 VB applications. Since each app runs in its own process space, a variable in one application is not meaningful to another application in a different process space.

Typically you pass data between apps by turning one of them into an ActiveX server to create a client/server relationship using COM to handle the inter-process communication. Your client application sets or reads a property in the server application and you pass the data that way.

Here is a faster, simpler approach using the SendMessage API function. I will demonstrate a few variations of this technique: passing strings then a larger amount of data. Unlike COM, however, this approach will not work cross machines.

Send a data to another application without COMReceive data passed from another application
Download Source Code

Overview

Application A communicates with application B by sending a message to a window in application B. The message carries with it the desired data. This sample consists of 2 programs. As their names imply, SendData and ReceiveData send and receive data respectively.

Application B needs to know when the message arrives and then needs to process it. Application A may want to have application B process the data either synchronously or asynchronously.

Sending a string to application B is easy. Remember, the form and everything on it, such as a textbox, are windows. A textbox is ideal for receiving a string since its Change Event will automatically fire when a WM_SETTEXT message is received. This event can contain whatever logic is necessary to process the string. My sample application simply displays a message box.

The problem with sending data to a textbox is that there is no good way to distinguish among multiple textboxes on the same window. So, I load an additional form containing a single textbox. This form is invisible since it is Loaded but not shown and its Visible and ShowInTaskBar properties are set false.

This solves another issue as well. The easiest way to find the window containing the textbox is by looking at its caption. If two applications have windows with similar captions we may not find the correct window. Using an invisible form lets you set its caption to one that is unlikely to be duplicated in another program.

Sending a String

I call the FindWindow API function with the caption of the target window in the ReceiveData application. FindWindow returns the handle of the window containing the textbox. Now we need the textbox's handle. FindWindowEx can be used to find child windows. I pass it the window's handle and since the window only has a single child, the textbox, FindWindowEx returns the desired handle.

Next I call SendMessage to pass a string from Application A to the textbox in Application B. SendMessage takes as parameters the handle of the textbox, the WM_SETTEXT message and the string to pass.

Receiving the String

When the textbox receives the message its Change event uses a message box to display it. The SendMessage call waits until the message box is dismissed before processing in Application A continues. To allow Application B to process the data asynchronously and have Application A continue to respond, you have 2 options. First, you can use the SendMessageTimeout function. With this call you can set the time interval after which the API will return. Or, you can call the ReplyMessage API in the textbox's change event. This API returns a value to the SendMessage call telling it to return and continue processing.

Sending String Data

Sending larger amounts of data is harder because we need to use the WM_COPYDATA message. This message lets you use Memory Mapped files to communicate between applications. This is what COM does. Problem is, the target window does not automatically respond to this message. Subclassing the window is required to trap the message.

To pass string data from one Visual Basic application to another, the Unicode string must be converted to ASCII before you pass it. The receiving application must then convert the ASCII string back to Unicode.

Using the CopyMemory API, the string is converted to an ASCII byte array. We now need to populate a CopyDataStruct structure to pass the data. The trick here is that the lpData element of CopyDataStruct must be a pointer to the data to pass. To do this we must use Visual Basic’s undocumented VarPtr function which yields the address of variables and user-defined types (similarly StrPtr returns the address of a string and ObjPtr the address of an object).

Lastly, the SendMessage function sends the WM_COPYDATA message along with our CopyDataStruct structure.

Receiving String Data

To receive the string data the ReceiveData application must hook into Window’s message stream to catch the WM_COPYDATA message. This is accomplished by using the SetWindowLong API with the GWL_WNDPROC flag to replace the original procedure called whenever the form receives a message with our own fWindowProc callback procedure.

When a message is received the fWindowProc function is invoked. If it is the copy message, a call is made to the pReceiveMsg procedure. In any case, the CallWindowProc API is called to pass the message to original window procedure.

PReceiveMsg uses the CopyMemory API to copy the CopyDataStruct sent to this application to a local structure. A second call to CopyMemory copies the string pointed to by the lpData element of CopyDataStruct into a byte array. The string is converted back to Unicode and then displayed.

Other Types of Data

Other types of data can be handled similarly. This sample can also pass an array of strings and an array of doubles. For simplicity sake, the array of strings is first stored in a PropertyBag to facilitate converting it to a byte array.

An Aside on PropertyBags

The PropertyBag object allows you to accept a series of values and store them as a stream of bytes. It lets you store a key/value pair and use the key to extract the value, similar to a Collection. This is where the similarities stop, however.

The PropertyBag object is really a stream of key/value pairs. Writing to the propertybag appends to the end of the stream without deleting anything. Reading from it reads in a round robin fashion. For example:

   Dim pb As New PropertyBag
   pb.WriteProperty "Dept", "Sales"
   pb.WriteProperty "Name", "Dave"
   pb.WriteProperty "Name", "Susan"
   pb.WriteProperty "Name", "Jordan"
   pb.ReadProperty("Name") 'Dave
   pb.ReadProperty("Name") 'Susan
   pb.ReadProperty("Name") 'Jordan
   pb.ReadProperty("Name") 'Dave Again!

You can easily persist the PropertyBag's contents to a file:

   Dim pb As New PropertyBag
   Dim varIn As Variant
   Dim varOut As Variant

   pb.WriteProperty "Title", "Developer"
   pb.WriteProperty "Name", "Dave"
   pb.WriteProperty "WebSite", "www.thescarms.com"
   varOut = pb.Contents
   Open "c:\temp\data.dat" For Binary As #1
   Put #1, , varOut
   Close #1
   Open "c:\temp\data.dat" For Binary As #1
   Get #1, , varIn
   Close #1
   pb.Contents = varIn
   Debug.Print pb.ReadProperty("Title")   'Developer
   Debug.Print pb.ReadProperty("Name")    'Dave
   Debug.Print pb.ReadProperty("WebSite") 'www.thescarms.com

The standard PropertyBag does not let you display a count of its items or enumerate through them. The above download contains a class module that extends the standard PropertyBag by adding these features.

Instructions

Start both sample applications.

  1. Enter a string into the textbox of the SendData application. Click the "Send the Above String" button. The text appears in the ReceiveData application.
  2. Clear the textbox in SendData. Click the "Get a String from its Textbox" button. The text appears in the SendData application.
  3. Enter a new string into the textbox of SendData. This is important. If you send the same text again, the textbox's Change event will not fire since the text hasn't changed. Select the "SendMessage" option. Click the "Send a String to its Textbox" button. The Change event for the textbox on the hidden window in the Receive Data application fires and displays a message box.

    Note that the SendData application does not respond until you dismiss the message box.
  4. Enter a new string into the textbox of SendData. Select "SendMessageTimeout". Click the "Send a String to its Textbox" button. Again the Receive Data application displays a message box.

    This time, however, the SendData application does respond without dismissing the message box because the SendMessage call timed out.
  5. Change the text in SendData again. Now check the "Send Reply" box on "Receive Data" and select "SendMessage". Click the "Send a String to its Textbox" button. Again the Receive Data application displays a message box.

    Now, event though you used the synchronous SendMessage command, the SendData application will respond without dismissing the message box.
  6. Sending text clears the textbox. Copying text appends to the existing contents. Click the "Copy a String to its Textbox" button to try it.
  7. In the SendData application, enter the number items to send. Click the "Send Numeric Data" button to populate the listbox.
  8. Click the "Send String Data" button to populate the listbox.



About TheScarms
About TheScarms


Sample code
version info

If you use this code, please mention "www.TheScarms.com"

Email this page


© Copyright 2024 TheScarms
Goto top of page