Using Visual C++ DLLs in a C++Builder Project
来源:互联网 发布:python怎么写爬虫 编辑:程序博客网 时间:2024/04/26 07:22
Using Visual C++ DLLs in a C++Builder Project
It is likely that one day your boss will ask you if you can create a
GUI with C++Builder that
interfaces to an existing 32 bit DLL compiled with Microsoft Visual
C++. Often times, the original
DLL source code won't be available to you, either because the DLL
comes from a third party
vendor, or because the 22 year old intern just deleted the /DLL/SRC
directory from the network.
Given a DLL and a header file, this article shows you how to call the
DLL from your C++Builder
project.
Calling DLL functions from a C++Builder project
The problem with Visual C++ DLLs
Step 1: Identify calling conventions used by the Visual C++ DLL
Step 2: Examine the linker names in the DLL
Step 3: Generate an import library for the Visual C++ DLL
Step 4: Add the import library to your project
Conclusion
Calling DLL functions from a C++Builder project
Calling a DLL that was created with Visual C++ presents C++Builder
programmers with some
unique challenges. Before we attempt to tackle DLLs generated by
Visual C++, it may be beneficial
to review how you call a DLL that was created with C++Builder. A DLL
that was created with
C++Builder presents fewer roadblocks than one that was made by 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 runtime instead of using an import library, but we will
stick to the import library method
for simplicity). To call a DLL function, add the import library to
your C++Builder project by
selecting the Project | Add To Project menu option in 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.
The last step is to add the code that calls the DLL function.
Listings A and B contain source code for a DLL that can serve as a
test DLL. Notice that the test
code implements two different calling conventions (__stdcall and
__cdecl). This is 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 disagreements due to 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 list their calling
convention.
//------------------------------------------
// Listing A: DLL.H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _BUILD_DLL_
#define FUNCTION __declspec(dllexport)
#else
#define FUNCTION __declspec(dllimport)
#endif
FUNCTION int __stdcall StdCallFunction(int Value);
FUNCTION int __cdecl CdeclFunction (int Value);
FUNCTION int UnknownFunction(int Value);
#ifdef __cplusplus
}
#endif
//------------------------------------------
//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;
}
To create a test DLL from Listing A and Listing B, open up C++Builder
and bring up the Object
Repository by selecting the File | New menu option. Select the DLL
icon and click the OK button.
C++Builder responds by creating a new project with a single source
file. That file will contain a
DLL entry point function and some include statments. Now select File
| New Unit. Save the new unit
as DLL.CPP. Cut and paste the text from Listing A and insert it into
the header file DLL.H. Then copy
the code from Listing B and insert it into DLL.CPP. 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 a DLL
from a C++Builder project: the
DLL itself, a header file for function prototypes, and an import
library to link with. Now we need 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 DLL
project to this new project. Select the Project | Add To Project menu
option and add the LIB file 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
the DLL functions from Listing A
and B.
//------------------------------------------
// Listing C: MAINFORM.CPP - DLLTest program
#include
#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);
}
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 several points. For
starters, Borland and Microsoft disagree on file formats for 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 surmountable.
The two products also 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 protoyped during
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:
1- Visual C++ sometimes decorates exported __stdcall functions.
2- Borland C++Builder expects imported __cdecl functions to be
decorated.
So why is this such a big deal? Take disagreement #1 regarding the
__stdcall calling convention.
If you create a DLL with Visual C++ that contains a __stdcall
function called MyFunction(),
Visual C++ will give the function a linker name that looks like
_MyFunction@4. When the Borland
linker tries to resolve calls made to this function, 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 reports an unresolved external, which
means it couldn't find the
function.
Your attack strategy for overcoming these three problems will depend
on how the Visual C++ DLL
was compiled. I have broken the process into four steps.
Step 1: Identify calling conventions used by the Visual C++ DLL
In order to combat the naming convention entanglements, you must
first determine what calling
conventions are used by functions in the DLL. You can determine this
by investigating the header
file for the DLL. The function prototypes in the DLL header should
look something like this:
__declspec(dllimport) void CALLING_CONVENTION MyFunction(int nArg
);
CALLING_CONVENTION should 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 that Visual C++ followed
when it created the DLL.
Visual C++ decorates __stdcall functions by default, but the DLL
programmer can prohibit name
decorations if they add a DEF file to their project. Your work will
be slightly more tedious if the
DLL supplier did not use a DEF file.
The command line TDUMP utility allows you to examine the linker names
of functions exported by the
DLL. The following command invokes TDUMP on a DLL.
TDUMP -ee MYDLL.DLL > MYDLL.LST
TDUMP can report a ton of information about the DLL. We're only
interested in functions exported by
the DLL. The -ee command option instructs TDUMP to list only export
information. If the DLL is
large, you may want to redirect the output of TDUMP to a file (via
the > MYDLL.LST appendage).
The TDUMP output for the test DLL in Listing A and B looks like this:
Turbo Dump Version 5.0.16.4 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
Here comes 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 where 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
---------------------------------------------------------------------
--
__stdcall _MyFunction@4 MyFunction MyFunction
__cdecl MyFunction MyFunction _MyFunction
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 VC++ and C++Builder agree only when the DLL uses
__stdcall functions.
Furthermore, the DLL must be compiled with a DEF file to prevent VC++
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. You can create an import library by
running IMPLIB on the DLL. No
aliases are needed.
IMPLIB works like this:
IMPLIB (destination lib name) (source dll)
For example,
IMPLIB mydll.lib mydll.dll
Create the import library and 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 the DLL vendor
omits a calling convention when prototyping the functions, and
__cdecl forces you into Case 2.
Secondly, even if your vendor has utilized the __stdcall calling
convention, they probably
neglected to utilize a DEF file to strip the Visual C++ decorartions.
However you got here, Good Day, and 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 program 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 (Destination DEF file) (source DLL file).
For example
IMPDEF mydll.def mydll.dll
After running IMPDEF, open the resulting DEF file using the editor of
your choice. When the DLL
source in Listing A and B is compiled with Visual C++, the DEF file
created by IMPDEF looks like
this:
LIBRARY DLL.DLL
EXPORTS
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 in Listing A and B, the
aliased DEF looks like this:
EXPORTS
; 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
Create the import library and move 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
StdCallFunction
_CdeclFunction size = 0
_CdeclFunction
_UnknownFunction size = 0
_UnknownFunction
Step 4: Add the import library to your project
Once you create an import library for the Visual C++ DLL, you can add
the import library to your
C++Builder project using the Project | Add to Project menu option.
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 your project and see if you can successfully
link.
Conclusion:
This article demonstrated how you can call functions in a Visual C++
DLL from a C++Builder
project. The techniques work with C++Builder 1 and C++Builder 3, and
DLLs built with Visual
C++ 4.X or Visual C++ 5 (I haven't tested Visual C++ 6 yet).
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. C++ DLLs 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
methods. 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 will
automatically alias __cdecl functions from the Visual C++ format to
the C++Builder format. The
automatic aliasing can simplify Step 3 if the DLL exclusively uses
the __cdecl calling convention.
Copyright ? 1997-1998 by Harold Howe.
All rights reserved.
- Using Visual C++ DLLs in a C++Builder Project
- Plug-In framework using DLLs
- Visual studio using Zeranoe FFmpeg libs and dlls crashes in release mode.
- What is the best free memory leak detector for a C/C++ program and its plug-in DLLs
- What is the best free memory leak detector for a C/C++ program and its plug-in DLLs
- Using Cookie in C#
- wine/dlls/ddraw/d3dvertexbuffer.c
- [C/C++] Using `getopt' in c/c++
- [转]Finding a query in UCM using the query builder
- Using C code in symbian
- bitonic_sort using mpi in c
- Using LAPACK in C/C++
- Using LAPACK in C/C++
- Using fork() in C/C++ to create a child process(用C创建子进程)
- Using UTF-8 as the internal representation for strings in C and C++ with Visual Studio
- (HTTP) Streaming a video using a program in C/C++ under Linux(轉載)
- C初学者之选Visual C++ 与 C++Builder
- Flash Builder Help / Unable to create a project in Flash Builder's default workspace | Flash Builde
- 为了更明确的生活,需要计划
- 深刻剖析经典面试题之一:堆和栈的区别
- asp多行多列的分页以及form中传递特殊字符的应用(1)
- Linux & Windows
- 写完《Ruby on Rails快速开发简略》的心情
- Using Visual C++ DLLs in a C++Builder Project
- javascript 数组的引用
- Kademlia: 基于异或运算的P2P信息系统
- Linux上构建JSP方案:Apache+JSDK+Tomcat
- ASP.NET 安全认证
- 在ASP.Net中使用FCKeditor
- javascript 最大化最小化窗口
- 三种下载文件的办法
- NAT的完全分析及其UDP穿透的完全解决方案