Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Visual C++ DLLs Need a DEF File for VB Clients : Page 2

This article explains a few quirks you might bump into when writing a C++ DLL for use with Visual Basic. The problem is caused by how the DLL exports its functions, but you can't work around it by preparing a companion DEFinition file.


Writing DLLs with C/C++
The message shown above is remarkably clear: there's no Check function in the DLL! At least this time, the culprit is not Visual Basic, but a particular feature of Visual C++.

When you write DLLs with C or C++ you have to qualify the function with a macro to specify the calling convention and make sure the function will be regularly exported. Macros such as APIENTRY, WINAPI, or a keyword such as __stdcall do just this. They are all synonims. Using the standard convention (also known as the PASCAL convention) causes the parameters of a function to be inserted onto the stack in the same order they appear in the function's declaration from right to left. As a result, the function will pick them up in the reading order. (The stack follows a LIFO discipline.) Adhering to the standard convention also means the all the arguments are assumed to be by value unless a pointer is explicitly mentioned and the stack is cleaned up after the call.

Let's try to recompile the Visual C++ DLL without the WINAPI qualify, and following a non-standard C-based convention. What changes is just the error!

What happens is that now Visual Basic finds out a function called Check but it doesn’t like the function's calling convention –which is not the Pascal's.

Looking at the C++ function declaration, notice the keyword extern "C". This is necessary only if you're using the C++ language, that is the file is compiled as C++ and not C. It just informs both compiler and linker that the function is not a public member of any class but must be considered as a simpler standalone, global symbol. In other words, that function name must not be changed to fit into a class name scheme. (When you exports C++ classes, their methods are exported through different names. The new name is a combination of the method's name, the class name and the prototype to make the final symbol unique. Such a process is known as name-mangling and may differ from compiler to compiler!)

Overall, the extern "C" keyword is not responsible for the errors we're having. The same thing can be said regarding WINAPI. Perhaps, the point to investigate further is the keyword that actually causes the function to be exported.

Exporting Functions from C/C++ DLLs
From Visual C++ 5.0 on, Microsoft recommends the use of _declspec(dllexport) to export a DLL function. There's an alternative technique: don’t add anything at the level of the function's declaration but group all the exported names in a definition file (DEF) to be linked to the project. The use of a DEF file is suggested to be somewhat obsolete. The DEF file for a simple DLL exporting just a Check function is like this:

LIBRARY vbcpp EXPORTS Check @1

You just declare the library and lists the exported functions with an optional number that can be used as well as the function name to load the function dynamically.

In theory these two techniques should be completely equivalent. In practice, this is true only if you're calling the DLL from within C/C++ programs. If you want to call the DLL also from VB, then only the DEF technique works fine! Let's see the details.

It's Visual C++'s fault…
By design the Visual C++ applies a sort of name-mangling to function names when it detects the standard PASCAL calling convention (WINAPI, APIENTRY or __stdcall). Due to this, a simple Check function becomes an odd _Check@4, as shown below.

The screenshot reproduces a portion of the output that dumpbin.exe generates. It is a utility you find in the BIN directory of Visual C++. It returns information about the content of a Win32 binary module. The command line I used to get this is:

dumpbin /EXPORTS vbcpp.dll >vbcpp.lst

The switch EXPORTS makes it enumerate all the exported symbols. As you can see, there's no mention of a Check function which justifies the error we got above. The mangling, and the subsequent error 453, is due to the PASCAL calling convention. On the other hand, if we change the calling convention we obtain another error! One possible solution is… using another compiler to create our C/C++ library. The same scenario doesn’t occur if you compile with the Borland C++ compiler, for example! Another solution might be calling the function with its actual name: _Check@4. However, this isn’t a valid name to Visual Basic. With reason!

Actually, I've never experienced this…
I write DLLs almost every day and a large number of them ends up being called from within Visual Basic. So, imagine my surprise when I heard of this. Taking for granted that any DLL function must be declared WINAPI and that you have to prefix it with extern "C" if you're writing a C++ file, what remains to decide is how to actually export a function to make it callable from VB. You have just to use a DEF file. The name that the linker writes in the DLL file is what is written in the DEF file if any. The linker works in two steps. Firstly, it mangles the name of the function as soon as it realizes about the PASCAL convention. Secondly, it writes down in the binary file the name defined by the DEF file. If no DEF file is specified then the function names default to the mangled ones. Since I've never used _declspec to export functions, but always resorted to DEFs, actually I've experienced this. Thus, remember to export functions through a DEF file to make them callable from VB clients. This behavior applies to both Visual C++ 5.0 and 6.0. Everything works fine, instead, with previous versions and other vendor's compilers.

Dino Esposito is a trainer and consultant based in Rome, Italy. He specializes in Windows and COM development and authored many programming books, among which Visual C++ Windows Shell Programming; and Windows Scripting Host Tutorial, both from Wrox Press. Dino is also a contributing editor to MIND, MSJ and MSDN News, and has written articles for many other magazines. He currently works for Artis srl, a Rome-based consulting company which is active in the system integration area and Web-based architectures. Dino is also a frequent speaker at industry conferences such as Microsoft DevDays and TechEd. You can see a list of his articles in the Magazine Bank.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date