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

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

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


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


           Calling DLL functions from a C++Builder project
           The problem with Visual C++ DLLs
           Step 1: Identify calling conventions used by the Visual C++ DLL
           Step 2: Examine the linker names in the DLL
           Step 3: Generate an import library for the Visual C++ DLL
           Step 4: Add the import library to your project
           Conclusion


       Calling DLL functions from a C++Builder project


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


       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 Project | Add To Project menu option in C++Builder.
Next, insert a #include
       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.


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


           //------------------------------------------
           // Listing A: DLL.H


           #ifdef __cplusplus
           extern "C" {
           #endif


           #ifdef _BUILD_DLL_
           #define FUNCTION __declspec(dllexport)
           #else
           #define FUNCTION __declspec(dllimport)
           #endif


           FUNCTION int __stdcall   StdCallFunction(int Value);
           FUNCTION int __cdecl     CdeclFunction  (int Value);
           FUNCTION int             UnknownFunction(int Value);


           #ifdef __cplusplus
           }
           #endif


           //------------------------------------------
           //Listing B: DLL.C


           #define _BUILD_DLL_
           #include "dll.h"


           FUNCTION int __stdcall StdCallFunction(int Value)
           {
               return Value + 1;
           }


           FUNCTION int __cdecl   CdeclFunction(int Value)
           {
               return Value + 2;
           }


           FUNCTION int UnknownFunction(int Value)
           {
               return Value;
           }


       To create a test DLL from Listing A and Listing B, open up C++Builder
and bring up the Object
       Repository by selecting the File | New 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 statments. Now select File
| New Unit. Save the new unit
       as DLL.CPP. Cut and paste the text from Listing A and insert it into
the header file DLL.H. Then copy
       the code from Listing B and insert it into DLL.CPP. Make sure that
the #define for _BUILD_DLL_ is
       placed above the include statement for DLL.H.


       Save the project as BCBDLL.BPR. Next, compile the project and take a
look at the files produced.
       C++Builder generates both a DLL and an import library with a .LIB
extension.


       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
DLL.H header file from DLL
       project to this new project. Select the Project | Add To Project menu
option and add the LIB file to
       the project. Next, add a #include statement in the main unit that
includes DLL.H. Finally, add code
       that calls the DLL functions. Listing C shows code that calls each
the DLL functions from Listing A
       and B.


           //------------------------------------------
           // Listing C: MAINFORM.CPP - DLLTest program
           #include
           #pragma hdrstop


           #include "MAINFORM.h"
           #include "dll.h"
           //---------------------------------------------------------
           #pragma resource "*.dfm"
           TForm1 *Form1;
           //---------------------------------------------------------
           __fastcall TForm1::TForm1(TComponent* Owner)
             : TForm(Owner)
           {
           }
           //---------------------------------------------------------
           void __fastcall TForm1::Button1Click(TObject *Sender)
           {
               int Value = StrToInt(Edit1->Text);
               int Result= StdCallFunction(Value);
               ResultLabel->Caption = IntToStr(Result);
           }
           //---------------------------------------------------------
           void __fastcall TForm1::Button2Click(TObject *Sender)
           {
               int Value = StrToInt(Edit1->Text);
               int Result= CdeclFunction(Value);
               ResultLabel->Caption = IntToStr(Result);
           }
           //---------------------------------------------------------
           void __fastcall TForm1::Button3Click(TObject *Sender)
           {
               int Value = StrToInt(Edit1->Text);
               int Result= UnknownFunction(Value);
               ResultLabel->Caption = IntToStr(Result);
           }


       The problem with Visual C++ DLLs


       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 IMPLIB utility,
       the file format differences are surmountable.


       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.


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


           1- Visual C++ sometimes decorates exported __stdcall functions.
           2- Borland C++Builder expects imported __cdecl functions to be
decorated.


       So why is this such a big deal? Take disagreement #1 regarding the
__stdcall calling convention.
       If you create a DLL with Visual C++ that contains a __stdcall
function called MyFunction(),
       Visual C++ will give the function a linker name that looks like
