April 1998
Download Apr98VPcode.exe (40KB)
In addition to developing the coolest Windows-based development tools with Stingray Software, George teaches seminars with DevelopMentor. Scot is a co-founder of Stingray Software. George and Scot are co-authors of MFC Internals (Addison-Wesley, 1996).
|
Q I want to call a certain kind of function from Visual Basic. The function opens up a dialog box, accepts some text and returns the text to the Visual Basic-based program. The dialog box is in an MFC DLL I created with Visual C++. How can I export a member function? What function within the DLL should be called?
Robert Ginns
A Thanks for the question, Robert. You're hitting upon some fundamental issues that come up when you try to integrate components written in two different languages like C++ and Visual Basic®. There are actually several ways to get Visual Basic working with dialogs (or some other C++ class) inside your MFC DLL. In this example, we'll create a simple dialog in MFC, stick it in a DLL, and then invoke the dialog from Visual Basic. Figure 1 shows what the end result will look like.
via CompuServe |
Figure 1 Dialog in a DLL |
Calling DLL Functions from Visual Basic
|
|
Of course, Visual Basic syntax is somewhat different than C++ syntax. Visual Basic uses the Declare keyword for prototyping a function. Unlike C++ (in which all subroutines are really functions), Visual Basic distinguishes between functions that return values and functions that return void. That is, Visual Basic syntax includes two keywords for prototyping subroutines: Sub and Function. The name of the function or subroutine comes next. If the function isn't native to the Visual Basic runtime, then the keyword Lib tells Visual Basic the name of the DLL containing the function (that is, which library to load). Finally, the arguments being passed to the function are listed in parenthe Ses. This is the Visual Basic prototype of TestFunction: |
|
Notice how the Visual Basic syntax really simplifies things. To call this function from a C++ program, you'd have to do one of two things. First, you could call LoadLibrary
on visprog.dll and then call GetProcAddress to get the address of TestFunction. Alternately, you could use visprog's import library (visprog.lib) to resolve the addresses. If
you have the function prototyped correctly in Visual Basic, then the Lib keyword does all the work for you. Armed with this knowledge, how can you access an MFC dialog from Visual Basic?
Accessing an MFC Dialog from Visual Basic
Using a Single Entry Point
|
|
The DLL shows the dialog without flinching. Having the dialog available to the program at all is definitely a good thing. What if the client code needs a bit more control? Perhaps your Visual Basic-based application needs to create an instance of the dialog and hold on to it for a while, manipulating it from time to time. In addition, it's always better to use an object-oriented approach. Let's see if there's a way to accomplish that.
Exporting C++ Classes from a DLL
|
|
Using the special declaration is actually quite convenient. If you use this declaration in your DLL, then you can write a C++ program that employs the dialog class as though it was linked through static linking. For example, you can use operators new and delete without a hitch.
This is, in fact, the approach taken by MFC to share its classes within the MFC DLLs. When you develop an application and tell AppWizard you want to use MFC in a shared library, you're telling AppWizard to generate source code that generates links to the MFC DLL. Most of the MFC classes are exported from the DLL in this manner. However, using this approach from Visual Basic doesn't wash for two main reasons. The first and most obvious reason is that Visual Basic doesn't have an operator new like C++ does. How do you create an instance of the CSomeMFCDialog class? There's no way to do this in Visual Basic. The second problem has to do with name mangling and isn't quite so obvious. Here's what happens when you expose the dialog class using _declspec(dllexport). The compiler simply takes each of the class's member functions and exports the M using the C++ mangled type. So, even if you could find a way to instantiate the class from Visual Basic, using it would be a pain. The following shows the names of the member functions as they are exported by the compiler: |
|
Can you imagine typing in the Visual Basic prototypes for the Se? You'd have to get every single character correct. In addition, there's a hidden parameter in each of the Se functions that you'd need to account foreach of the Se functions includes an implicit this pointer. In all practicality, the Se function names are really only for use by the Visual C++® compiler to resolve addresses in the DLL. This is definitely not the way to expose the C++ class to Visual Basic. There must be a better way.
Wrapping the DLL Functions
Implementing a COM Interface
|
|
Using COM Automation simplifies many aspects of linking from the client side. The prototypes are gone. Visual Basic reads the type information included with a COM class to figure out how to call methods on the class. In addition, the actual name of the DLL is missing, too. That's because the names and paths to COM DLLs are managed by the registry. the Only information the client needs to know is the name of the Object (used in the CreateObject function) and what methods may be invoked.
Finally, you may choose to implement normal interfaces on your C++ classVisual Basic understands more than just IDispatch. If your COM interfaces use automation-compatible data types as parameters, Visual Basic will have no problem understanding type information and using the Object.
Conclusion
Have a question about programming in Visual Basic, Visual FoxPro, Microsoft Access, Office, or stuff like that? Send your questions via email to visual_developer@stingsoft.com or George Shepherd at 70023.1000@compuserve.com. |
From the April 1998 issue of Microsoft Systems Journal.