QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3994|回复: 0
打印 上一主题 下一主题

Creating DLLs in BCB that can be used from Visual C++

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2005-1-26 12:51 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
<>In a past article, we discussed how to call a DLL created by MS Visual C++ from a C++Builder project. This article reverses that scenario by illustrating how to create a DLL with C++Builder that you can call from a Visual C++ project. </P>
. L0 z" W5 K, a<>
: R( R; ?  P, tIntroduction: why is this so difficult
/ d+ W0 p& ]+ x6 JSummary of guidelines 5 y1 b% O7 C$ R( k) A
Example 1: Implicit linking
9 B3 A6 y2 ~  ?6 v5 E& |Example 2: Explicit linking ) `5 ^! p" n# k( w0 n; U" B
Example 3: Implicit linking with a #define kludge # I8 C! a( e! t3 _$ k, F' |
Example 4: Implicit linking with stdcall functions 5 R1 ~8 j0 b' B
Conclusion </P>) Q3 K% M% n2 o4 Z* k% L6 T( w7 [
: t& s% O/ \4 ?* }5 ^& F: ^, x
<>Introduction: why is this so difficult
; q- {. ~" i. H, k; a. H  tIf you have ever created a DLL with BCB that you called from another BCB executable, then you know that using DLLs in this manner is not that difficult. When you build a DLL, BCB generates an import library with a .LIB file exension. You take this LIB file and add it to the project for your application. The linker uses the import library to resolve calls into the DLL. When you run your program, the DLL is implicitly loaded for you, and calls into the DLL work without much thought required on your part. </P>
$ B" D, ^2 t; T) Z" S2 k<>The situation becomes more complex when the EXE is compiled with Microsoft Visual C++. There are 3 main problems. First, BCB and MSVC do not agree on how functions should be named in the DLL. BCB uses one convention, and MSVC uses a different convention. Of course, the two conventions are not compatible. The naming problems are discussed in the article on how to use VC++ compiled DLLs in BCB. Table 1 summarizes how each compiler would export a function called MyFunction based on its calling convention. Notice that Borland prepends a leading underscore to exported __cdecl functions. MSVC does not. On the other hand, MSVC does expect __stdcall functions to be exported with a leading underscore and some garbage at the end. </P>4 C' ?/ Y% A$ A( g7 L$ `
<>Table 1: Visual C++ and C++Builder naming conventions</P>
  i0 n6 b" m0 [% b/ j3 y( f<>Calling convention  VC++ name       VC++ (DEF used)     C++Builder Name
" Y7 T" R7 D$ |' }; e-----------------------------------------------------------------------) }1 W, ]0 O2 v6 @% O( O
__stdcall           <a href="mailt_MyFunction@4" target="_blank" >_MyFunction@4</A>   MyFunction          MyFunction- Z. G' j* W' v+ u6 O: K9 z
__cdecl             MyFunction      MyFunction          _MyFunction</P>
# ]7 P; K- U8 |6 t( K% B<>The second problem is that Borland import libraries are not binary compatible with MSVC. The import library that BCB creates when you compile the DLL cannot be linked with MSVC. If you want to use implicit linking, then you need to create an MSVC import library. The other alternative is to switch to explicit linking (LoadLibrary and GetProcAddress). </P>8 {9 |1 a/ l8 h# t2 V* p. _
<>The third problem is that you can't export C++ classes and member functions from your DLL if you want MSVC users to be able to call it. Well, that's not entirely true. Your DLL can export C++ classes and member functions, but MSVC will not be able to use them. The reason is that C++ member function names are mangled by the compiler. This mangled name ends up in the DLL. In order to call a mangled function in a DLL, you have to know how that function was mangled. Borland and Microsoft do not use the same name mangling scheme. As a reasult, MSVC cannot even see C++ classes and member functions in a Borland compiled DLL. </P>) T) ]: {5 N) l4 H
<>  Note:
2 \9 r4 W' Q6 l+ Q, V, O9 J, O/ m--------------------------------------------------------------------------------
! z  ]! @% J6 {6 U" |8 eBorland and Microsoft do not mangle functions the same way because C++ compilers are not supposed to adhere to the same guidelines, according to the ANSI C++ standard. Name mangling is something that is implementation specific. 7 w7 Y% j8 f$ `+ ]
--------------------------------------------------------------------------------5 g- Y5 G2 N8 t# f3 e4 u+ }# u
</P>" C; R: ~7 a4 c5 Y5 S6 w; f& W5 s# T
<>+ ^& M$ ^! h: r  V) m; k0 t
These three issues make it difficult to create a DLL in Borland that can be called from MSVC, but it isn't impossible. This article describes a set of guidelines that you can follow to make your BCB DLLs Microsoft compatible. We will discuss four different techniques. Three involve implicit linking with an import library, and one technique utilizes explicit linking at runtime. </P>
! Z9 v9 H" j. v/ T) h( ?% v<>Summary of guidelines
6 r+ g4 T: ]* C) B$ O$ gThe following lists summarize the guidelines that you should follow for building your DLL. The first list discusses implicit linking. The second list describes explicit lnking. The third technique uses implicit linking and a #define trick. The last example utilizes a dummy MSVC DLL project to create a import library for __stdcall functions. </P>
/ s3 |" b8 B$ I& z<>Technique 1: Implicit linking
: S- P8 a% L2 z) c6 l+ ~0 y. j------------------------------------------------------------------------------9 X. _! E0 F& _% p
1- Use the __cdecl calling convention instead of __stdcall.5 z. L! h3 C/ z! P
2- Export plain "C" functions. No C++ classes or member functions.
/ @" Y0 M4 j) a; k( R) E3- Make sure you have an extern "C" {} around your function prototypes.
1 n" [' `5 B( ?2 ^4- Create a DEF file that aliases the exported functions to a Microsoft  i9 C6 S, Y3 H' u, d
   compatible name. Alias the names so they don't contain a leading
7 g; }; G( P: j$ a   underscore. The DEF file will look like this:</P>
% u+ K0 V3 K7 f. S! }! G. U<>   EXPORTS
  k/ S" Q1 W& _5 D   ; MSVC name    = Borland name
  Z; f, C3 k5 W3 N7 N" v3 g     Foo          = _Foo( j/ Q' f$ P1 B. K* x
     Bar          = _Bar</P>
