⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vcdll.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 2 页
字号:


<HTML>

<HEAD>

   <TITLE>Using Visual C++ DLLs in a C++Builder Project</TITLE>

   <META NAME="Author" CONTENT="Harold Howe">

</HEAD>

<BODY>



<CENTER>

<TABLE  BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH="640">

<TR>



<TD>









<H2>

Using Visual C++ DLLs in a C++Builder Project

</H2>



<P>

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

<TT>\DLL\SRC</TT> 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.

</P>

<UL>

<LI><A HREF="#project"  >Calling DLL functions from a C++Builder project</A>

<LI><A HREF="#visualc"  >The problem with Visual C++ DLLs</A>

<LI><A HREF="#step1"    >Step 1: Identify calling conventions used by the Visual C++ DLL</A>

<LI><A HREF="#step2"    >Step 2: Examine the linker names in the DLL</A>

<LI><A HREF="#step3"    >Step 3: Generate an import library for the Visual C++ DLL</A>

<LI><A HREF="#step4"    >Step 4: Add the import library to your project</A>

<LI><A HREF="#conclusion" >Conclusion</A>

</UL>

</P>

<BR>

<H3>

<A NAME="project">Calling DLL functions from a C++Builder project</A>

</H3>

<P>

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

</P>

<P>

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 <I>Project | Add To Project</I> menu option in C++Builder. Next,

insert a <TT>#include</TT> 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.

</P>

<P>

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 (<TT>__stdcall</TT> and <TT>__cdecl</TT>). 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.

</P>

<pre>

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

<font color="navy">// Listing A: DLL.H</font>



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

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

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



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

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

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

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

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



FUNCTION <b>int</b> <b>__stdcall</b>   StdCallFunction<b>(</b><b>int</b> Value<b>)</b><b>;</b>

FUNCTION <b>int</b> <b>__cdecl</b>     CdeclFunction  <b>(</b><b>int</b> Value<b>)</b><b>;</b>

FUNCTION <b>int</b>             UnknownFunction<b>(</b><b>int</b> Value<b>)</b><b>;</b>



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

<b>}</b>

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





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

<font color="navy">//Listing B: DLL.C</font>



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

<font color="green">#include "dll.h"</font>



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



FUNCTION <b>int</b> <b>__cdecl</b>   CdeclFunction<b>(</b><b>int</b> Value<b>)</b>

<b>{</b>

    <b>return</b> Value <b>+</b> <font color="blue">2</font><b>;</b>

<b>}</b>



FUNCTION <b>int</b> UnknownFunction<b>(</b><b>int</b> Value<b>)</b>

<b>{</b>

    <b>return</b> Value<b>;</b>

<b>}</b>

</pre>

<P>

To create a test DLL from Listing A and Listing B, open up C++Builder and bring up the Object Repository by selecting

the <I>File | New</I> 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 statements. Now select

<I>File | New Unit</I>. Save the new unit as <TT>DLL.CPP</TT>. Cut and paste the text from Listing A and insert it into

the header file <TT>DLL.H</TT>. Then copy the code from Listing B and insert it into <TT>DLL.CPP</TT>. Make sure that

the <TT>#define</TT> for <TT>_BUILD_DLL_</TT> is placed above the include statement for <TT>DLL.H</TT>.

</P>

<P>

Save the project as <TT>BCBDLL.BPR</TT>. Next, compile the project and take a look at the files produced. C++Builder

generates both a DLL and an import library with a <TT>.LIB</TT> extension.

</P>

<P>

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 <TT>DLL.H</TT> header file from DLL project to this new project. Select the <I>Project | Add To Project</I> menu

option and add the LIB file to the project. Next, add a <TT>#include</TT> statement in the main unit that includes

<TT>DLL.H</TT>. Finally, add code that calls the DLL functions. Listing C shows code that calls each the DLL functions

from Listing A and B.

</P>

<pre>

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

