bcbdll.htm

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

HTM
1,000
字号
<TT>EXPORTS</TT> to <TT>IMPORTS</TT> because we are importing the functions now, rather than exporting them (I doubt that  

this makes a difference to the MSVC <TT>LIB.EXE</TT> tool).  

</P>  

<P>  

Next, we create a COFF library from this DEF file using the Microsoft <TT>LIB.EXE</TT> tool. The syntax is:  

<PRE>

lib /DEF:bcbdll.def /out:bcbdll_msvc.lib

</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 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 <TT>LIB.EXE</TT> visible. The batch file is called <TT>VCVARS32.BAT</TT>, and it is  

located in the <TT>\VC\BIN</TT> subdirectory of the DevStudio installation directory.  

<hr size = 1>  

</TD>  

</TR>  

</TABLE>  

  

<P>  

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 <TT>#include</TT> the DLL header file from  

there source code.  

</P>  

<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 <TT>main</TT> function, a <TT>#include</TT> for the DLL header, and the DLL calls themselves.  

The important thing is that you add the correct import library, the one generated by <TT>LIB.EXE</TT>, to your MSVC project.  

</P>  

  

<pre>

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

<font color="navy">// Listing 3- MSVC DLL client code</font>

<font color="green">#include &lt;iostream&gt;</font>

<font color="green">#include &lt;windows.h&gt;</font>

<b>using</b> <b>namespace</b> std<b>;</b>



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



<b>int</b> main<b>(</b><b>)</b>

<b>{</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Foo(10) = &quot;</font> <b>&lt;&lt;</b> Foo<b>(</b><font color="blue">10</font><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar()   = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar()   = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar()   = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

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

<b>}</b>

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

</pre>  

  

  

<H3>  

<A NAME="example2">Example 2: Explicit linking</A>  

</H3>  

<P>  

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

<P>  

In this example, we will create a DLL that exports two functions: <TT>Foo</TT> and <TT>Bar</TT>. The functions look like  

the same two functions from the previous example.  

</P>  

<pre>

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

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

</pre>  

<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 <TT>__cdecl</TT> 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 <TT>__cdecl</TT> functions</TT>. 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 <TT>__stdcall</TT> instead of <TT>__cdecl</TT>. This is what we will do in this example. Listings 4 and 5 show the source  

code for our DLL.  

</P>  

  

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

If you export <TT>__stdcall</TT> functions, it is crucial that client apps know this. Some people make the mistake of thinking that  

using <TT>__stdcall</TT> does nothing more than strip off the leading underscore that <TT>__cdecl</TT> functions have. Don't fall into  

this trap. <TT>__stdcall</TT> functions manage the stack differently. Bad things will happen if a client app calls treats a  

<TT>__stdcall</TT> function as if it were a <TT>__cdecl</TT> function (namely, the stack will get corrupted and the client app  

will die a horrible death).  

<hr size = 1>  

</TD>  

</TR>  

</TABLE>  

  

  

<pre>

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

<font color="navy">// Listing 4- 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>



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

IMPORT_EXPORT <b>int</b> <b>__stdcall</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>  

  

<pre>

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

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

<font color="green">#include &lt;windows.h&gt;</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> Value <b>+</b> <font color="blue">1</font><b>;</b>

<b>}</b>



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

<b>{</b>

    <b>static</b> <b>int</b> ret <b>=</b> <font color="blue">0</font><b>;</b>

    <b>return</b> ret<b>++</b><b>;</b>

<b>}</b>

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

</pre>  

<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 <TT>Foo</TT> and <TT>Bar</TT> to use the <TT>__stdcall</TT> calling convention instead of <TT>__cdecl</TT>.  

</P>  

<P>  

Now lets look at the code for the MSVC program that calls this DLL. The code is shown in Listing 6.  

</P>  

<pre>

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

<font color="navy">// Listing 6- MSVC client code</font>

<font color="green">#include &lt;iostream&gt;</font>

<font color="green">#include &lt;windows.h&gt;</font>

<b>using</b> <b>namespace</b> std<b>;</b>



HINSTANCE hDll <b>=</b> <font color="blue">0</font><b>;</b>

<b>typedef</b> <b>int</b> <b>(</b><b>__stdcall</b> <b>*</b>foo_type<b>)</b>  <b>(</b><b>int</b> Value<b>)</b><b>;</b>