_MyFunction@4. When the Borland
       linker tries to resolve calls made to this function, it expects to
find a function with the name
       MyFunction. Since the import library for the Visual C++ DLL doesn't
contain a function called
       MyFunction, the Borland linker reports an unresolved external, which
means it couldn't find the
       function.


       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.


       Step 1: Identify calling conventions used by the Visual C++ DLL


       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:


           __declspec(dllimport) void CALLING_CONVENTION MyFunction(int nArg
);


       CALLING_CONVENTION should be __stdcall or __cdecl (see Listing A for
concrete examples). In
       many cases, the calling convention won't be specified, in which case
it defaults to __cdecl.


       Step 2: Examine the linker names in the DLL


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


       The command line TDUMP utility allows you to examine the linker names
of functions exported by the
       DLL. The following command invokes TDUMP on a DLL.


           TDUMP -ee MYDLL.DLL > MYDLL.LST


       TDUMP can report a ton of information about the DLL. We're only
interested in functions exported by
       the DLL. The -ee command option instructs TDUMP to list only export
information. If the DLL is
       large, you may want to redirect the output of TDUMP to a file (via
the > MYDLL.LST appendage).


       The TDUMP output for the test DLL in Listing A and B looks like this:


           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'


       Notice the leading underscore and the trailing @4 on the __stdcall
function. The __cdecl 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 __stdcall function would not be
present.


       Step 3: Generate an import library for the Visual C++ DLL


       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.


       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.


       Table A: Visual C++ and C++Builder naming conventions


       Calling convention  VC++ name       VC++ (DEF used)     C++Builder
Name
       ---------------------------------------------------------------------
--
       __stdcall           _MyFunction@4   MyFunction          MyFunction
       __cdecl             MyFunction      MyFunction          _MyFunction


       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: __stdcall 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.


       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.


       Case 1: The DLL contains only __stdcall functions and the DLL vendor
utilized a DEF file.


       Table A reveals that VC++ and C++Builder agree only when the DLL uses
__stdcall 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 __stdcall calling
convention was used (Step 1), and
       TDUMP will reveal whether or not the functions are decorated (Step
2). If the DLL contains
       __stdcall 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 IMPLIB on the DLL. No
       aliases are needed.


       IMPLIB works like this:


           IMPLIB (destination lib name) (source dll)


       For example,


           IMPLIB mydll.lib mydll.dll


       Create the import library and move on to step 4.


       Case 2: The DLL contains __cdecl functions or decorated __stdcall
functions.


       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 __cdecl if the DLL vendor
       omits a calling convention when prototyping the functions, and
__cdecl forces you into Case 2.
       Secondly, even if your vendor has utilized the __stdcall calling
convention, they probably
       neglected to utilize a DEF file to strip the Visual C++ decorartions.


       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.


       The first step is to create a DEF file from the Visual C++ DLL by
using the IMPDEF program that
       comes with C++Builder. IMPDEF creates a DEF file that lists all of
the functions exported by the
       DLL. You invoke IMPDEF like this:


           IMPDEF (Destination DEF file) (source DLL file).


       For example


           IMPDEF mydll.def mydll.dll


       After running IMPDEF, 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 IMPDEF looks like
       this:


           LIBRARY     DLL.DLL


           EXPORTS
               CdeclFunction        @1
               UnknownFunction      @3
               _StdCallFunction@4   =_StdCallFunction      @2


       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:


           EXPORTS
             ; use this type of aliasing
             ; (Borland name)   = (Name exported by Visual C++)
               _CdeclFunction   = CdeclFunction
               _UnknownFunction = UnknownFunction
               StdCallFunction  = _StdCallFunction@4


       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.


       The final step is to create an aliased import library from the
aliased DEF file. Once again, you rely
       on the IMPLIB utility, except that this time, you pass IMPLIB the
aliased DEF file as its source file
       instead of the original DLL. The format is


           IMPLIB (dest lib file) (source def file)


       For example


           IMPLIB mydll.lib mydll.def


       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 TLIB utility to inspect the import library.


           TLIB mydll.lib, mydll.lst


       The list file for the test DLL looks like this:


           Publics by module


           StdCallFunction size = 0
                   StdCallFunction


           _CdeclFunction  size = 0
                   _CdeclFunction


           _UnknownFunction size = 0
                   _UnknownFunction


       Step 4: Add the import library to your project


       Once you create an import library for the Visual C++ DLL, you can add