<font color="navy">// Listing C: MAINFORM.CPP - DLLTest program</font>

<font color="green">#include &lt;vcl\vcl.h></font>

<font color="green">#pragma hdrstop</font>



<font color="green">#include "MAINFORM.h"</font>

<font color="green">#include "dll.h"</font>

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

<font color="green">#pragma resource "*.dfm"</font>

TForm1 <b>*</b>Form1<b>;</b>

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

<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b>

  <b>:</b> TForm<b>(</b>Owner<b>)</b>

<b>{</b>

<b>}</b>

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

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>Button1Click<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    <b>int</b> Value <b>=</b> StrToInt<b>(</b>Edit1<b>-></b>Text<b>)</b><b>;</b>

    <b>int</b> Result<b>=</b> StdCallFunction<b>(</b>Value<b>)</b><b>;</b>

    ResultLabel<b>-></b>Caption <b>=</b> IntToStr<b>(</b>Result<b>)</b><b>;</b>

<b>}</b>

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

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>Button2Click<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    <b>int</b> Value <b>=</b> StrToInt<b>(</b>Edit1<b>-></b>Text<b>)</b><b>;</b>

    <b>int</b> Result<b>=</b> CdeclFunction<b>(</b>Value<b>)</b><b>;</b>

    ResultLabel<b>-></b>Caption <b>=</b> IntToStr<b>(</b>Result<b>)</b><b>;</b>

<b>}</b>

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

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>Button3Click<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    <b>int</b> Value <b>=</b> StrToInt<b>(</b>Edit1<b>-></b>Text<b>)</b><b>;</b>

    <b>int</b> Result<b>=</b> UnknownFunction<b>(</b>Value<b>)</b><b>;</b>

    ResultLabel<b>-></b>Caption <b>=</b> IntToStr<b>(</b>Result<b>)</b><b>;</b>

<b>}</b>

</pre>

<BR>

<H3>

<A NAME="visualc">The problem with Visual C++ DLLs</A>

</H3>

<P>

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

<TT>IMPLIB</TT> utility, the file format differences are surmountable.

</P>

<P>

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.

</P>

<P>

With regard to linker function names, Borland and Microsoft disagree on these points:

<UL>

<LI>1- Visual C++ sometimes decorates exported <TT>__stdcall</TT> functions.

<LI>2- Borland C++Builder expects imported <TT>__cdecl</TT> functions to be decorated.

</UL>

</P>

<P>

So why is this such a big deal? Take disagreement #1 regarding the <TT>__stdcall</TT> calling convention. If you create a DLL

with Visual C++ that contains a <TT>__stdcall</TT> function called <TT>MyFunction()</TT>, Visual C++ will give the

function a linker name that looks like <TT>_MyFunction@4</TT>. When the Borland linker tries to resolve calls made to

this function, it expects to find a function with the name <TT>MyFunction</TT>. Since the import library for the Visual

C++ DLL doesn't contain a function called <TT>MyFunction</TT>, the Borland linker reports an unresolved external, which

means it couldn't find the function.

</P>

<P>

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.

</P>



<BR>

<H3>

<A NAME="step1">Step 1: Identify calling conventions used by the Visual C++ DLL</A>

</H3>

<P>

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:

</P>

<PRE>

    __declspec(dllimport) void <I>CALLING_CONVENTION</I> MyFunction(int nArg);

</PRE>

<P>

<TT>CALLING_CONVENTION</TT> should be <TT>__stdcall</TT> or <TT>__cdecl</TT> (see Listing A for

concrete examples). In many cases, the calling convention won't be specified, in which case it defaults to <TT>__cdecl</TT>.

</P>

<BR>

<H3>

<A NAME="step2">Step 2: Examine the linker names in the DLL</A>

</H3>

<P>

If step 1 reveals that the DLL utilizes the <TT>__stdcall</TT> 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++

⌨️ 快捷键说明

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