bcbdll.htm

来自「C++builder学习资料C++builder」· HTM 代码 · 共 1,000 行 · 第 1/4 页

HTM
1,000
字号
<TT>GetProcAddress</TT> 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 <TT>GetProcAddress</TT>. This includes Borland <TT>__cdecl</TT> names with a leading  

underscore (ie <TT>_Foo</TT>). 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 <TT>GetProcAddress</TT>.  

Whether you will actually be able to call the function without crashing is another matter, but at least you  

will have a chance.  

<hr size = 1>  

</TD>  

</TR>  

</TABLE>  

  

<P>  

That'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>  

  

<H3>  

<A NAME="example3">Example 3: Implicit linking with a #define kludge</A>  

</H3>  

<P>  

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 <TT>#define</TT> to add the leading underscore to <TT>__cdecl</TT> functions whenever  

the Microsoft compiler is detected. In other words, we simply <TT>#define Foo</TT> to be <TT>_Foo</TT>.  

</P>  

<P>  

The advantage of this technique is that we don't have to perform any function aliasing. We can just export <TT>__cdecl</TT> functions  

that contain a leading underscore. However, we still have to create an MSVC compatible import library using Microsoft's <TT>LIB.EXE</TT>  

</P>  

<P>  

The key to this technique is that MSVC does not expect <TT>__cdecl</TT> functions to be decorated in any way (see Table 1). They  

should just appear as is. If an MSVC app tries to execute a <TT>__cdecl</TT> DLL function called Foo, it expects to find an  

undecorated function called <TT>Foo</TT> inside the DLL. If we change the MSVC code so it calls <TT>_Foo</TT>, then it will try to find  

a function called <TT>_Foo</TT> in the DLL.  

</P>  

<P>  

Borland adds a leading underscore to all <TT>__cdecl</TT> 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>  

<P>  

The DLL code for the <TT>#define</TT> 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>  

<pre>

<font color="navy">// ----------------------------------------------</font>

<font color="navy">// Listing 7- DLL header file</font>

<font color="green">#ifndef BCBDLL_H</font>

<font color="green">#define BCBDLL_H</font>



<font color="green">#ifdef __cplusplus</font>

<b>extern</b> <font color="blue">&quot;C&quot;</font> <b>{</b>

<font color="green">#endif</font>



<font color="green">#ifdef BUILD_DLL</font>

<font color="green">#define IMPORT_EXPORT __declspec(dllexport)</font>

<font color="green">#else</font>

<font color="green">#define IMPORT_EXPORT __declspec(dllimport)</font>

<font color="green">#endif</font>



<font color="navy">// #define kludge. If we are being compiled with MSVC, then just tack on a</font>

<font color="navy">// leading underscore because Borland C++ will export Foo and Bar as _Foo</font>

<font color="navy">// and _Bar respectively</font>

<font color="green">#ifdef _MSC_VER</font>

<font color="green">#define Foo _Foo</font>

<font color="green">#define Bar _Bar</font>

<font color="green">#endif</font>



IMPORT_EXPORT <b>int</b> <b>__cdecl</b>  Foo  <b>(</b><b>int</b> Value<b>)</b><b>;</b>

IMPORT_EXPORT <b>int</b> <b>__cdecl</b>  Bar  <b>(</b><b>void</b><b>)</b><b>;</b>



<font color="green">#ifdef __cplusplus</font>

<b>}</b>

<font color="green">#endif</font>

<font color="green">#endif</font>

<font color="navy">// ----------------------------------------------</font>

</pre>  

<P>  

In addition to the <TT>#define</TT> 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 <TT>IMPDEF</TT> on the  

compiled DLL to get a DEF file. Then run the Microsoft <TT>LIB.EXE</TT> 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>  

<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 <TT>LIB.EXE</TT>  

</P>  

<PRE>

// Create def file

&gt; impdef bcbdll.def bcbdll.dll



// create COFF import library using MS lib.exe

&gt;lib /def bcbdll.def

</PRE>  

  

<H3>  

<A NAME="example4">Example 4: Implicit linking with __stdcall functions</A>  

</H3>  

<P>  

Before we proceed, let's examine why we need to treat <TT>__stdcall</TT> functions separately. MSVC does not provide a tool that is  

equivalent to Borland's <TT>IMPLIB</TT> tool. You cannot grab a DLL and generate an import library for it using MSVC. The closest tool  