8 o$ D7 J. I$ L+ d0 i! ~<>5- Add the DEF file to your BCB DLL project and rebuild it.' t; Z1 D/ ?; e/ K- I
6- Copy the DLL and the DLL header file to your MSVC project directory.
, a3 _: b2 [1 k2 j* K# |* N7- Run impdef on the DLL to create a second DEF file for the DLL. This DEF
2 I; v* V' R4 w7 X   file is used to create the import library.! j2 A# E0 @/ B$ ?1 c) E9 w
     &gt; impdef mydll.def mydll.dll
: `6 ?) u6 l$ x, A6 J' C8- Run Microsoft's LIB tool to create a COFF import library from the DEF file
9 ?1 d  D. Z9 S( J/ [   created in the previous step. The format is:4 l3 m% p: |2 L, g( U
     &gt; lib /DEF mydll.def& F1 I/ |& u" q* v- H
9- Add the LIB file created by LIB.EXE to your MSVC project.</P>
! T7 _) B0 I: r* \1 g  f0 I9 @$ v8 `  u6 A( O& z/ i
<>Technique 2: Explicit linking) n5 r7 ]( P% Q! {% V; u7 `& a8 D, f
------------------------------------------------------------------------------; F/ ]  B  }* X! Z% @- p3 y2 t
1- Use either __cdecl or __stdcall. If you use __stdcall, you can skip steps; y4 m$ m( w) T$ ?" h
   4 and 5.: z8 ~* m/ O# E# L* p7 l. @
2- Export plain "C" functions. No C++ classes or member functions.
  w6 d- m; ]& A! Z, M; I" o0 m1 a3- Make sure you have an extern "C" {} around your function prototypes.5 t9 V6 U# U, p
4- If you are using __cdecl, then you may want to strip off the leading
2 L8 ]9 ^( u, u- K& y+ ?   underscore in your exported function names, but you don't have to. You can
+ S0 N" m7 `' n2 u4 e9 L   strip off the unscore by following steps 4 and 5 from Example 1. If you
8 t" `" ?1 R) ?0 t7 ~* ]- S   don't strip off the underscores, users will have to include the underscore, X" ]) J1 {/ b- Q4 n' T6 d
   in the function name when they call GetProcAddress.
, {% M. y. p: a, I5- Copy the DLL to the MSVC project directory.6 }# X# Z8 s* q6 J& x- ~# _' i
6- From the MSVC app, use the API LoadLibrary routine to load the DLL.
1 z  g7 R1 M; j) ^0 Z: Q9 C7- Call the API GetProcAddres function to find functions in the DLL. Store the7 m/ x' X" K, [6 k: z; l9 ?
   result of GetProcAddress in a function pointer. Dereference the function. t- H: ^5 K  w
   pointer when you want to call the function.
8 |1 h1 x& E/ g: l5 X, L8- Call FreeLibrary when you are done using the DLL.</P>( P8 n5 q+ d3 L2 k* S4 u
* g) z3 `- p2 M1 m( ~; b& u" K( M
<>Technique 3: Implicit linking with a #define kludge: W2 L, z. [; d! T1 e# l
------------------------------------------------------------------------------
6 i/ [( R* i0 J1- Use the __cdecl calling convention instead of __stdcall.. C! f$ H7 s- X  W" W- D
2- Export plain "C" functions. No C++ classes or member functions.. B# a& h% k4 `% x% Q
3- Make sure you have an extern "C" {} around your function prototypes.
" M6 {, B6 m9 P0 j' O4- In your DLL header file, create a #define for each exported function name.1 |/ \/ ~; i. S  q  P
   The #define will tell the preprocessor to add a leading underscore to each* S7 o  d% C% g# X# Q, X4 g
   function name. The code checks for _MSC_VER because we only want to do this% |8 A" K7 w. ~' e
   aliasing from MSVC.</P>
8 K$ F' N% q; B, T( @0 E<>     #ifdef _MSC_VER
1 u: t# O" g8 t8 Z, u) u     #define Foo _Foo
* P, T( E; [2 Q' Q     #define Bar _Bar
7 T. t" h2 n9 ?, j7 b9 o! x     #endif</P>
. s  y; C! U2 Y' ~' S<>5- Copy the DLL and the DLL header file to your MSVC project directory.
- `& g- L6 ^; \6- Run impdef on the DLL to create a DEF file for the DLL.- J' D2 p- `8 ^- ^1 t4 H7 d( e
     &gt; impdef mydll.def mydll.dll6 j$ n6 f0 m9 j4 I& Z8 }
7- Use Microsoft's LIB tool to create a COFF import library from the DEF file.
/ O' x6 B8 e3 p# p. |3 c     &gt;lib /def mydll.def
# Y2 T9 Z6 Z+ Y" z$ [9 |8- Add the LIB file created by LIB.EXE to your MSVC project.</P>
: [% u5 M2 Y) S% R3 S1 C; z* f. N% H2 J1 T: q
<>Technique 4: Implicit linking with __stdcall functions/ F& j! N/ z$ W
------------------------------------------------------------------------------
* |) s- L, ?& P5 `9 E1- Use the __stdcall calling convention when building your DLL./ j  J$ J( k  [$ [
2- Export plain "C" functions. No C++ classes or member functions.
& @4 c9 R0 }% O9 c2 A9 L3- Make sure you have an extern "C" {} around your function prototypes.
1 s, I. L% c2 {% v4- Create an import library for MSVC. This is the difficult part. You cannot+ q+ R, Y$ E% {' C* W
   create an import library for __stdcall functions with LIB.EXE. You must
/ Y6 z8 Y1 p9 g! `7 x   create the import library by compiling a dummy DLL in MSVC. To do so,
2 [) f6 @$ U- ~; |! w! \0 j) R' L   follow these steps:' q) z, w. W+ D' A. }
     4a- Create a non-MFC DLL project with MSVC
/ t- ^: o, D& `2 Y, f& L     4b- Copy over your DLL header file and DLL source code from BCB: b* K7 d* V  O( V
     4c- Edit your DLL source code and rip out the function bodies of each
1 c8 B: C# L5 @7 y4 f         routine. Use dummy return statements in routines that return values.# a0 w* p! L# T9 z+ D8 T9 f
     4d- Configure the MSVC project to generate a DLL with the same file name
( U0 t" u: {; A/ K1 K         as the BCB DLL.
* v* {4 n  o1 U0 @* C     4e- Add a DEF file to the MSVC project to suppress its hoaky __stdcall
, n7 E9 P2 H6 t' y, a* j7 w1 m# h( R! ^         name decorations (<a href="mailt_Foo@4" target="_blank" >_Foo@4</A>)
; W  P5 d0 W! \5- Compile the dummy DLL project from step 4. This will generate a DLL (which, e* Y5 Q3 ^& q% U5 j( l
   you can pitch in the trash) and a LIB file (which you need).3 K: x1 |: w+ j9 Q& r8 z/ _
6- Add the LIB file from step 5 to any MSVC project the needs to call the BCB2 |& G! L' H* L; ?( T5 y! W  ?
   DLL. The LIB file will satisfy the linker. Deploy the MSVC executable with
- g- ?6 W" g6 _+ H9 E6 K   the BCB DLL (not the dummy DLL).</P>7 e& k5 w- n" r: H; {
3 ?1 c& g( S4 J. i9 q( _) Z* Z! t
<>  Note: 4 X  h6 s5 {0 w3 T) p
--------------------------------------------------------------------------------
( q0 Y4 J! K3 q$ o" [Under normal circumstances, implicit linking is preferred over explicit linking simply because implicit linking is easier for the programmer and because it is more typesafe (errors are generated at link time as opposed to runtime). However, when you share a DLL across compilers, you must create compatible import libraries for each compiler if you choose to stick with implicit linking. The added burden of creating compatible import libraries makes explicit linking at runtime look more appealling. * `+ M; I8 }3 R, v
--------------------------------------------------------------------------------
  J. N& U6 M1 m- ~( w" ^; y </P>) U3 E# s8 h- E
<>  Q! B+ q$ }5 S; m: ^
  Note: + W# D" V9 S  C. i& ~