<b>typedef</b> <b>int</b> <b>(</b><b>__stdcall</b> <b>*</b>bar_type<b>)</b>  <b>(</b><b>)</b><b>;</b>

foo_type Foo<b>=</b><font color="blue">0</font><b>;</b>

bar_type Bar<b>=</b><font color="blue">0</font><b>;</b>



<b>void</b> DLLInit<b>(</b><b>)</b>

<b>{</b>

    hDll <b>=</b> LoadLibrary<b>(</b><font color="blue">&quot;bcbdll.dll&quot;</font><b>)</b><b>;</b>

    Foo <b>=</b> <b>(</b>foo_type<b>)</b>GetProcAddress<b>(</b>hDll<b>,</b> <font color="blue">&quot;Foo&quot;</font><b>)</b><b>;</b>

    Bar <b>=</b> <b>(</b>bar_type<b>)</b>GetProcAddress<b>(</b>hDll<b>,</b> <font color="blue">&quot;Bar&quot;</font><b>)</b><b>;</b>

<b>}</b>



<b>void</b> DLLFree<b>(</b><b>)</b>

<b>{</b>

    FreeLibrary<b>(</b>hDll<b>)</b><b>;</b>

<b>}</b>



<b>int</b> main<b>(</b><b>)</b>

<b>{</b>

    DLLInit<b>(</b><b>)</b><b>;</b>



    cout <b>&lt;&lt;</b> <font color="blue">&quot;Foo() = &quot;</font> <b>&lt;&lt;</b> Foo<b>(</b><font color="blue">10</font><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar() = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar() = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    cout <b>&lt;&lt;</b> <font color="blue">&quot;Bar() = &quot;</font> <b>&lt;&lt;</b> Bar<b>(</b><b>)</b> <b>&lt;&lt;</b> endl<b>;</b>

    DLLFree<b>(</b><b>)</b><b>;</b>

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

<b>}</b>

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

</pre>  

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

<P>  

Secondly, notice that the code does not bother to <TT>#include bcbdll.h</TT>. There is an important reason for this.  

<TT>bcbdll.h</TT> declares prototypes for the <TT>Foo</TT> and <TT>Bar</TT> 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 <TT>Foo</TT> and <TT>Bar</TT> prototypes in the header file won't mean much to us.  

</p>  

<P>  

The third thing to notice about the code is the presence of the <TT>typedef</TT>'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 <TT>GetProcAddress</TT> function. You must store the result of <TT>GetProcAddress</TT> 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 <TT>Foo(10)</TT>).  

</P>  

<P>  

The <TT>typedef</TT> statements create two new types: <TT>foo_type</TT> and <TT>bar_type</TT>. These types are function pointer types.  

<TT>foo_type</TT> declares a type that is a pointer to a <TT>__stdcall</TT> function that takes one integer argument and returns an  

integer value. <TT>bar_type</TT> declares a type that is a pointer to a <TT>__stdcall</TT> function with no arguments and an  

integer return value. These <TT>typedef</TT>'s serve two purposes. First, they provide a clean way to declare the function  

pointer variables <TT>Foo</TT> and <TT>Bar</TT>. Secondly, they give us a convenient way to cast the result of  

<TT>GetProcAddress</TT>. The return value from <TT>GetProcAddress</TT> is a pointer to a <TT>__stdcall</TT> function with no  

arguments and an integer result. Unless your function follows this same format, you will need to perform a cast on the  

<TT>GetProcAddress</TT> result (this casting is why explicit linking is less type safe than implicit linking).  

</P>  

<P>  

Below the <TT>typedef</TT>'s are two variables called <TT>Foo</TT> and <TT>Bar</TT>. 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  

<TT>Foo</TT> and <TT>Bar</TT> to make the code resemble the implicit linking example. Don't be tripped up by this. The <TT>Foo</TT>  

and <TT>Bar</TT> variable names have no connection with the names of the actual functions in the DLL.  

We could have named the variables <TT>Guido</TT> and <TT>Bjarne</TT> if we wanted to.</P>  

<P>  

Below the function pointer declarations you will see two routines called <TT>DllInit</TT> and <TT>DllFree</TT>. 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 <TT>Foo</TT> and  

<TT>Bar</TT> like it normally would (or <TT>Guido</TT> and <TT>Bjarne</TT> if you change the names). The only hitch is that we must  

call <TT>DllInit</TT> before calling any DLL routines. We should also be nice and call <TT>DllFree</TT> to release the library.  

</P>  

  

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

⌨️ 快捷键说明

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