is <TT>LIB.EXE</TT>, 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 <TT>IMPDEF</TT> tool.  

</P>  

<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 <TT>LIB.EXE</TT> tool. Correct, as long as you stick to <TT>__cdecl</TT> functions. You will run into  

problems as soon as you switch to <TT>__stdcall</TT>. The problem is that Microsoft's <TT>LIB.EXE</TT> tool is completely incapable  

of generating an import libary for a DLL that exports <TT>__stdcall</TT> functions.  

</P>  

<P>  

For this reason, I have separated implicit linking with <TT>__stdcall</TT> 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>  

<P>  

Since we can't use <TT>LIB.EXE</TT> to generate an import library for a BCB DLL with <TT>__stdcall</TT> 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 <TT>__stdcall</TT> functions, the compiler and linker  

generate an import library the will correctly resolve the exported <TT>__stdcall</TT> routines.  

</P>  

<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 <TT>__stdcall</TT> import library. The DLL  

created by MSVC can be pitched in the trash. We don't need it.  

</P>  

<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  

<TT>__stdcall</TT> routines.  

</p>  

<P>  

Here are the steps necessary for this technique. First, compile your DLL with BCB. Use the <TT>__stdcall</TT> 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>  

<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 <I>Project-Settings-Link</I>. 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>  

<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 <TT>#include</TT> statements that are not necessary (you  

should be able to remove most <TT>#includes</TT>, since all the function bodies are empty).  

</P>  

<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>  

<pre>

<font color="navy">// ----------------------------------------------</font>

<font color="navy">// Listing 8- dummy DLL source code</font>

<font color="green">#define BUILD_DLL</font>

<font color="green">#include &quot;bcbdll.h&quot;</font>



<b>int</b> <b>__stdcall</b>  Foo  <b>(</b><b>int</b> Value<b>)</b>

<b>{</b>

    <b>return</b> <font color="blue">0</font><b>;</b>

<b>}</b>



<b>int</b> <b>__stdcall</b> Bar  <b>(</b><b>void</b><b>)</b>

<b>{</b>

    <b>return</b> <font color="blue">0</font><b>;</b>

<b>}</b>

<font color="navy">// ----------------------------------------------</font>

</pre>  

<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 <TT>__stdcall</TT> functions. When a Microsoft DLL exports a  

<TT>__stdcall</TT> routine, in normally decorates them by adding a leading underscore and appending an '@' symbol and a number  

to the end of the function (see Table 1 at the beginning of the article). For example, <TT>Foo</TT> would be exported as <TT>_Foo@4</TT>.  

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, <TT>__stdcall</TT> functions (<TT>Foo</TT> and <TT>Bar</TT>).  

It doesn't do any good to generate an import library with decorated names (<TT>_Foo@4</TT> and <TT>_Bar@0</TT>) that don't match.  

</P>  

<P>  

Fortunately, we can prevent MSVC from decorating the dummy <TT>__stdcall</TT> functions by adding a DEF file to the dummy DLL project.  

The DEF file should simply list each exported routine. Like this:  

</P>  

<PRE>

LIBRARY     BCBDLL.DLL



EXPORTS

    Bar

    Foo

</PRE>  

<P>  

<TABLE WIDTH="100%">  

<TR>  

<TD VALIGN="top">  

<IMG SRC="images/exclamation.gif" ALT="Tip" BORDER=0 HSPACE="0" ALIGN="top" width="32" height="48">  

</TD>  

<TD valign="top">  

<b>Note:</b>  

<hr size = 1>  

The 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).  

<hr size = 1>  

</TD>  

</TR>  

</TABLE>  

<P>  

Add 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 <TT>__stdcall</TT>  

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>  

  

<H3>  

<A NAME="conclusion">Conclusion</A>  

</H3>  

  

<P>  

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>  

  

<BR>  

<H3>  

<A NAME="downloads">Downloads</A>  

</H3>  

  

<BR>  

<TABLE  BORDER=1 CELLPADDING=10 CELLSPACING=0 WIDTH="100%">  

<TR> <TD colspan = 2><B>Downloads for this article</B> </TD> </TR>  

<TR> <TD><TT><a HREF="download/bcbdll.zip">bcbdll.zip</a></TT></TD><TD>Source code for all 4 techniques (130 kB).</TD> </TR> 

</TABLE> 

</TD> </TR> 

 

</TABLE> 

</CENTER> 

</BODY> 

</HTML> 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?