📄 vcdll.htm
字号:
decorates <TT>__stdcall</TT> 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.
</P>
<P>
The command line <TT>TDUMP</TT> utility allows you to examine the linker names of functions exported by the DLL. The following
command invokes <TT>TDUMP</TT> on a DLL.
</P>
<PRE>
TDUMP -ee MYDLL.DLL > MYDLL.LST
</PRE>
<P>
<TT>TDUMP</TT> can report a ton of information about the DLL. We're only interested in functions exported by the DLL. The
<TT>-ee</TT> command option instructs <TT>TDUMP</TT> to list only export information.
If the DLL is large, you may want to redirect the output of <TT>TDUMP</TT> to a file (via the <TT>> MYDLL.LST</TT> appendage).
</P>
<P>
The <TT>TDUMP</TT> output for the test DLL in Listing A and B looks like this:
</P>
<PRE>
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'
</PRE>
<P>
Notice the leading underscore and the trailing <TT>@4</TT> on the <TT>__stdcall</TT> function. The <TT>__cdecl</TT> 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 <TT>__stdcall</TT> function would not be present.
</P>
<BR>
<H3>
<A NAME="step3">Step 3: Generate an import library for the Visual C++ DLL</A>
</H3>
<P>
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.
</P>
<P>
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.
</P>
<PRE>
<B>Table A: Visual C++ and C++Builder naming conventions</B>
Calling convention VC++ name VC++ (DEF used) C++Builder Name
-----------------------------------------------------------------------
__stdcall _MyFunction@4 MyFunction MyFunction
__cdecl MyFunction MyFunction _MyFunction
</PRE>
<BR>
<P>
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: <TT>__stdcall</TT> 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.
</P>
<P>
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.
</P>
<H4>Case 1: The DLL contains only <TT>__stdcall</TT> functions and the DLL vendor utilized a DEF file.</H4>
<P>
Table A reveals that VC++ and C++Builder agree only when the DLL uses <TT>__stdcall</TT> 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
<TT>__stdcall</TT> calling convention was used (Step 1), and <TT>TDUMP</TT> will reveal whether or not the functions
are decorated (Step 2). If the DLL contains <TT>__stdcall</TT> 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 <TT>IMPLIB</TT> on
the DLL. No aliases are needed.
</P>
<P><TT>IMPLIB</TT> works like this:
</P>
<PRE>
IMPLIB (destination lib name) (source dll)
</PRE>
<P> For example,</P>
<PRE>
IMPLIB mydll.lib mydll.dll
</PRE>
Create the import library and move on to step 4.
<H4>Case 2: The DLL contains <TT>__cdecl</TT> functions or decorated <TT>__stdcall</TT> functions.</H4>
<P>
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 <TT>__cdecl</TT> if the DLL vendor omits a calling convention when prototyping the functions,
and <TT>__cdecl</TT> forces you into Case 2. Secondly, even if your vendor has utilized the <TT>__stdcall</TT> calling
convention, they probably neglected to utilize a DEF file to strip the Visual C++ decorartions.
</P>
<P>
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.
</P>
<P>
The first step is to create a DEF file from the Visual C++ DLL by using the <TT>IMPDEF</TT> program that comes with
C++Builder. <TT>IMPDEF</TT> creates a DEF file that lists all of the functions exported by the DLL.
You invoke <TT>IMPDEF</TT> like this:
</P>
<PRE>
IMPDEF (Destination DEF file) (source DLL file).
</PRE>
<P>For example</P>
<PRE>
IMPDEF mydll.def mydll.dll
</PRE>
<P>
After running <TT>IMPDEF</TT>, 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 <TT>IMPDEF</TT> looks like this:
<P>
<PRE>
LIBRARY DLL.DLL
EXPORTS
CdeclFunction @1
UnknownFunction @3
_StdCallFunction@4 =_StdCallFunction @2
</PRE>
<P>
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:
</P>
<PRE>
EXPORTS
; use this type of aliasing
; (Borland name) = (Name exported by Visual C++)
_CdeclFunction = CdeclFunction
_UnknownFunction = UnknownFunction
StdCallFunction = _StdCallFunction@4
</PRE>
<P>
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.
</P>
<P>
The final step is to create an aliased import library from the aliased DEF file. Once again, you rely on the <TT>IMPLIB</TT>
utility, except that this time, you pass <TT>IMPLIB</TT> the aliased DEF file as its source file instead of the original
DLL. The format is
</P>
<PRE>
IMPLIB (dest lib file) (source def file)
</PRE>
<P>For example</P>
<PRE>
IMPLIB mydll.lib mydll.def
</PRE>
<P>
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 <TT>TLIB</TT> utility to
inspect the import library.
</P>
<PRE>
TLIB mydll.lib, mydll.lst
</PRE>
<P>
The list file for the test DLL looks like this:
</P>
<PRE>
Publics by module
StdCallFunction size = 0
StdCallFunction
_CdeclFunction size = 0
_CdeclFunction
_UnknownFunction size = 0
_UnknownFunction
</PRE>
<BR>
<H3>
<A NAME="step4">Step 4: Add the import library to your project</A>
</H3>
<P>
Once you create an import library for the Visual C++ DLL, you can add the import library to your C++Builder project
using the <I>Project | Add to Project</I> 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.
</P>
<BR>
<H3>
<A NAME="conclusion">Conclusion:</A>
</H3>
<P>
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).
</P>
<P>
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.
</P>
<P>
C++Builder 3.0 introduced a new command line utility called <TT>COFFtoOMF.EXE</TT>. This utility can convert a Visual
C++ import library to a C++Builder import library. Furthermore, the program will automatically alias <TT>__cdecl</TT>
functions from the Visual C++ format to the C++Builder format. The automatic aliasing can simplify Step 3 if the DLL
exclusively uses the <TT>__cdecl</TT> calling convention.
</P>
</TD> </TR>
</TABLE>
</CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -