Introduction
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.
General Purpose
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.
Coverage
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.
Recommended Pre-Requisites
- 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).
Warning
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!
Thanks To...
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.
Why VB?!
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.
Example Architecture
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.
Functions like Activate
and 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 1
End Sub
The Demonstrate
function in the VC++ DLL will launch a particular demonstration depending on the integer parameter sent to it.
void WINAPI Demonstrate (int WhatToDemonstrate)
{
switch (WhatToDemonstrate)
{
case 1:
{
CallVBFunctionForString("Greetings from VC++!");
break;
}
...
}
}
Functions like GetDLLData
, VCNumericArray
or VCStringArray
will interact with the DLL using simple variables, arrays or structures.
Subclassing
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_SETTEXT
and 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:
LIBRARY TestVCDLL
DESCRIPTION "Test DLL used for demonstration "
"of linking Visual C++ with Visual Basic."
EXPORTS
Activate @1
Demonstrate
VCNumber @2
VCStructure @3
GetDLLData
VCNumericArray @4
VCStringArray @5
SECTIONS
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
procedure.
Activate AddressOf NumberDemoFunction, _
AddressOf StringDemoFunction, _
AddressOf StructureDemoFunction, _
AddressOf ArrayDemoFunction
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
...
GetDLLData cmStatus
...
End Sub
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);
OutsideFunction FunctionCall;
FunctionCall = (OutsideFunction)extNumberProc;
FunctionCall(lSomeValueToSend);
}
The comments pretty much explain it. We’re defining a __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 Long
.
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 __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:
COPYDATASTRUCT cdsData;
char cTemp[50];
BSTR sString;
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;
SendMessage(hwndExternalApplication, WM_COPYDATA,
(WPARAM)hwndExternalApplication, (LPARAM)&cdsData);
SysFreeString(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.
Because 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 __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.
Conclusion
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_USER
or 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...
History
- 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.