Using a C++ DLL in a Visual Basic project


Search this site

This week I have had to make a VB6 project interface with a 3rd party API packaged as a DLL and written in C++. Specificly, I am working on interfacing Embtrak, the leading embroidery order management software to Veristitch. I've learned several things along the way, some of them not well-documented on the Internet, as far as I can tell. Thanks to Sid Kaspar and RKerr on the microsoft.public.vb.general.discussion group for their part in helping me sort this out. I also had to actually consult a book my boss had at the office instead of find some of this online. But I found Dan Appleman's Visual Basic Programmer's Guide to the WIN32 API very helpful and thorough. I would recommend it for a better understanding of Windows, API's in general, and Visual Basic in particular. I don't know how much of what he's written has changed in VB.Net, but for VB6 it's great.
Buy at Amazon!


Using EXPORTS to solve error 453

The C++ project must have a .DEF file added to it with an EXPORTS statement. The best explanation I found on that is at http://www.rgagnon.com/pbdetails/pb-0123.html. This is simply a text file that the linker uses to determine the names that will be visible to your program. Otherwise, the name will be "mangled" or "decorated". You will get error 453 "Cannot Find Entry Point". To see the mangling, download Dependency Walker.

__stdcall solves error 49 (usually)

The C++ project must also be set to use the __stdcall calling convention. Otherwise you will get error 49 "Bad DLL Calling Convention". The issue there is that C++ and VB natively push arguments on the stack in different manner. But when you tell Visual C++ (presumable could do the same w/ Borland or another compiler) to use the __stdcall convention it makes it so your VB program can read it.

Translating into VB

If you've made it to this page, I assume you're familiar with declaring functions for API calls. If not have a look at the MSDN article on Using a DLL Procedure in Your Application

The function declarations you will be reading from the DLL's documentation will be in C++. VB programmers don't necessarily know how to translate those, especially in some of the more complicated situations. MSDN's Converting C Declarations to Visual Basic is helpful in converting the types, but I'll add a few examples and notes on them as well.

void parameter

BOOL ThisAPIstop( void )
This one's straightforward. A void parameter means there is no parameter so this translates to:
Declare Function ThisAPIstop Lib "ThisAPI" () As Boolean

Strings (LPSTR) as parameters

BOOL ThisAPIstart( LPSTR cpIpAddress, LPSTR cpAppName, LPSTR cpUser, LPSTR cpPass, BOOL dialFlag, BOOL dnsFlag) This translates to:
Declare Function ThisAPIstart Lib "ThisAPI" (ByVal cpIpAddress As String, _
    ByVal cpAppName As String, _
    ByVal cpUser As String, _
    ByVal cpPass As String, _
    ByVal dialFlag As Boolean, _
    ByVal dnsFlag As Boolean) As Boolean
Note that all parameters are passed by value, even though the strings are declared as LPSTR -- long pointer to a string. What VB passes is a pointer, and if you pass the pointer by reference you're passing a pointer to a pointer -- not what the C++ DLL is looking for. Sometimes a DLL does want a pointer -- a pass by reference -- but not for strings. MSDN's article Passing Strings to a DLL Procedure goes into some detail on this. It says that you even specify that the string is passed ByVal when it really is to be altered by the DLL procedure. Counterintuitive, but, oh well.

char = Byte, not String!

char ThisAPIrxType(void)
Resist the urge to call that char return value a string! In VB a string of 1 character is indistinguishable from a single character, but not in C. Call this a Byte. When you need to print it or otherwise treat it like a character, use chr$().
Declare Function ThisAPIrxType Lib "ThisAPI" () As Byte

Reading a returned LPSTR in your VB app

Now for the hard part: what to do when the C++ DLL returns an LPSTR? This is the one that got me to read Chapter 15 of Appleman's book (mentioned at the top of this page.) That book gave me the foundation to be able to use the code Karl E. Peterson has posted to copy memory.
LPSTR ThisAPIrxKeyValue (void)
becomes:
Declare Function ThisAPIrxKeyValue Lib "ThisAPI" () As Long
You see, an LPSTR is, indeed, a 32-bit pointer. We don't usually handle pointers explicitly in Visual Basic (if we did some things would be a whole lot easier), but Karl's Hexdump code at his One-Stop Source Shop shows how to handle it. I am not at liberty to reproduce the code here, but you can look at it for yourself on his site. He basicly uses the CopyMemory API call to copy from the pointer to a local buffer of bytes. He then calls StrConv to convert the Byte array into a VB string.


Search this site
1