This tutorial will walk you through the mechanisms of non DDE inter process communication. Inter process communication is something that may become vital to the future of your application. I chose for this article to achieve communication between two very different (and popular) programming languages: Visual C++ (version 6 to be more specific) and Visual Basic (again, version 6). The contents of this article will however probably work with no or little modifications in Visual C++ .NET and Visual Basic .NET too.
We can use the term of inter process communication not only for two applications but also for an application and a DLL, which is exactly the topic covered here. Even more, this article will also be useful in a situation when you need to link two applications. Since we will use API and callbacks, there won’t be too much of a headache for the reader to also understand how to achieve communication between two applications or two DLLs or more complex arrangements.
Note that even if you already know how to communicate between two or more processes, this article may still help you in a better understanding of this concept and may present new ideas and tricks. Beginners or programmers who aren’t so familiar with the subject will definitely find a lot of interesting things here.
My personal battle with inter process communication was caused by the need to combine the RAD (Rapid Application Development) power of Visual Basic with the efficiency of Visual C++. It is well known that Visual Basic 6 is unable to build true DLLs (only builds COM). Unfortunately, when I started one of my big projects, I was unaware of such a thing and I only discovered that when it was too late to port everything to Visual C++ (I would also lose the RAD and ease with which VB handles ActiveX). Inter process communication saved me from a very dangerous position where I would be forced to waste months porting to Visual C++ or just give up on one of my project’s most important functionalities!
This is what this kind of communication is very good for: combining the advantages of two or even more programming languages through the use of Windows API and DLL exported functions. In my case, take the RAD from Visual Basic and the power of Visual C++, to reach the best results.
Article Code And How To Use It
The code consists of a Visual Basic 6 test application and a Visual C++ 6 test DLL. Simply download and extract the Zip archive at a convenient location and after that run the Visual Basic executable provided. The code for both the test application and the DLL is very well commented! I wrote it this way so that anybody can understand it.
Zip Archive Content
- Full source code and demo projects for the Visual Basic 6 test application and the Visual C++ 6 test DLL.
- Diagrams (can be displayed directly via the TestVBApp.exe) to help the reader for a faster and better understanding of the code and of the processes involved, plus this article in a nicely formatted Word document.
This article focuses only on a Visual Basic application and a Visual C++ DLL because it’s a situation which will cover all the required areas of inter process communication without becoming too complicated. If your personal project structure is different, the APIs and techniques detailed here will help you none-the-less. If you only work in Visual Basic, things will be even easier because Visual Basic allows easy connection of components. This article will handle only the trickier situations, things you might consider as “hard”.
Newbies to programming techniques such as subclassing will also find this article highly educative, since I will be using subclassing in Visual Basic to achieve some little tricks which I discovered while pulling my hair out in front of the computer during late nights when I tried to link it with my Visual C++ work.
- Have Microsoft Visual C++ 6 or newer and Microsoft Visual Basic 6 or newer installed on your computer.
- Intermediate level Visual C++.
- Intermediate level Visual Basic 6.
- Windows Operating System (should work on any version).
Although I checked my code, comments and article a lot of times, I am not a guru of computer programming. Don’t expect my code to be perfect. Maybe there are some mistakes in the comments too. However, trust me that I did my best to provide you with a well written, well coded article with as few mistakes as my skills at this time made possible. Please forgive me for any silly mistakes that I might have done, and if you have the time, drop me an e-mail (axonnus at yahoo dot com) with any opinions or advices you might have. We’re all here together to improve each other and that’s exactly what I am trying to do. I do enjoy constructive criticism!
Before finally getting to work, I would like to thank the entire internet community for helping me so much in my evolution as a computer programmer. Also thanks to Nicholas Skapura for an article that gave me quite a lot of technical insight into the works of communication between VB and VC++ and Randor for fixing the callback C++-VB array issue. Of course, my deepest “Thank you” message for the people helping on the Code Project message boards, especially for those who answered my questions : ). They were there for me countless times and almost always gave me good ideas and helped me solve problems. The same message also stands for the other forums all around the internet and people who sacrifice their own free time and keystrokes for the good of others. You all have my respect and I hope that I will help too, through my articles and software, with a tiny bit of knowledge to everybody. Thank you all.
Good question. Even though Pascal was my first programming language, VB is where I spent a lot more time than in any other (although C++ is catching up fast). A lot of people underestimate VB. That maybe because they either consider it “too un-elite” or simply don’t know how to use it. The truth is that indeed VB is unable to do some things, but it has other advantages. Big advantages. For example, working with ActiveX or developing User Interfaces. VB means Rapid Application Development and it sure can make your life easier.
The layout of the example is quite simple. A Visual Basic application is used to work with a VC++ DLL. The VB application calls various methods from the VC++ DLL and the DLL answers with the appropriate actions.
Demonstrate will not expect any result from the DLL.
Demonstrate will actually trigger the DLL to perform a certain callback to a VB function or some other type of demonstration.
Private Sub cmdStringDemo_Click()
Demonstrate function in the VC++ DLL will launch a particular demonstration depending on the integer parameter sent to it.
void WINAPI Demonstrate (int WhatToDemonstrate)
CallVBFunctionForString("Greetings from VC++!");
VCStringArray will interact with the DLL using simple variables, arrays or structures.
An important part of the example relies on subclassing. Subclassing is used to get in full control of the messages that the VB application receives from the Operating System. The VC++ DLL will use
WM_USER + 241 (241 being an arbitrary value that I chose) to send string data to VB. Or
WM_COPYDATA to send numbers, strings or string arrays to VB.
In Visual Basic, subclassing is quite easy to achieve, provided that you have the proper API functions and constants declared. Subclassing is a very powerful tool and it allows the programmer extensive control over a Visual Basic application. Without such a tool, it would be impossible to catch and react on messages as the ones I just mentioned above.
This particular area of the example is very well explained through the comments in the code and the reader should find it quite easy to understand.
Hey, C++, Where Art Thou?
Almost any VB programmer should know how to declare an API function. If you don’t, no problem, you will now. But how about a function from a C++ DLL? Well, first of all, the DLL must export the function. I used a .DEF file in which I included the functions to be exported, like this:
DESCRIPTION "Test DLL used for demonstration "
"of linking Visual C++ with Visual Basic."
DLLShare READ WRITE SHARED
That’s pretty much a list of all the functions exported. The
@X after some function names indicates they require some arguments. Of course, you also have to properly declare the functions in the CPP. I used the
WINAPI tag, like this:
void WINAPI VCNumber (long ValueFromVB)
As for the VB side, here’s how to declare a function from our DLL (luckily, that’s the same as API functions):
Public Declare Sub VCNumber Lib ".\Debug\TestVCDLL.dll" (ByVal Value As Long)
Easy huh? Of course, the TestVCDLL must be in the Debug subfolder of the folder where you’re running the Visual Basic test application from.
Reaching Over To VB
Traveling from VB to VC++ wasn’t so hard. The other way around however, it’s a bit trickier. First of all, our VB isn’t a DLL, but an application. But it’s an application which runs. So then any function from it has an address in the memory of the computer. To permit the DLL to call a function from VB, it must first know the address of such a function. VB will send some function addresses to the DLL via the
Activate AddressOf NumberDemoFunction, _
AddressOf StringDemoFunction, _
AddressOf StructureDemoFunction, _
Back in the DLL, the
Activate procedure will save those addresses and the DLL will use them later to call functions from VB whenever it needs to do so.
So now we can call functions from the DLL whenever we need, and back from the DLL functions from the EXE whenever we need. Next, those functions have to do something. Since I don’t need to care what your applications or DLLs will do, I don’t need to care about complex functions inside routines or any of that stuff. All I care is how to get your data from one place to another as fast and reliable as possible. There are lots of methods to do this and I explored a few of them in the example.
Passing By Reference
This way VB can call a function from the DLL passing some parameters by reference. The DLL can modify the parameters and then VB can have access to the new values. It’s a standard approach which doesn’t really need any other additional comments. Here’s the VB code for calling some DLL functions:
Private Sub cmdGetDataDemo_Click()
Dim cmStatus As DLLStatusStruct
And the DLL function itself:
void WINAPI GetDLLData (DLLStatusStruct *Response)
Response->lFirst = extNumberProc;
The VC++ DLL modifies some members of the structure and when execution returns to VB, the test application will have access to the new values.
I will now concentrate on the trickier methods that I covered across the example.
VC++ To VB calls
Here’s how I send a number to VB from the DLL (This call executes independently from the VB application’s internal conditions. It can be a result of a timer inside the DLL or any other DLL-internal condition. In the example, it’s the
Demonstrate procedure which calls the shots which in turn is called from VB, but this doesn’t mean that the DLL can’t call on the VB function any other given moment.):
void CallVBFunctionForNumber (long lSomeValueToSend)
typedef void (__stdcall *OutsideFunction)(long AValue);
FunctionCall = (OutsideFunction)extNumberProc;
The comments pretty much explain it. We’re defining a <CODE lang=mc++>__stdcall calling convention function prototype, then that is being used to declare the function instance, and then the function instance is linked with its address (which was received from VB via the
Activate function). After that, the function from VB is called.
In this case, in VB it isn’t really a function, but a procedure, a
Sub. It doesn’t return anything; therefore the return type of our function is
void. However, if you wish to return another number, simply change it to
long and change the sub from VB into a function that returns a
If you wish to return a string however, beware, for VB uses
BSTR as the return type. The code carefully explains this and provides you with comments and techniques to handle this.
The VC++ DLL offers plenty of examples of such calls. Even arrays can be sent. Check out the
CallVBFunctionForArray which can send numeric or string arrays to VB. Since it uses the
LPSAFEARRAY data type, it’s a bit trickier, but it’s working, and once understood, it’s easy to exploit.
Harnessing The Power Of Subclassing
Now here’s a trick you don’t stumble upon every day (or do you...).
Suppose you don’t want to bother sending function addresses from VB to VC++. Suppose you don’t want to go through all that <CODE lang=mc++>__stdcall casting and any of that stuff. Or maybe you wish to do an asynchronous call. Here are some ways that can do all those and even more, could simplify your life. Take a look at this little piece of code:
SendMessage(hwndExternalApplication, WM_USER + 241, NULL,
(LPARAM) "Addresses of VB functions saved."
" Ready for demonstration. (Sent via WM_USER + 241)");
That’s all you need to send that string (the one cast to
LPARAM) to VB. Of course, there’s a catch. The bulk of the code is in VB. Take a look at the module of the VB application in the example, at the
MsgProc function (the function which is responsible for the subclassing). However, the comments there prove that this is actually not a difficult approach. Also, sending data like this can be done asynchronously. While callbacks are synchronous, you can use
PostMessage to do things asynchronously, without having to bother with multiple threads or any of the like.
And there’s more to it. That isn’t the only thing you can do with subclassing and message sending. Here’s one more nice trick that can be done.
The World Of WM_COPYDATA
This nifty message can allow you to send pretty much anything from VC++ to VB. I used it to send numbers, strings, arrays of numbers and even arrays of strings. In C++, things are rather simple; again, just like with
WM_USER + 241, you don’t need to know anything about the application where you’re sending the data, except the handle of its window. You only care about sending the message; after that, the external application (in our case, the VB test application) will take care of the rest.
WM_COPYDATA works through the usage of the
COPYDATASTRUCT structure. Before sending the message itself, you need to set those members appropriately. Here’s how I sent a string to VB:
strcpy(cTemp, "This is the string passed via WM_COPYDATA.\0");
sString = SysAllocString(ConvertCharToBSTR(cTemp));
cdsData.dwData = 1000;
cdsData.cbData = strlen(cTemp)*2;
cdsData.lpData = sString;
Again, I urge you to look in the VB code of my example for the full details of how this is handled VB-side. It’s very well explained and I don’t want to clutter the article with useless information when you can easily read it from there.
WM_COPYDATA works directly using a memory structure, in VB you need to know what kind of structure to interpret. I used the
dwData member to tell VB what kind of structure it should expect to find in the
lpData member. It’s quite nice I believe. As I said, communicating like this removes the need of sending internal function addresses to VC++ and calling them using <CODE lang=mc++>__stdcall casts or stuff like that.
Using the same
WM_COPYDATA, I also managed to send arrays of number or strings without any kind of problems whatsoever. A cool thing that I discovered is that an array from VB can be modified any time by the VC++ application simply by sending a
WM_COPYDATA message. Because the
MsgProc subclassing procedure executes whenever a message is received by the VB application, if you send a
WM_COPYDATA message to update an array, this will be done without any other intervention required by VB. So you can practically update VB-side variables synchronously or asynchronously from the VC++ DLL. This could be put to very good use in case you have a DLL engine for a VB application and the engine constantly updates things in the VB application. The engine will update whatever data structures that need updating, and once the subclassing is in place, you don’t have to move a single finger in VB.
There are also other methods of communication between processes. You could consider DDE, sockets, files or whatever else you think might suit you better. I have covered here several methods which can be exploited easier and probably faster. Also, these are the methods which I personally used in my biggest project to date, a freeware application which is yet to be released at the time I published the first version of this article.
Sending data using messages such as
WM_COPYDATA provides a fast and simple way to the target. It also gives the possibility of asynchronous data transfer which might come in handy for many applications.
I hope you enjoyed this article at least as much as I enjoyed writing it for you. It’s my first article and my first public contribution to the internet programmers community. It took a long time to finish since I was very busy with work and other projects, but at last, it’s here now...
- 2005-11-10 (Thursday, 10th November) – Version 1.1. Article updated. Example updated. Fixed VC++ to VB array issues (thanks Randor!).
- 2005-11-06 (Sunday, 6th November) – Version 1.0. Article & example.