--------------------------------------------------------------------------------
, s' ^! T" r* X; [The guidelines for explicit linking also apply if you want to make your DLL available to Visual Basic developers. If you want to give your DLL to VB developers, follow the explicit linking guidelines, and use the __stdcall calling convention. 5 f6 \9 x# P4 u
--------------------------------------------------------------------------------( w1 n0 H5 O# z' }
</P>
- Q8 W5 I. c% C0 H<>
5 k; f) u/ s3 FThe next 4 sections describe each technique in detail. </P>9 m  ^) F/ \& R7 t
<>Example 1: Implicit linking ) W; L% I, G/ V$ G
This example elaborates on the guidelines in Technique 1 from the previous section. The guidelines for Technique 1 can be split into two groups. Items 1-5 deal with compiling the DLL on the BCB side. Items 6-9 deal with using the DLL from the MSVC side. We will separate our discussion along these lines. </P>
7 C: M/ \& m$ J5 V<>In this example, we will build a DLL with BCB that exports two functions: Foo and Bar. Both functions return an integer value. The functions look like this:
3 {0 B: |1 L6 ]- M1 Sint Foo (int Value);
; H+ M, ~; \, T( B6 ]int Bar (void);</P>* ^9 @  Z7 I3 ^+ A% n: W/ |
<>We will then build a test EXE in MSVC that calls the Borland DLL. 5 {. [3 {& d. o8 @% b
Compiling the DLL with BCB' K2 P8 F  \. k2 I& O
The following two listings contain the source code for our DLL. Listing 1 is a header file that will be shared by both BCB and MSVC. Listing 2 contains the implementations of our DLL functions. Create a BCB DLL project and paste in the the code from listings 1 and 2. Or you can save time by dowloading the source code for this article. The BCB DLL project is already already set up for you. (see the Downloads section at the very bottom). # o2 v2 e3 \! W
// ----------------------------------------------( [: Z2 s$ [: X
// Listing 1- DLL header file
( m, y: z( E( v8 Z- F7 Y$ O  ]#ifndef BCBDLL_H% N6 T6 ~9 f2 O  o
#define BCBDLL_H</P>
) x4 Q4 H9 A' x: Z$ S" D2 Y<>#ifdef __cplusplus
; l8 T, S9 A0 K' Lextern "C" {
/ E3 i' R) V4 T" V/ n" a0 _( |7 c5 q#endif</P>
0 x5 J: |$ N3 t5 b. o<>#ifdef BUILD_DLL1 r" E$ T2 ^1 Y
#define IMPORT_EXPORT __declspec(dllexport)2 i2 J2 }7 w% D8 h# ^
#else
2 t" y7 K( d% G$ F- |3 K- p% S#define IMPORT_EXPORT __declspec(dllimport)
$ r+ ^' u1 w; O; H0 N#endif</P>
7 x8 H6 r; o+ e0 S2 e<>IMPORT_EXPORT int __cdecl  Foo  (int Value);
8 c9 j7 S2 b5 i+ b4 t$ nIMPORT_EXPORT int __cdecl  Bar  (void);</P>
. [5 p( `' L, `" t<>#ifdef __cplusplus* \2 C* y7 O3 i  x1 z* b5 W
}" M& l3 i& O! T! A* f! \/ H# w
#endif</P>
. k7 }6 b- f1 b8 E1 n<>#endif
: Q9 C1 ^$ U" g; z// ----------------------------------------------</P>$ B( x' G; U- Z% X9 ^* X, I, {
<P>// ----------------------------------------------
: A5 i# G( k- y/ o5 V// Listing 2- DLL source code
1 t+ z8 Z$ J8 M7 n) X* {: W#include &lt;windows.h&gt;# p7 S8 d& F/ E2 w/ G3 ]
#pragma hdrstop</P>
, e# Q. p5 F% D3 z1 U0 b  {<P>#define BUILD_DLL
) f! D! u1 ^: Q; @# R#include "bcbdll.h"</P>) Z. e3 j4 Q7 y2 {
<P>int __cdecl  Foo  (int Value)+ R' ]+ c- N( [- b! n. V" {& t
{# U0 J  }. }; ]* p
    return Value + 1;( ]0 A" Q; m7 V: s8 U
}</P>0 s( I& d& C+ ^' @8 o
<P>int __cdecl  Bar  (void)- G( m% u: k( o7 Y7 k
{6 o1 L' [1 v) R
    static int ret = 0;
5 o& T0 |2 |2 ~) z& J    return ret++;
9 z. S% |3 _! o+ ~- m) n- T}2 g7 H% z" A4 c
// ----------------------------------------------</P>
0 k3 B4 ]+ p' H  Y<P>There are couple of points to notice about the header file. First, observe how we use extern "C" to ensure that the function names are not mangled by the C++ compiler. Second, notice that the exported functions are prefixed by the special directive __declspec(dllexport) when we build the DLL. When we use the DLL from MSVC, the prefix changes to __declspec(dllimport). The directive is controlled via the IMPORT_EXPORT #define. </P>' n( q% L- s* O7 `6 t
<P>Lastly, note that we explicitly declare __cdecl to be the calling convention. Technically, we could omit the __cdecl keyword, since __cdecl is already the default. However, I think it is a good practice to list it anyway. By listing the calling convention, you explicitly tell people that you chose __cdecl for a reason. Also, the default calling convention in both compilers can be changed via compiler switches. You don't want these compiler switches to interfere with the usability of your DLL. </P>
3 w$ T# M2 S- w2 o5 ?$ L7 R# T<P>The header file alone satisfies Items 1-3 in our list of guidelines. The next thing we need to do is handle item #4: aliasing the exported functions names. </P>
8 N* }4 {- {9 o9 d<P>First, build the DLL code as it stands now. Next, run the TDUMP tool to verify to yourself that the exported function names really do contain a leading underscore. </P>. K6 \# u" \; h# d" ?
<P>c:&gt; tdump -m -ee bcbdll.dll/ l! k/ Q. J1 }& ?1 g
Turbo Dump  Version 5.0.16.12 Copyright (c) 1988, 2000 Inprise Corporation# j' k! p0 Z# |' W$ c- t
                    Display of File BCBDLL.DLL</P>
" d& O" ]& Z9 I, J" f6 b8 t8 Y<P>EXPORT ord:0001='_Bar'4 X$ ]+ s5 A2 P; Y' l& B
EXPORT ord:0002='_Foo'- G  j5 }" l( f
EXPORT ord:0003='___CPPdebugHook'</P>
/ j6 d+ ~/ Q1 q7 Q* H4 G<P>  Note: * s8 A6 q4 |* N* z% K$ b8 E, D
--------------------------------------------------------------------------------0 e6 T- W& m  R2 I* J( O: X9 n( \
Don't forget to use the -m switch with TDUMP. TDUMP attempts to unmangle decorated names so they are easier to read. However, when you are working with DLLs, it is wise to see the functions in their raw format. The -m switch tells TDUMP to display raw function names.
& _' x0 \# l0 g! Y6 A--------------------------------------------------------------------------------* I' P0 w' [0 W8 u& B& u4 F7 Z
</P>% d5 z$ D+ L" ?
<P>! J6 ~0 [9 [' |6 a: D# Y- [" ]/ i/ E
As you can see, both Foo and Bar contain a leading underscore. As for that __CPPdebugHook stuff, you can simply ignore that man behind the curtain. Pretend __CPPdebugHook doesn't exist. It doesn't mean much to you, and you can't make it go away, so there isn't much point worrying about it. </P>* f, d" n' _5 Z5 K0 t' ~3 w
<P>In order to alias away the underscores, we need to do three things: First create a DEF file from the DLL. Next, tweak the DEF file to alias from Borland names to MSVC names. Lastly, add this DEF file to your BCB project and rebuild the DLL. </P>4 ^3 c# J4 x- u% f1 ?
<P>To create the DEF file, run Borland's IMPDEF tool on the DLL. </P>
+ `5 m; a: @! k# I' @9 k<P>C:&gt; impdef bcbdllx.def bcbdll.dll</P>2 l$ I! [5 j# B) v- @
<P>I chose to name the DEF file bcbdllx.def because we will make another DEF later on (just before we create the MSVC import library). I wanted to avoid confusion between the two. bcbdllx.def should look like this: </P>
, c- }; l0 }6 Z<P>LIBRARY     BCBDLL.DLL</P>
) Y$ k5 ^7 B! G% o$ C- Z* q. G<P>EXPORTS
3 \$ n( D! u6 U    _Bar                           @1   ; _Bar. `, e0 |& Q. O4 [, ]
    _Foo                           @2   ; _Foo  _" g- R+ ^! W* H" N4 x
    ___CPPdebugHook                @3   ; ___CPPdebugHook</P>4 @+ L2 K2 l+ k* w6 v: Z5 F
<P>Notice the leading underbar in front of Foo and Bar. If the DLL exports Foo and Bar as _Foo and _Bar, MSVC users will see linker errors when they try to build their projects. We need to strip the underbars away. We do this by aliasing the function names in the DEF file. </P>% u% g' P# [( ~2 |6 E
<P>DEF file aliasing allows us to export a function name that acts as a proxy, or placeholder, for the real function. The real functions in the DLL will still be _Foo and _Bar. The proxy names will be Foo and Bar (note the missing underscores). When we alias the two original functions, the DLL will export two new symbols that refer back to the originals. </P>
! `( l; R9 p8 E) G. R9 J& w<P>To perform the alias, edit the DEF file and change it so it looks like this: </P>& D2 p/ L, N$ ~3 V
<P>LIBRARY     BCBDLL.DLL</P>
% b  I9 C/ [9 B- c7 W; [6 e<P>EXPORTS
  q5 J8 k5 R0 v# X" y) q* Q    Bar = _Bar
1 D0 D8 d9 }3 ]* \    Foo = _Foo</P>. I& J1 P3 P0 r  U
<P>This DEF file creates two new exports, Foo and Bar, that act as placeholders for _Foo and _Bar respectively. Save this DEF file back to your hard drive. Once you have done that, add the DEF file to your BCB project using the Project-Add To Project menu item. BCB will display the DEF file in the project manager tree after you add it. </P>
# G3 }& n" e# V/ ?  X: X<P>Once you have the DEF file added to your project, do a complete rebuild. After the project has linked, run TDUMP on the DLL again to verify that the undecorated functions are being exported from the DLL. </P>4 H4 I& e$ D2 \$ I2 @' z: I5 B
<P>&gt;tdump -m -ee bcbdll.dll
* J, \. I, \0 ?Turbo Dump  Version 5.0.16.12 Copyright (c) 1988, 2000 Inprise Corporation, |1 H  p& h6 @( L, [0 B1 f' ^+ t
                    Display of File BCBDLL.DLL</P>: v# l2 ~! N; X* t1 a
<P>EXPORT ord:0004='Bar'9 x" ]) a' i" `$ O6 p& t& W
EXPORT ord:0005='Foo'5 c/ W. R; k/ q
EXPORT ord:0002='_Bar'% s+ o* ?: n, l7 I' N) T
EXPORT ord:0001='_Foo'
2 j! {  M' O; LEXPORT ord:0003='___CPPdebugHook'</P>( K  y* l$ V( g  F: Z) S
<P>There are a couple things to notice about the output from TDUMP. First, obsverve the presence of Foo and Bar (without the leading underscore). Now the DLL is exporting function names that agree with MSVC. Also notice that the original functions, _Foo and _Bar, are still there. The decorated functions are still exported from the DLL. Using a DEF file to alias the names does not hide the originals. </P>
; @% j, N# S/ B4 q3 o/ e: X! y; |<P>You might think that it would be advantageous to somehow hide the original two functions, _Foo and _Bar. However, this would be detrimental to people who want to use your DLL from a BCB project. Remember that the BCB linker expects the leading underscore to be there. If you were to somehow hide _Foo and _Bar from the DLL (which is not possible to the best of my knowledge), then your DLL would be difficult to call from BCB. </P>1 X7 a: B! j( U6 G: `! c9 H
<P>If the output from TDUMP does not list the proxy functions (the ones without the underscore), then go back and check your DEF file. You need to get the alias names to appear before you can proceed. If the DLL looks OK, then it is time to move over to the MSVC side. </P>% p# A5 s0 Y$ }: H/ O$ s. ~
<P>
2 j- K- K: o/ j) s& \  UCalling the DLL from MSVC
+ @( F) Q- }) X6 J9 @8 j/ fOnce you have the DLL molded into shape so that it exports unmangled, __cdecl functions, the next step is to generate an import library for your MSVC users. For this, you will need the DLL that you just created, Borland's IMPDEF utility (again), and the LIB.EXE tool from MSVC. The first step is to create a DEF file from the DLL. For this, I suggest that you copy the DLL and the DLL header file over to your MSVC project directory, and work from there. </P>
6 x  z& f: O8 v3 t<P>C:&gt; impdef bcbdll.def  bcbdll.dll</P>
9 W: O) |) S& a% \: z, ]* V2 Q: v! `<P>Impef will create a DEF file that looks like this: </P>
9 W7 W7 w, |: e, {9 {<P>C:&gt; impdef bcbdll.def  bcbdll.dll</P>, e6 l3 C/ y: G& @- D
<P>LIBRARY     BCBDLL.DLL</P>
5 b1 `2 g+ S2 Z' F# O<P>EXPORTS
9 v( y! W) j" d( }& a' N( o; f3 {    Bar                            @4   ; Bar0 Z; p  ?; m" h0 O" w
    Foo                            @5   ; Foo6 {8 B: A1 P3 z# j* Y
    _Bar                           @2   ; _Bar
, k  u( |5 n( l) F3 W' g) Q+ s* R    _Foo                           @1   ; _Foo
( h% C( s; A* e    ___CPPdebugHook                @3   ; ___CPPdebugHook</P>8 q7 j5 Y1 f# f+ l
<P>Open the DEF file in an editor, and alter it so it looks like this: </P>- J  W! G& {. r6 Y, m- r1 O
<P>LIBRARY     BCBDLL.DLL</P>6 ^; C: G9 {& d4 O( a" w# e
<P>IMPORTS
( ]  m) Z! `) ]3 w    Bar                            @4   ; Bar4 I* Y# Y2 S) v" |1 i
    Foo                            @5   ; Foo</P>
% d) {" g: V- a# X- l; ^$ D<P>Notice that we removed the functions that contain underscores, and the debug hook function. We also changed the word EXPORTS to IMPORTS because we are importing the functions now, rather than exporting them (I doubt that this makes a difference to the MSVC LIB.EXE tool). </P>
, q" @; o, H7 O0 g- Y- i* @<P>Next, we create a COFF library from this DEF file using the Microsoft LIB.EXE tool. The syntax is: </P>+ {: F4 h. I' l2 a% }
<P>lib /DEF:bcbdll.def /out:bcbdll_msvc.lib</P>
" t3 B! U+ z% q5 g; q2 q$ E0 i<P>  Note:
" c9 Q/ W3 p8 ^9 P7 ]" m--------------------------------------------------------------------------------
& n) f) R" d4 Y- q) F; rThe MSVC command line utilities are not, by default, configured to be in your path. You may need to run a batch file the comes with MSVC in order to make LIB.EXE visible. The batch file is called VCVARS32.BAT, and it is located in the \VC\BIN subdirectory of the DevStudio installation directory.
, M0 H/ J$ D+ @& |--------------------------------------------------------------------------------) ~) N5 a( `0 o1 ]7 P% }
</P>4 ^- m7 ^0 d0 ?, l+ S2 d
<P>
8 F! J/ B% g2 C  ?At this point, all of the hard work is done. Now all you need to to is give your DLL, the MSVC LIB file, and the DLL header file to your MSVC clients. To use the DLL, they need to add the LIB file to their MSVC project and #include the DLL header file from there source code. </P>
$ I' e; S2 A+ i/ L6 ]3 c4 b<P>I have a provided MSVC sample projects that prove the concept. Listing 3 shows the source code for the DLL client code. There is nothing exciting here, just a main function, a #include for the DLL header, and the DLL calls themselves. The important thing is that you add the correct import library, the one generated by LIB.EXE, to your MSVC project. </P>% n8 J, P" C, x3 Y
<P>// ----------------------------------------------
8 q; x! U+ ~$ J% \: ]- X// Listing 3- MSVC DLL client code* L7 @" A3 p% d! H: s) v2 r7 d" D) @
#include &lt;iostream&gt;
) _4 z, a' i4 C9 C6 v% q#include &lt;windows.h&gt;
* \. L# M1 v0 U$ v# Husing namespace std;</P>
% n- }$ x3 ?4 W<P>#include "bcbdll.h"</P>
  R# n  A+ r( V/ h6 ~# z/ j; I9 {- @6 g7 d<P>int main()& a8 d2 K4 z3 L& Z" S
{
6 l! \- t& ?7 Z# |* ]0 p    cout &lt;&lt; "Foo(10) = " &lt;&lt; Foo(10) &lt;&lt; endl;' l# N$ t# |0 r  j5 \, j
    cout &lt;&lt; "Bar()   = " &lt;&lt; Bar() &lt;&lt; endl;
1 I& w8 v. s1 O9 l; U0 N    cout &lt;&lt; "Bar()   = " &lt;&lt; Bar() &lt;&lt; endl;8 {1 J/ ?0 s( s; H$ G: E) p4 u
    cout &lt;&lt; "Bar()   = " &lt;&lt; Bar() &lt;&lt; endl;
4 H: y6 M8 p. W% \    return 0;
5 `, x  Z, ~, n  ^# ~+ k. K}
" p; l4 m% U; T( ~// ----------------------------------------------</P>" T; p0 z5 e) V% T5 U+ {
<P>Example 2: Explicit linking
  ]- }9 P) i0 A: L- nThis example shows how you can use explicit linking to call a BCB compiled DLL from MSVC. With explicit linking, you don't have to mess with creating an MSVC compatible import library. The disadvantages to explicit linking are that it requires more work on the users part, it is less typesafe than implicit linking with an import library, and errors are deferred until runtime instead of compile or link time. Even with the many disadvantages to explicit linking, it can still be quite useful in certain situations. </P>
- Z) t9 o- Z) j0 M5 [0 q<P>In this example, we will create a DLL that exports two functions: Foo and Bar. The functions look like the same two functions from the previous example. </P>$ M4 J2 b6 R5 {; ?! a
<P>int Foo (int Value);
1 {: G- |% m( @9 {; W9 Q$ N/ Pint Bar (void);</P>
3 Z( u. L2 a& R1 A  {<P>The guidelines for explicit linking are similar to the guidelines for implicit linking. We need to export plain C functions, and we need to prevent C++ name mangling. If we use the __cdecl calling convention, then we may also want to alias our function names to remove the leading underscore that BCB puts in front of exported functions. If we choose not to alias away the leading underscore, then we will have to include the underscore when loading the function by name. In other words, you have to deal with the underscore at some point when working with __cdecl functions. You can either deal with underscore when you build your DLL with BCB, or you can deal with it when you call the DLL at runtime. We can circumvent the entire underscore issue altogether by utilizing __stdcall instead of __cdecl. This is what we will do in this example. Listings 4 and 5 show the source code for our DLL. </P># ?5 [. L4 l; f: O. b$ J- G
<P>  Note:
8 Q$ X% {' v& G; L3 d2 U--------------------------------------------------------------------------------% y2 A7 S0 {8 N8 J! _. B
If you export __stdcall functions, it is crucial that client apps know this. Some people make the mistake of thinking that using __stdcall does nothing more than strip off the leading underscore that __cdecl functions have. Don't fall into this trap. __stdcall functions manage the stack differently. Bad things will happen if a client app calls treats a __stdcall function as if it were a __cdecl function (namely, the stack will get corrupted and the client app will die a horrible death). + J$ n- ~  j/ _: W/ T
--------------------------------------------------------------------------------; H/ U0 x6 ]2 O' g# _7 V6 g) H
</P>
9 A! V7 J3 f4 e  j/ M0 D# C<P>0 t0 X" l2 y! K  T; H/ C
// ----------------------------------------------7 h; e& S7 e8 r+ h% n9 j9 `
// Listing 4- DLL header file" V4 m+ F2 g. \/ J$ z
#ifndef BCBDLL_H
8 f- ]8 s5 G. N/ E' E" u4 J#define BCBDLL_H</P>! D) `0 y$ ]7 J5 ?
<P>#ifdef __cplusplus
1 @9 x7 I3 S: f0 ^extern "C" {
% k) ?1 Y! z* F" T( m4 L#endif</P>" V9 x( P. M# m
<P>#ifdef BUILD_DLL, ]- B- @# a3 l" b
#define IMPORT_EXPORT __declspec(dllexport)% k' q% f/ f& \7 F  R- U+ Z) w
#else
+ t* @( [* Z/ U8 p0 G6 E#define IMPORT_EXPORT __declspec(dllimport)+ o( m) u" @0 ?$ H0 c
#endif</P>
0 d+ F/ `& Z; b% Q<P>IMPORT_EXPORT int __stdcall Foo  (int Value);3 l, A8 b2 p5 D2 M! K
IMPORT_EXPORT int __stdcall Bar  (void);</P>
( [3 T0 C) ]8 S<P>#ifdef __cplusplus
( J) Q/ l) Y; r: C: i) b5 Z% V. A- z}
) Z- m/ y9 o8 v9 o1 {5 U#endif</P>
" ?1 E% ^) `/ {8 D. S& ?4 U<P>#endif; N8 o6 |/ @1 Z
// ----------------------------------------------</P>- a) Z: X9 s7 g! ~% y
<P>// ----------------------------------------------! A2 Y/ Q! R- D7 O
// Listing 5- DLL source code3 H5 _8 H* S) f7 ?# t! V+ }6 o* g
#include &lt;windows.h&gt;</P>9 n2 c3 W$ j9 J* a
<P>#define BUILD_DLL: {/ Y3 i* O6 e0 Y2 p
#include "bcbdll.h"</P>
9 ^; w% k/ n5 V* x<P>int __stdcall  Foo  (int Value)
1 ~/ |) g: O' S8 Z0 {8 m5 W{4 S( Y( H& H9 |# S# O: g. u
    return Value + 1;1 [; Z; n1 k/ h6 H! |6 d
}</P>  n& T9 g6 o! @$ m
<P>int __stdcall Bar  (void)
! X! x( b3 z6 g. y# l{# ~! e* A' w* o$ o; x
    static int ret = 0;) \& V7 _2 J. R4 U1 U
    return ret++;
8 V6 p; [# E8 t8 w: H}
5 Z- K& z/ s+ f. o3 \4 ^// ----------------------------------------------</P>
! u: m) r9 w- n/ r$ w  e+ }<P>Note that this code is pretty much the same as the DLL code from the implicit linking example. The only difference is that we changed Foo and Bar to use the __stdcall calling convention instead of __cdecl. </P>  ?, ^2 y; c3 Y1 h; {6 _, D
<P>Now lets look at the code for the MSVC program that calls this DLL. The code is shown in Listing 6. </P>. X7 R. j* _% I( E* F8 J
<P>// ----------------------------------------------- m/ o& x' W. ~9 E' I9 j
// Listing 6- MSVC client code
8 _8 C/ T' e' b) W$ H8 X% `& @#include &lt;iostream&gt;- Q/ }: h( I+ {+ ~8 c* D
#include &lt;windows.h&gt;
$ r4 B# S5 g4 e) O6 [using namespace std;</P>
- E( N* b5 k1 @7 P) B6 m<P>HINSTANCE hDll = 0;
  Y: f: O" D; K: ^typedef int (__stdcall *foo_type)  (int Value);0 S0 j! `/ m2 @& t$ M) {2 G2 o
typedef int (__stdcall *bar_type)  ();8 i# @5 d- B. I! f
foo_type Foo=0;8 c3 ^, b! ~: a
bar_type Bar=0;</P>
8 ?1 n; e; N) M" F3 C: C) W<P>void DLLInit(): p+ `4 b' o6 R/ o( N' }
{5 ]: z% P3 S+ S1 X4 g
    hDll = LoadLibrary("bcbdll.dll");
: x% J( r- x) N; B# E5 J+ w$ O    Foo = (foo_type)GetProcAddress(hDll, "Foo");% d8 P# E8 d5 u
    Bar = (bar_type)GetProcAddress(hDll, "Bar");/ R% T2 a+ ?4 F2 U
}</P>3 Z5 p1 Y9 N) C! r* Z: F
<P>void DLLFree()) N1 S; {2 d/ [5 y% d% b  P1 u
{
" r- g9 h& K, E3 _, {    FreeLibrary(hDll);8 x6 i" m( ^) ~6 V. r
}</P>
6 R' U7 _5 S- W" Q) R<P>int main()5 @: W* S; j! ~7 K
{! `9 C! C* a; y  W4 n2 L  d
    DLLInit();</P>/ L  g4 O' D( m4 n( i, d" e
<P>    cout &lt;&lt; "Foo() = " &lt;&lt; Foo(10) &lt;&lt; endl;
7 }2 u' c! X* z7 p" `. j  [    cout &lt;&lt; "Bar() = " &lt;&lt; Bar() &lt;&lt; endl;& o% g' |5 V' T, E7 W
    cout &lt;&lt; "Bar() = " &lt;&lt; Bar() &lt;&lt; endl;- b( |, u! L& u6 J: A8 [
    cout &lt;&lt; "Bar() = " &lt;&lt; Bar() &lt;&lt; endl;- f' K" k* Q6 c  w5 o1 A" `4 j: {
    DLLFree();9 ?# @* B7 A* S+ ]8 z0 P. P
    return 0;
/ u& j* |( ~2 S% J3 ~! k2 [& C}2 J8 z7 D1 H4 k# I: U6 p
// ----------------------------------------------</P>5 S; g6 K! ~7 B( m. w
<P>There is a lot to digest in this code snippet. First and foremost, observe that the code itself is compiler independent. You can compile it with BCB or MSVC. I compiled it first with BCB to make sure that it worked as I had hoped. </P>
9 a4 G; |4 H5 p. [<P>Secondly, notice that the code does not bother to #include bcbdll.h. There is an important reason for this. bcbdll.h declares prototypes for the Foo and Bar functions. However, we are not linking our code with anything that will provide a definition for those prototypes. Normally, the stubs for those prototypes would come from an import library. But this example demonstrates explicit linking, and you don't use import libraries when you explicitly link. Since we don't have an import library, the Foo and Bar prototypes in the header file won't mean much to us. </P>- \  m. X5 Z+ z$ h
<P>The third thing to notice about the code is the presence of the typedef's and function pointers located near the top of the source file. Explicit linking requires that you load the addresses of the DLL functions at runtime using the API GetProcAddress function. You must store the result of GetProcAddress in some location. The best place is to store the result in a function pointer. By storing the function address in a function pointer, you can call the function using normal function call syntax (ie Foo(10)). </P>
* T1 t4 V6 e* t1 H" I. j<P>The typedef statements create two new types: foo_type and bar_type. These types are function pointer types. foo_type declares a type that is a pointer to a __stdcall function that takes one integer argument and returns an integer value. bar_type declares a type that is a pointer to a __stdcall function with no arguments and an integer return value. These typedef's serve two purposes. First, they provide a clean way to declare the function pointer variables Foo and Bar. Secondly, they give us a convenient way to cast the result of GetProcAddress. The return value from GetProcAddress is a pointer to a __stdcall function with no arguments and an integer result. Unless your function follows this same format, you will need to perform a cast on the GetProcAddress result (this casting is why explicit linking is less type safe than implicit linking). </P>; o) E2 c/ c' H+ N& [' K# J
<P>Below the typedef's are two variables called Foo and Bar. These variables are function pointer variables. They will hold the addresses of the two functions that we want to call. Note that the names of these variables are arbitrary. I chose Foo and Bar to make the code resemble the implicit linking example. Don't be tripped up by this. The Foo and Bar variable names have no connection with the names of the actual functions in the DLL. We could have named the variables Guido and Bjarne if we wanted to.</P># h% }. {) z4 h. e( }6 C
<P>Below the function pointer declarations you will see two routines called DllInit and DllFree. These two routines handle the task of loading the DLL, finding the exported functions, and freeing the library when we are done with it. This way, the rest of the code doesn't have to know that the DLL was explicitly linked. It can just call Foo and Bar like it normally would (or Guido and Bjarne if you change the names). The only hitch is that we must call DllInit before calling any DLL routines. We should also be nice and call DllFree to release the library. </P>
8 f) b- t( ?- l) W<P>  Note:
3 z8 Q' p. ^* Q& ]$ W* ~2 k; j--------------------------------------------------------------------------------
/ T8 P$ T) o+ C' e: a2 IGetProcAddress is your last line of defense when fighting naming issues between the Borland compiler and the Microsoft compiler. You can pass any string that you like to GetProcAddress. This includes Borland __cdecl names with a leading underscore (ie _Foo). It also includes mangled C++ names. If someone hands you a DLL with mangled functions names in it, you can always pass those ugly, mangled names to GetProcAddress. Whether you will actually be able to call the function without crashing is another matter, but at least you will have a chance.
& S9 Z0 J! S% S; v--------------------------------------------------------------------------------) P& C( P& z4 E4 v
</P>" z; [+ [0 Z7 S" w: W2 m
<P>
. k5 d6 K: u, b! zThat's all there is to it. Just compile the code in MSVC and your off. You don't have to mess with DEF files or import libraries. But you do have some grunt work to handle from inside your code. </P>- r  P* A% g0 m5 N* `
<P>Example 3: Implicit linking with a #define kludge + Y5 \2 y! G/ P& u) Y# K  z
This example shows probably the easiest way to use a BCB DLL from an MSVC project, but it is probably also the least attractive way. The code uses a crafty #define to add the leading underscore to __cdecl functions whenever the Microsoft compiler is detected. In other words, we simply #define Foo to be _Foo. </P>
8 H- @/ X: z3 w<P>The advantage of this technique is that we don't have to perform any function aliasing. We can just export __cdecl functions that contain a leading underscore. However, we still have to create an MSVC compatible import library using Microsoft's LIB.EXE </P>; A6 W. B" f$ L+ n+ g% J
<P>The key to this technique is that MSVC does not expect __cdecl functions to be decorated in any way (see Table 1). They should just appear as is. If an MSVC app tries to execute a __cdecl DLL function called Foo, it expects to find an undecorated function called Foo inside the DLL. If we change the MSVC code so it calls _Foo, then it will try to find a function called _Foo in the DLL. </P>5 F9 _) }/ G/ i5 z+ w$ A
<P>Borland adds a leading underscore to all __cdecl functions. We can coax MSVC into calling this function by simply adding a leading underscore to the function name. Keep in mind though that we only want to add an underscore on the MSVC side, and not on the Borland side. </P>
7 y" Q' W  @) d6 ]$ L<P>The DLL code for the #define kludge is exactly the same as the code in Listing 2 from Example 1 The only difference is the DLL header file. The DLL header file adds an underscore to each function prototpype when MSVC is detected. Listing 7 shows the modified header file. </P>  \$ p& a& o& u
<P>// ----------------------------------------------5 _4 M2 I( A1 \. s% W
// Listing 7- DLL header file
3 X, o. @$ L4 b# ^6 O#ifndef BCBDLL_H
3 |- u4 U, l7 M( [#define BCBDLL_H</P>
7 }; A/ H9 f8 D$ c8 R* e<P>#ifdef __cplusplus  K2 J! W& u9 i2 f/ r
extern "C" {. Q( i: `) L+ z
#endif</P>+ S/ g. v, u; E
<P>#ifdef BUILD_DLL# X: {+ k/ W+ R+ E  w1 g7 T
#define IMPORT_EXPORT __declspec(dllexport)9 `; ^3 y+ i% l0 A' h) c+ F: m
#else9 K% @2 |7 r: k6 G2 Y) x, N
#define IMPORT_EXPORT __declspec(dllimport)" n- |5 }$ r# R/ N3 C& J9 v; g* T
#endif</P>' Z3 E3 ?1 i  H
<P>// #define kludge. If we are being compiled with MSVC, then just tack on a
- T# T& F( S, s. q  w# Q) |  h// leading underscore because Borland C++ will export Foo and Bar as _Foo
, i( P; |+ @1 O- `, d// and _Bar respectively
, r; z8 p6 j6 ]5 q#ifdef _MSC_VER
% L+ B8 g5 L3 ]  P, @#define Foo _Foo( B- I* `1 y! l2 `
#define Bar _Bar
# f" ~" {% k" F4 t' K# ~#endif</P>7 D6 A8 i3 i2 u2 Q
<P>IMPORT_EXPORT int __cdecl  Foo  (int Value);* I1 j4 }- E2 Q0 C/ T6 X3 E
IMPORT_EXPORT int __cdecl  Bar  (void);</P># [9 p' X/ t* i% l5 u4 g0 g
<P>#ifdef __cplusplus
7 G6 ]- O) Y& L' d}9 i- Z& O# a3 G' K  P: K
#endif% I( T/ ]- R: F
#endif
' _# q& e( J" k+ J& w// ----------------------------------------------</P>2 K) X7 O) n% v! p1 o
<P>In addition to the #define kludge in the header file, you still have to create an MSVC compatible import library. You do this using the same steps as before. Run IMPDEF on the compiled DLL to get a DEF file. Then run the Microsoft LIB.EXE tool to create a COFF import library. This time, you don't have to worry about editing the DEF file. Finally, copy the DLL, the COFF import library, and the DLL header to your MSVC project. Add the library to your MSVC project and rebuild it. </P>
! ^; A* L7 l: f0 p6 ]% j<P>Here are the command line examples for creating the MSVC import library. Note that we don't have to edit the DEF file. We can just pass it as-is to LIB.EXE </P>
& O- U& g2 W: m/ \9 f3 R<P>// Create def file" n! w! U5 A( h2 O+ s; J; L
&gt; impdef bcbdll.def bcbdll.dll</P>$ K/ p6 W! |  C$ b
<P>// create COFF import library using MS lib.exe
* m) q$ Z: e" n- T&gt;lib /def bcbdll.def</P>
2 p  a2 U" f* a+ b8 h2 ]5 C+ X0 G, _<P>Example 4: Implicit linking with __stdcall functions 9 e3 y4 d! u* \) x  w& Q
Before we proceed, let's examine why we need to treat __stdcall functions separately. MSVC does not provide a tool that is equivalent to Borland's IMPLIB tool. You cannot grab a DLL and generate an import library for it using MSVC. The closest tool is LIB.EXE, which can create an import library from a DEF file. The DEF file must be either be created by hand, or generated from a utility tool such as Borland's IMPDEF tool. </P>* e& i5 _! S# W$ j# X! \0 }9 F
<P>No big deal right? You can still create MSVC import libraries, you just have to go through the intermediate step of creating the DEF file an passing it to the LIB.EXE tool. Correct, as long as you stick to __cdecl functions. You will run into problems as soon as you switch to __stdcall. The problem is that Microsoft's LIB.EXE tool is completely incapable of generating an import libary for a DLL that exports __stdcall functions. </P>
& P6 M$ t4 f( V8 ]/ C: }/ \<P>For this reason, I have separated implicit linking with __stdcall into it's own section. We need to follow a different sequence of steps to create the Microsoft compatible import library. (Also note that I put this section last for good reason, the steps are tedious to say the very least). </P>
5 R- R8 b9 e( ~5 D5 [6 c/ G<P>Since we can't use LIB.EXE to generate an import library for a BCB DLL with __stdcall functions, we need to come up with a different strategy. One way to generate the import library (and perhaps the only way), is to rely on the fact that MSVC generates an import library whenever you build a DLL. If you build an MSVC DLL that contains __stdcall functions, the compiler and linker generate an import library the will correctly resolve the exported __stdcall routines. </P>
* e. M' }1 V, D8 I2 J$ K<P>So how does this help us you ask? After all, we are compiling the DLL in Borland C++. What good does it do to create a DLL project in MSVC? We want the EXE to be compiled with MSVC, but the DLL should remain on the Borland side. The answer to this question is that we can compile a dummy DLL project in MSVC for the sole purpose of generating a __stdcall import library. The DLL created by MSVC can be pitched in the trash. We don't need it. </P>
& L1 h* F$ o2 H' Y0 h<P>The dummy DLL project is the foundation of this technique. We create a dummy DLL project in MSVC for the sake of generating a Microsoft import library. We then combine this import library with the BCB generated DLL to give MSVC users a way to call our Borland DLL with __stdcall routines. </P>
8 V2 I6 w8 p9 t8 n" {3 L5 E, b<P>Here are the steps necessary for this technique. First, compile your DLL with BCB. Use the __stdcall calling convention, export plain C functions, and wrap all prototypes with extern "C". The code for the DLL is the same as the code in Listings 4 and 5 from Example 2, so I won't list them again. The second step is to create the dummy DLL project in MSVC. Compile the dummy DLL project and swipe the generated import library. The last step is to add this import library to any MSVC project that wants to call the borland DLL. </P>/ y4 o# X3 q- i5 u! \
<P>The most challenging part of this technique is genering the dummy DLL project and the import libary. To build the dummy DLL project, create a non-MFC DLL workspace with MSVC. Edit the MSVC project settings so that the name of the generated DLL matches the name of the BCB DLL (bcbdll.dll in our examples). This setting can be found under Project-Settings-Link. Copy your DLL header file and source file from the Borland project directory over to the dummy DLL project directory. If your project consists of multiple CPP files, then copy over only the header files and source files that contain exported functions. Add the CPP source file to the dummy workspace. </P>9 W  i, d5 E+ n0 Y) s  ~
<P>Next, go into the definition of each exported function and rip out the code for each routine. You should end up with a bunch of empty functions. If a routine has a return value, leave a return statement in place. Just have the return value be some dummy value (such as 0). In addition to ripping out the function bodies, remove any #include statements that are not necessary (you should be able to remove most #includes, since all the function bodies are empty). </P>
) k6 G* h4 S( J* a! V) T8 y<P>Our BCB DLL contains the same code that was shown in Listings 4 and 5 from example 2. Listing 8 shows the trimmed down version of the same code. This trimmed down version is added to the dummy DLL workspace. </P>
0 n1 @1 t: r: d! B& U# \<P>// ----------------------------------------------1 d+ s" X, L/ I( [- d: G
// Listing 8- dummy DLL source code) ?( Y* k7 I0 {' I6 Q. R) W
#define BUILD_DLL
: k8 w! n; p! _& A. X1 Q  O; W: }#include "bcbdll.h"</P>  Y& g7 R( s5 ^4 U6 w
<P>int __stdcall  Foo  (int Value)+ k& c7 c2 r! j! H. R, v( T
{
; L' G- I  @3 z. A# i. p( p" L3 W; |    return 0;
, G: l) m* D) m}</P>
3 F3 ]+ f$ o% J; A, d' U<P>int __stdcall Bar  (void)+ k  ~, g: H& @1 Z' l& k  c& K2 S
{
! D* `& ^7 Y% u9 _    return 0;3 u/ n- E7 h/ j& e2 C
}
3 {9 z: m- q( x// ----------------------------------------------</P>. E; s% [' j# u+ ^& ^5 z
<P>At this point, we should be able to compile the dummy DLL project in MSVC. But before we do, we must perform one more step to combat some hoakiness in the Microsoft compiler. Our dummy DLL exports __stdcall functions. When a Microsoft DLL exports a __stdcall routine, in normally decorates them by adding a leading underscore and appending an <a href="mailt'@'" target="_blank" >'@'</A> symbol and a number to the end of the function (see Table 1 at the beginning of the article). For example, Foo would be exported as <a href="mailt_Foo@4" target="_blank" >_Foo@4</A>. This is not the behavior we want. The entire purpose of the dummy DLL is to generate an MSVC import library for our BCB DLL. Our BCB DLL contains plain, undecorated, __stdcall functions (Foo and Bar). It doesn't do any good to generate an import library with decorated names (<a href="mailt_Foo@4" target="_blank" >_Foo@4</A> and <a href="mailt_Bar@0" target="_blank" >_Bar@0</A>) that don't match. </P>0 Z) b7 _8 A8 V* o2 ?
<P>Fortunately, we can prevent MSVC from decorating the dummy __stdcall functions by adding a DEF file to the dummy DLL project. The DEF file should simply list each exported routine. Like this: </P>
4 Z; Z. b: H% m7 z: k% M) P<P>LIBRARY     BCBDLL.DLL</P>
4 u$ W% x+ d' D) C1 L/ H- I<P>EXPORTS
4 B' {6 I& g. l    Bar% C% G4 ]6 |' Y# ^( F" D; H
    Foo</P>; D8 R9 u( c) g2 Y
<P>  Note:
; a0 \# k: F' e--------------------------------------------------------------------------------
. L% y; d2 Q; h5 A' lThe library name in the DEF file should match the name of the dummy DLL generated by MSVC, which in turn should match the name of the DLL we created with BCB. If these three items don't match, then you will run into a variety of different errors (usually unresolved linker errors). 2 P. \7 ~4 N7 Q" ]
--------------------------------------------------------------------------------
, ?, f: B6 p- u5 W: Y$ h </P>0 o2 q# [4 x/ ^& M( J
<P>
. E% N* j! _8 b3 _! i0 CAdd the DEF file to the dummy DLL project and build the dummy DLL. MSVC should create an import library when it builds the DLL project. This import library is the key ingredient to allowing MSVC users to implicitly link with a BCB DLL that exports __stdcall functions. Save this import library, and give it to MSVC users along with your DLL. Your users should add this import library to any MSVC project that calls your BCB DLL. </P>( C  E' ~7 F, J# y+ v8 l6 H6 t  Y3 ]
<P>Conclusion * P$ i( ?4 c3 M- S
This article provided four techniques for calling a BCB compiled DLL from Microsoft Visual C++. I hope that the article has described each technique adequately (some of this stuff is not easy to follow). To help you understand each technique, I have made the sample code for each technique available for download. The zip file extracts into four subdirectories, one for each technique. Each of those subdirectories contains a DLL directory with a BCB5 DLL project, and and EXE directory with an MSVC project that calls the BCB DLL. The MSVC projects are VC++ 5 projects, but they should work in MSVC 6 as well. </P>
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2025-5-11 17:44 , Processed in 0.406863 second(s), 51 queries .

回顶部