Using Visual C++ DLLs with C++Builder

by Harold Howe

It is possible that one day your boss will ask you if you can create an application with C++Builder that interfaces to an existing DLL compiled with Microsoft Visual C++. Often, the original DLL source code wonít be available to you; either because the DLL comes from a third party vendor, or because your new intern just deleted the \DLL\SRC directory from the network. This article shows you how to call a DLL built with Visual C++ from your C++Builder project given only a DLL and a header file.


Calling DLL functions from a C++Builder project

Calling a DLL that was created with Visual C++ presents some unique challenges. Before I explain how to use DLLs generated by Visual C++, it may be beneficial to review how you call functions in a DLL that was created with C++Builder. A DLL that was created with C++Builder presents fewer roadblocks than one that was built with Visual C++.

You need to gather three ingredients in order to call a DLL function from your C++Builder program: the DLL itself, a header file with function prototypes, and an import library. (You could load the library at run time instead of using an import library, but we will stick to the import library method for simplicity.) To call a function in a DLL, you first add the import library to your C++Builder project by selecting the Project | Add To Project from the main menu. The import library is created for you automatically when you build a DLL project with C++Builder. Next, insert a #include statement for the DLL header file in the C++ source file that needs to call one of the DLL functions. Finally, add the code that calls the DLL function.

Listings A and B contain source code for a simple DLL that that you can use for testing. Notice that the test code implements two different calling conventions: standard call (represented by the __stdcall keyword) and the C calling convention (__cdecl). I did this for a very good reason: when you try to call a DLL that was compiled with Visual C++, most of your headaches will result from problems that arise due to mismatched calling conventions. Also notice that one function does not explicitly list the calling convention that it uses. This unknown function will act as a measuring stick for DLL functions that donít specifically declare a calling convention.

To build the test DLL, first create a new DLL project from the Object Repository. (Alternatively, you can download the project from the Bridges Publishing Web site.) C++Builder now shows the source for the DLL. At this point, the source contains just a DLL entry point function and some include statements. Create a new unit and save it as DLL.CPP. Enter the code from Listing A and insert it into the DLL.H header. Next switch to DLL.CPP file and enter the code from Listing B. Make sure that the #define for _BUILD_DLL_ is placed above the include statement for DLL.H.

Save the project as BCBDLL.BPR. Next, compile the project and take a look at the files produced. C++Builder generates both a DLL and an import library with a .LIB extension.

At this point, you have the three ingredients needed to call functions in the DLL from a C++Builder project. Next, you need to create a C++Builder project that will try to call the DLL functions. Create a new project in C++Builder and save it to your hard drive. Copy the DLL, the import library, and the DLL.H header file from the DLL projectís folder to the folder containing the test EXE project. Select Project | Add To Project from the C++Builder main menu and add BDCBDLL.LIB to the project. Next, add a #include statement in the main unit that includes DLL.H. Finally, add code that calls the DLL functions. Listing C shows code that calls each of the DLLís functions. You should find that all is well and that the test application can call functions in the DLL without problems.


The problem with Visual C++ DLLs

In an ideal world, calling a DLL created with Visual C++ would be no more difficult than calling a DLL built with C++Builder. Unfortunately, Borland and Microsoft disagree on three primary technical points when it comes to DLLs. First, Borland and Microsoft use different object file formats (this affects objs and import library files). Visual C++ uses the COFF library format while Borland uses OMF. This means that you canít add a Microsoft generated import library to a C++Builder project. Thanks to the Borland IMPLIB utility, the file format differences are easily remedied.

Second, the two products disagree on linker naming conventions. This turns out to be the primary hurdle when trying to call a Visual C++ DLL from C++Builder. Every function in a DLL or OBJ has a linker name. The linker uses the linker name to resolve functions that were prototyped at compile time. The linker will generate an unresolved external error if it canít find a function with a linker name that it thinks is needed by the program.

With regard to linker function names, Borland and Microsoft disagree on these points:



So why is this such a big deal? Take the first point above. Letís say you have a DLL created with Visual C++ and that the DLL contains a __stdcall function called MyFunction(). Visual C++ will give the function a linker name of _MyFunction@4. When the Borland linker tries to resolve calls made to this function, however, it expects to find a function with the name MyFunction. Since the import library for the Visual C++ DLL doesnít contain a function called MyFunction, the Borland linker generates an ďunresolved externalĒ linker error. Basically, the linker couldnít find the function in the import library so it generates the error.

Your strategy for overcoming these problems will depend on how the Visual C++ DLL was compiled. I have broken the process into four steps.


Step 1: Identify the calling conventions

In order to combat the naming convention entanglements, you must first determine what calling conventions are used by functions in the DLL. You can do this by investigating the header file for the DLL. The function prototypes in the DLL header should look something like this:

__declspec(dllimport) void <convention>
  MyFunction(int nArg);

The placeholder convention will usually be __stdcall or __cdecl (see Listing A for concrete examples). In many cases, the calling convention wonít be specified, in which case it defaults to __cdecl.


Step 2: Examine the linker names in the DLL

If Step 1 reveals that the DLL utilizes the __stdcall calling convention, you will need to examine the DLL to determine the naming convention used when the DLL was built. Visual C++ decorates __stdcall functions by default, but the DLL programmer can prohibit name decorations if he or she adds a DEF file to the project. Your work will be slightly more tedious if the DLL supplier did not use a DEF file.

The TDUMP command line utility allows you to examine the linker names of functions exported by the DLL. The following command invokes TDUMP on a DLL.


TDUMP can report a ton of information about the DLL. Weíre only interested in functions exported by the DLL, so the -ee switch is used to instruct TDUMP to list only export information. If the DLL is large, you may want to redirect the output of TDUMP to a text file as shown in the previous example.

The TDUMP output for the test DLL in Listings A and B looks like this:

Turbo Dump  Version Copyright 
(c) 1988, 1998 Borland International

Display of File DLL.DLL

EXPORT ord:0000='CdeclFunction'
EXPORT ord:0002='UnknownFunction'
EXPORT ord:0001='_StdCallFunction@4'

Notice the leading underscore and the trailing @4 on the __stdcall function. The __cdecl and the unknown function donít contain any decorations. If the Visual C++ DLL had been compiled with a DEF file, the decorations on the __stdcall function would not be present.


Step 3: Generate an import library for the Visual C++ DLL

Now we get to the hard part. Due to the library file format differences between C++Builder and Visual C++, you cannot add an import library created with Visual C++ to your C++Builder project. You must create an OMF import library using the command line tools that come with C++Builder. Depending out what you found in the first two steps, this step will either go smoothly, or it could take some time.

As stated earlier, C++Builder and Visual C++ donít agree on how functions should be named in a DLL. Due to naming convention differences, you will need to create an aliased import library if the DLL implements calling conventions in which C++Builder and Visual C++ disagree. Table A lists the areas of disagreement.


Table A: Visual C++ and C++Builder naming conventions


Calling convention

VC++ name

VC++ (DEF used)

C++Builder Name











The C++Builder column lists function names that the Borland linker expects to see. The first Visual C++ column lists the linker names that Visual C++ generates when the Visual C++ project does not utilize a DEF file. The second Visual C++ column contains linker names that Visual C++ creates when a DEF file is used. For things to go smoothly, the C++Builder name should agree with the Visual C++ name. Notice that the two products agree in only one place: __stdcall functions where the Visual C++ project contained a DEF file. For the remaining scenarios, you will need to create an import library that aliases the Visual C++ name to a C++Builder compatible name.

Table A shows that there are several combinations that you may need to deal with when creating the import library. I have separated the combinations into two cases.


Case 1: The DLL contains only __stdcall functions and the DLL vendor utilized a DEF file.

Table A reveals that Visual C++ and C++Builder agree only when the DLL uses __stdcall functions. Furthermore, the DLL must be compiled with a DEF file to prevent Visual C++ from decorating the linker names. The header file will tell you if the __stdcall calling convention was used (Step 1), and TDUMP will reveal whether or not the functions are decorated (Step 2). If the DLL contains __stdcall functions that are not decorated, then Visual C++ and C++Builder agree on how the functions should be named. If that is the case, you can create an import library by simply running IMPLIB on the DLL. No aliases are needed. IMPLIB works like this:

IMPLIB (output lib name) (source dll)

For example:

IMPLIB mydll.lib mydll.dll

Once you have created the import library you can move on to step 4.


Case 2: The DLL contains __cdecl functions or decorated __stdcall functions.

If your DLL vendor is adamant about creating DLLs that are compiler independent, then you have a good chance of falling into the Case 1 category. Unfortunately, odds are you wonít fall into the Case 1 group for several reasons. For one, the calling convention defaults to __cdecl if a calling convention was not specified when the DLL was built. Secondly, even if your vendor has utilized the __stdcall calling convention, they probably neglected to utilize a DEF file to strip the Visual C++ name decorations.

However you got here, welcome to Case 2. Youíre stuck with a DLL whose function names donít agree with C++Builder. Your only way out of this mess is to create an import library that aliases the Visual C++ function names into a format compatible with C++Builder. Fortunately, the C++Builder command line tools allow you to create an aliased import library.

The first step is to create a DEF file from the Visual C++ DLL by using the IMPDEF utility that comes with C++Builder. IMPDEF creates a DEF file that lists all of the functions exported by the DLL. You invoke IMPDEF like this:

IMPDEF (output DEF file) (source DLL)

For example:

IMPDEF mydll.def mydll.dll

After running IMPDEF, open the resulting DEF file using the editor of your choice. When the DLL shown in Listings A and B is compiled with Visual C++, the DEF file created by IMPDEF looks like this:


 CdeclFunction @1
 UnknownFunction @3
 _StdCallFunction@4 =_StdCallFunction @2

The next step is to alter the DEF file so it aliases the DLL functions into names that C++Builder will like. You can alias a function by listing a C++Builder compatible name followed by the original Visual C++ linker name. For the test DLL the aliased DEF looks like this:

 ; use this type of aliasing
 ; (Borland name) = 
    (Name exported by Visual C++)
 _CdeclFunction   = CdeclFunction
 _UnknownFunction = UnknownFunction
 StdCallFunction  = _StdCallFunction@4

Notice that the function names on the left match the Borland compatible names from Table A. The function names on the right are the actual linker names of the functions in the Visual C++ DLL.

The final step is to create an aliased import library from the aliased DEF file. Once again, you rely on the IMPLIB utility, except that this time, you pass IMPLIB the aliased DEF file as its source file instead of the original DLL. The format is:

IMPLIB (dest lib file) (source def file)

For example:

IMPLIB mydll.lib mydll.def


Before going on to Step 4 you may want to examine the import library first to ensure that each DLL function appears in a naming format that C++Builder agrees with. You can use the TLIB utility to inspect the import library.

TLIB mydll.lib, mydll.lst

The list file for the test DLL looks like this:

Publics by module

StdCallFunction size = 0

_CdeclFunction  size = 0

_UnknownFunction size = 0

Step 4: Add the import library to your project

Once you have created an import library for the Visual C++ DLL, you can add the import library to your C++Builder project. You use the import library without regard to whether the import library contains aliases or not. After adding the import library to your project, build the project and see if you can successfully link.



You may have noticed that this article only discusses how to call C style functions in a DLL. No attempt is made to call methods of an object where the code for the class resides in a Visual C++ DLL. DLLs containing C++ classes present an even greater array of problems because linker names for member functions are mangled. The compiler employs a name-mangling scheme in order to support function overloading. Unfortunately, the C++ standard does not specify how a compiler should mangle class method names. Without a strict standard in place, Borland and Microsoft have each developed their own techniques for name mangling, and the two conventions are not compatible. In theory, you could use the same aliasing technique to call member functions of a class that resides in a DLL. However, you may want to consider creating a COM object instead. COM introduces many of its own problems, but it does enforce a standard way of calling methods of an object. A COM object created by Visual C++ can be called from any development environment, including both Delphi and C++Builder.

C++Builder 3.0 introduced a new command line utility called COFFToOMF.EXE. This utility can convert a Visual C++ import library to a C++Builder import library. Furthermore, the program automatically aliases __cdecl functions from the Visual C++ format to the C++Builder format. The automatic aliasing can simplify Step 3 if the DLL uses the __cdecl calling convention exclusively.

Listing A: DLL.H

#ifdef __cplusplus
extern "C" {

#ifdef _BUILD_DLL_
#define FUNCTION __declspec(dllexport)
#define FUNCTION __declspec(dllimport)

FUNCTION int __stdcall StdCallFunction(int Value);
FUNCTION int __cdecl CdeclFunction  (int Value);
FUNCTION int UnknownFunction(int Value);

#ifdef __cplusplus


Listing B: DLL.C

#define _BUILD_DLL_
#include "dll.h"

FUNCTION int __stdcall StdCallFunction(int Value)
  return Value + 1;

FUNCTION int __cdecl   CdeclFunction(int Value)
  return Value + 2;

FUNCTION int UnknownFunction(int Value)
  return Value;


Listing C: MAINFORM.CPP - DLLTest program

#include <vcl\vcl.h>
#pragma hdrstop

#include "MAINFORM.h"
#include "dll.h"

#pragma resource "*.dfm"
TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)

void __fastcall 
  TForm1::Button1Click(TObject *Sender)
  int Value = StrToInt(Edit1->Text);
  int Result= StdCallFunction(Value);
  ResultLabel->Caption = IntToStr(Result);

void __fastcall 
  TForm1::Button2Click(TObject *Sender)
  int Value = StrToInt(Edit1->Text);
  int Result= CdeclFunction(Value);
  ResultLabel->Caption = IntToStr(Result);

void __fastcall 
  TForm1::Button3Click(TObject *Sender)
  int Value = StrToInt(Edit1->Text);
  int Result= UnknownFunction(Value);
  ResultLabel->Caption = IntToStr(Result);