the import library to your
       C++Builder project using the Project | Add to Project 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.


       Conclusion:


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


       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.


       C++Builder 3.0 introduced a new command line utility called
COFFtoOMF.EXE. This utility can
       convert a Visual C++ import library to a C++Builder import library.
Furthermore, the program will
       automatically alias __cdecl functions from the Visual C++ format to
the C++Builder format. The
       automatic aliasing can simplify Step 3 if the DLL exclusively uses
the __cdecl calling convention.


                                 Copyright ? 1997-1998 by Harold Howe.
                                        All rights reserved.

 

CPAL脚本实战:巧用TestWaitForSignalInRange函数,精准捕捉自动泊车测试中的车位信号 本文详细介绍了在自动泊车系统测试中如何利用CPAL脚本的TestWaitForSignalInRange函数精准捕捉车位信号。通过分析信号检测原理、函数进阶用法及构建鲁棒测试模块,帮助开发者有效区分有效车位与干扰信号,提升测试准确性和系统可靠性。 阅读详情

相关推荐

c++的return返回

m0_63774801的博客 94

Source SDK 2013与Steamworks集成指南:从环境配置到发布上线

游戏引擎与平台服务的集成是游戏开发中的关键技术环节,它涉及底层API对接、编译环境配置和运行时服务调用。其核心原理在于通过SDK提供的接口库,将游戏客户端或服务器与平台方的服务生态(如用户系统、云服务、社交功能)进行安全、稳定的数据交换与功能联动。这项技术的价值在于能极大拓展游戏的功能边界和发行能力,让独立作品也能具备3A级的平台化体验。典型的应用场景包括为模组或独立游戏添加成就系统、云存档、创意工坊支持以及多人联机服务。本文以Source SDK 2013和Steamworks的集成为具体案例,详细解析了

ithome 438

eval() 执行模型输出:国产推理服务 Xinference 工具调用解析 RCE(CVE-2026-61539,CVSS 10.0)

eval() 执行模型输出:国产推理服务 Xinference 工具调用解析 RCE(CVE-2026-61539,CVSS 10.0) TL;DR AI 推理服务 Xinference(Xor

Starry-SkyJing的博客 48

C语言怎么样?难学吗?

其实C语言并没有想象中的那么难,很多人刚开始接触时觉得很难是因为之前没有接触过类似这样的课程,其实它就是一门语言而已,只不过是给机器使用的,所以刚开始一般人的思维转不过来

qq2227918581的博客 200

DeepSeek Harness 系列(02):万物皆插件——Cordis 核心设计深度解读

dsh 的所有能力都建立在 Cordis 插件框架之上。这篇文章结合真实源码,深度解读 Cordis 五个核心机制:Plugin、Context、Service、Event、Effect——每个机制讲清楚「是什么、怎么用、为什么这么设计」。读完之后,dsh 的一切都会豁然开朗。

Cheson的专栏 490

c语言保留标识符

不管是。

mingtiauigena的博客 275

开发图形界面Tkinter、wxPython、PyQt、PySide选哪一个

Python GUI选框架,不是挑工具而是定路线。很多人以为随便找个库就能上手,结果做一半卡死在许可证或打包上。明明代码写得差不多了,却因为选错了起点,最后全重来。

2601_96229748的博客 189

【网络编程 Day2】TCP 通信核心:三次握手、核心 API、C/S 架构与粘包问题全梳理

面向连接:通信前必须通过三次握手建立连接,通信结束通过四次挥手断开可靠传输:有确认机制、超时重传、排序、流量控制、拥塞控制,保证数据不丢、不乱、不重复面向字节流:数据被看作连续的字节流,没有消息边界,会出现粘包全双工:同一个连接建立后,双方可以同时发送和接收数据,两条数据流独立TCP 连接有两条独立的数据流(A→B 和 B→A),可以同时收发,互不干扰。同一个 fd 既能调用 send 也能调用 recv,这就是全双工。TCP 是面向字节流的,没有消息边界。

2401_89475491的博客 822

C++聊天软件服务器 客户端代码分享

基于 Linux epoll + Qt 从零实现的轻量级即时通讯系统,支持聊天、好友、历史记录与文件传输。

sweetikelike的博客 249

VSCode C/C++环境设置

本文介绍了在VSCode中配置C/C++开发环境的完整流程:通过下载MinGW-w64工具链,安装C/C++及C/C++ Runner扩展,配置tasks.json与launch.json实现编译、运行与调试。重点说明了使用快捷键(如Ctrl+Shift+B编译、F5调试)及解决中文路径导致GDB报错的方法。适合初学者快速搭建高效C/C++开发环境。

念致达的博客 121

【SQLite3 数据库】从安装、SQL 操作到 C/C++ API 实战

1.在线安装 sudo apt-get install sqlite3 sudo apt-get install libsqlite3-dev。当select查询数据时,每找到一条数据,callback就会被触发一次;数据库中的数据,通过select查询出时,统一按照字符串处理;callback必须要有返回值,返回0,表示正常;创建数据库:sqlite3 xxx.db。

2301_79651221的博客 132

windows上CRNN-CTC 进行NCNN识别训练转换部署的全流程避坑操作手册

windows上CRNN-CTC 进行NCNN识别训练转换部署的全流程避坑操作手册

qianniulaoren的博客 239

C 语言 “mem”系列内存操作函数全景解析

C语言 mem 系列函数以字节为单位操作内存,不依赖 '\0',可处理任意数据类型。六大标准函数:memset(仅适合清零)、memcpy(无重叠)、memmove(支持重叠)、memccpy、memcmp、memchr。另有 GNU 扩展如 memmem、mempcpy 等。核心原则:不确定重叠时用 memmove,memset 仅用于清零。mem 系列比 str 更底层通用,是处理二进制数据的利器。

Guangyu536的博客 567

C++速通2

int a = 3;int b = 4;// 声明名字空间int main()int a = 1;// 1// ::表示全局作用域 2// 3// 4return 0;```一个函数使用virtual关键字修饰,就是虚函数,虚函数是函数覆盖的前提。在QtCreator中使用斜体字。```cpppublic:// 虚函数cout << "动物爱吃饭" << endl;

qq_63858767的博客 429

C++ std::queue<float> 完整教程

std::queue是front()back()push()pop()

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 64

C++第一课

你暂时不用背它。

qq_56657939的博客 112

STL基础语法

后进先出,只能在栈顶插入删除元素。

m0_65163985的博客 206

MAIN.c(1): warning C318: can’t open file ‘STC8G.H’ 报错 解决 分析

编译STC8G1K08-38I-TSSOP20时出现warning C318: can't open file 'STC8G.H',因Keil找不到头文件。解决方法:以管理员身份运行STC-ISP,进入【Keil仿真设置】,点击“添加STC单片机到Keil中”,选择Keil安装目录(如D:\software\KEILC51),系统将自动复制STC8G.H至C51\INC\STC。随后在Keil中配置Include Paths为C:\Keil_v5\C51\INC\STC,即可成功包含头文件并消除警告。校验路

dlj1656955044的博客 124

Linux下服务器端开发流程及相关工具介绍(C++)

本组对外提供 HTTP 服务,以 C++/Java 服务器开发为主,辅以少量 perl、shell 脚本;基于 ABS 打包 rpm 包,内部 yum 源存放,通过金字塔自动化发布。开发环境为 RHEL x86_64,代码默认 GBK 编码。介绍 Linux 任务管理:Ctrl+z、bg、jobs、fg、kill 管理后台任务;推荐 screen 实现会话保活;构建工具含 Make、CMake、Bazel;正则调试可使用[regexr.com](https://regexr.com)

sheep404的博客 145
上一篇: 窗口消息
下一篇: 怎样在BCB中使用VC的dll
bestpdg
博客等级 码龄20年 1粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值