delphi调用c++的dll
软件: delphi
Delphi调用C++编写的DLL方法详解
Delphi与C++混合编程是常见的开发需求,特别是在需要利用C++高性能库或现有代码库时。以下是Delphi调用C++ DLL的几种主要方法及详细实现步骤。
一、通过标准DLL方式调用
1. 创建C++ DLL
首先需要创建一个C++编写的DLL,并确保函数导出方式正确:
// MyDll.h
ifdef MYDLL_EXPORTS
define MYDLL_API __declspec(dllexport)
else
define MYDLL_API __declspec(dllimport)
endif
extern "C" MYDLL_API int __stdcall Add(int a, int b);
// MyDll.cpp
include "MyDll.h"
extern "C" MYDLL_API int __stdcall Add(int a, int b) {
return a + b;
}
关键点:
使用extern "C"避免C++名称修饰(name mangling)
__stdcall指定调用约定,与Delphi的stdcall一致
__declspec(dllexport)确保函数被导出
2. Delphi中调用DLL
Delphi中可以通过静态或动态方式加载DLL:
静态调用方式
function Add(a, b: Integer): Integer; stdcall; external 'MyDll.dll';
动态调用方式
uses
System.SysUtils, System.DynamicLinkLibrary;
function Add(a, b: Integer): Integer;
var
DllHandle: HMODULE;
DllFunc: function(a, b: Integer): Integer; stdcall;
begin
DllHandle := LoadLibrary('MyDll.dll');
if DllHandle = 0 then
RaiseLastOSError;
try
@DllFunc := GetProcAddress(DllHandle, 'Add');
if Assigned(DllFunc) then
Result := DllFunc(a, b)
else
RaiseLastOSError;
finally
FreeLibrary(DllHandle);
end;
end;
动态调用的优势在于可以灵活处理DLL不存在的情况
二、调用约定注意事项
C++和Delphi之间的调用约定必须一致,否则会导致堆栈错误:
__stdcall:Windows API标准调用约定,参数从右向左压栈,被调用方清理堆栈
extern "C" __declspec(dllexport) int __stdcall Func(int a, int b);
function Func(a, b: Integer): Integer; stdcall; external 'DllName.dll';
__cdecl:C/C++默认调用约定,调用方清理堆栈
extern "C" __declspec(dllexport) int Func(int a, int b);
function Func(a, b: Integer): Integer; cdecl; external 'DllName.dll';
如果C++函数使用__stdcall但未使用extern "C",函数名会被修饰(如_Func@8),此时Delphi中需要指定修饰名:
function Func(a, b: Integer): Integer; stdcall;
external 'DllName.dll' name '_Func@8';
三、参数传递与数据类型对应
1. 基本数据类型对应
C++类型 Delphi类型
int Integer
float Single
double Double
char* PAnsiChar
wchar_t* PWideChar
bool Boolean
void* Pointer
2. 字符串传递
Delphi向C++ DLL传递字符串:
// Delphi侧
type

TSavePicture = procedure(filename: PAnsiChar); cdecl;
var
name: PAnsiChar;
begin
name := PAnsiChar(AnsiString('test.jpg'));
SavePicture(name);
end;
// C++侧
MYDLL void GetImage(char* filename) {
std::string str = filename;
// 处理字符串
}
3. 数组传递
Delphi向C++ DLL传递数组:
// Delphi侧
type
TTest = function(p: PInteger; count: Integer): Boolean; cdecl;
var
arr: array[0..255] of Integer;
begin
for i := 0 to 255 do
arr[i] := i;
TestFunc(@arr[0], 256);
end;
// C++侧
MYDLL void Test(int* arr, int count) {
for(int i = 0; i < count; i++) {
arr[i] = arr[i] * 2;
}
}
四、结构体和复杂数据类型传递
对于复杂数据类型,需要确保两边内存布局一致:
// C++侧
typedef struct {
char* p;
int s;
int i;
} MyStruct;
MYDLL void ProcessStruct(MyStruct* s);
// Delphi侧
type
PMyStruct = ^TMyStruct;
TMyStruct = record
p: PAnsiChar;
s: Integer;
i: Integer;
end;
var
Struct: TMyStruct;
begin
Struct.p := PAnsiChar('test');
Struct.s := 10;
Struct.i := 20;
ProcessStruct(@Struct);
end;
五、回调函数实现
Delphi向C++ DLL传递回调函数:
// C++侧
typedef void (__stdcall *CallbackFunc)(int progress);
MYDLL void SetCallback(CallbackFunc func);
// Delphi侧
type
TCallbackFunc = procedure(progress: Integer); stdcall;
procedure MyCallback(progress: Integer); stdcall;
begin
WriteLn('Progress: ', progress);
end;
var
SetCallback: procedure(cb: TCallbackFunc); stdcall;
begin
SetCallback := GetProcAddress(DllHandle, 'SetCallback');
SetCallback(MyCallback);
end;
六、通过COM组件调用
除了直接调用DLL,还可以通过COM组件方式:
1. 创建C++ COM组件
// MyComLib.h
include
[
object,
uuid(YOUR_UUID),
helpstring("My Com Component")
]
interface IMyComLib : IDispatch {
HRESULT __stdcall Add(long a, long b, long* result);
};
// MyComLib.cpp
include "MyComLib.h"
[uuid(YOUR_UUID)]
STDMETHODIMP MyComLib::Add(long a, long b, long* result) {
*result = a + b;
return S_OK;
}
2. Delphi调用COM组件
uses
ComObj, SysUtils;
var
MyComLib: Variant;
Result: Integer;
begin
try
MyComLib := CreateOleObject('MyComLib.IMyComLib');
MyComLib.Add(1, 2, Result);
ShowMessage(IntToStr(Result));
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
七、常见问题与解决方案
DLL加载失败
检查DLL路径是否正确
确保DLL依赖的所有其他DLL都可用
使用Dependency Walker工具检查DLL导出函数
函数调用失败
确保调用约定一致(stdcall/cdecl)
检查函数名是否被修饰,必要时使用name指定修饰名
确保参数类型和顺序完全匹配
内存管理问题
谁分配内存谁释放原则
对于字符串,通常由调用方分配和释放
对于复杂结构,确保两边内存布局一致
调试技巧
在Delphi中设置宿主程序调试DLL
使用OutputDebugString输出调试信息
在C++ DLL中加入日志功能
八、最佳实践建议
接口设计原则
保持接口简单,尽量减少参数数量
使用基本数据类型和简单结构
避免在接口中使用C++特有特性(如类、模板等)
错误处理
使用HRESULT返回错误码
提供GetLastError类似函数获取详细错误信息
在Delphi侧进行充分的错误检查
性能考虑
减少跨语言调用次数
对于大数据传递,考虑使用内存映射文件
批量处理数据而非单条处理
兼容性考虑
确保DLL与Delphi应用的位数一致(32/64位)
考虑不同Delphi版本的兼容性
测试在不同Windows版本上的行为
通过上面方法,Delphi可以有效地调用C++编写的DLL,充分利用两种语言的优势构建强大的应用程序。实际开发中,建议先从简单函数调用开始,逐步验证基本机制,然后再实现更复杂的数据交换和功能集成。
Delphi与C++混合编程是常见的开发需求,特别是在需要利用C++高性能库或现有代码库时。以下是Delphi调用C++ DLL的几种主要方法及详细实现步骤。
一、通过标准DLL方式调用
1. 创建C++ DLL
首先需要创建一个C++编写的DLL,并确保函数导出方式正确:
// MyDll.h
ifdef MYDLL_EXPORTS
define MYDLL_API __declspec(dllexport)
else
define MYDLL_API __declspec(dllimport)
endif
extern "C" MYDLL_API int __stdcall Add(int a, int b);
// MyDll.cpp
include "MyDll.h"
extern "C" MYDLL_API int __stdcall Add(int a, int b) {
return a + b;
}
关键点:
使用extern "C"避免C++名称修饰(name mangling)
__stdcall指定调用约定,与Delphi的stdcall一致
__declspec(dllexport)确保函数被导出
2. Delphi中调用DLL
Delphi中可以通过静态或动态方式加载DLL:
静态调用方式
function Add(a, b: Integer): Integer; stdcall; external 'MyDll.dll';
动态调用方式
uses
System.SysUtils, System.DynamicLinkLibrary;
function Add(a, b: Integer): Integer;
var
DllHandle: HMODULE;
DllFunc: function(a, b: Integer): Integer; stdcall;
begin
DllHandle := LoadLibrary('MyDll.dll');
if DllHandle = 0 then
RaiseLastOSError;
try
@DllFunc := GetProcAddress(DllHandle, 'Add');
if Assigned(DllFunc) then
Result := DllFunc(a, b)
else
RaiseLastOSError;
finally
FreeLibrary(DllHandle);
end;
end;
动态调用的优势在于可以灵活处理DLL不存在的情况
二、调用约定注意事项
C++和Delphi之间的调用约定必须一致,否则会导致堆栈错误:
__stdcall:Windows API标准调用约定,参数从右向左压栈,被调用方清理堆栈
extern "C" __declspec(dllexport) int __stdcall Func(int a, int b);
function Func(a, b: Integer): Integer; stdcall; external 'DllName.dll';
__cdecl:C/C++默认调用约定,调用方清理堆栈
extern "C" __declspec(dllexport) int Func(int a, int b);
function Func(a, b: Integer): Integer; cdecl; external 'DllName.dll';
如果C++函数使用__stdcall但未使用extern "C",函数名会被修饰(如_Func@8),此时Delphi中需要指定修饰名:
function Func(a, b: Integer): Integer; stdcall;
external 'DllName.dll' name '_Func@8';
三、参数传递与数据类型对应
1. 基本数据类型对应
C++类型 Delphi类型
int Integer
float Single
double Double
char* PAnsiChar
wchar_t* PWideChar
bool Boolean
void* Pointer
2. 字符串传递
Delphi向C++ DLL传递字符串:
// Delphi侧
type

TSavePicture = procedure(filename: PAnsiChar); cdecl;
var
name: PAnsiChar;
begin
name := PAnsiChar(AnsiString('test.jpg'));
SavePicture(name);
end;
// C++侧
MYDLL void GetImage(char* filename) {
std::string str = filename;
// 处理字符串
}
3. 数组传递
Delphi向C++ DLL传递数组:
// Delphi侧
type
TTest = function(p: PInteger; count: Integer): Boolean; cdecl;
var
arr: array[0..255] of Integer;
begin
for i := 0 to 255 do
arr[i] := i;
TestFunc(@arr[0], 256);
end;
// C++侧
MYDLL void Test(int* arr, int count) {
for(int i = 0; i < count; i++) {
arr[i] = arr[i] * 2;
}
}
四、结构体和复杂数据类型传递
对于复杂数据类型,需要确保两边内存布局一致:
// C++侧
typedef struct {
char* p;
int s;
int i;
} MyStruct;
MYDLL void ProcessStruct(MyStruct* s);
// Delphi侧
type
PMyStruct = ^TMyStruct;
TMyStruct = record
p: PAnsiChar;
s: Integer;
i: Integer;
end;
var
Struct: TMyStruct;
begin
Struct.p := PAnsiChar('test');
Struct.s := 10;
Struct.i := 20;
ProcessStruct(@Struct);
end;
五、回调函数实现
Delphi向C++ DLL传递回调函数:
// C++侧
typedef void (__stdcall *CallbackFunc)(int progress);
MYDLL void SetCallback(CallbackFunc func);
// Delphi侧
type
TCallbackFunc = procedure(progress: Integer); stdcall;
procedure MyCallback(progress: Integer); stdcall;
begin
WriteLn('Progress: ', progress);
end;
var
SetCallback: procedure(cb: TCallbackFunc); stdcall;
begin
SetCallback := GetProcAddress(DllHandle, 'SetCallback');
SetCallback(MyCallback);
end;
六、通过COM组件调用
除了直接调用DLL,还可以通过COM组件方式:
1. 创建C++ COM组件
// MyComLib.h
include
[
object,
uuid(YOUR_UUID),
helpstring("My Com Component")
]
interface IMyComLib : IDispatch {
HRESULT __stdcall Add(long a, long b, long* result);
};
// MyComLib.cpp
include "MyComLib.h"
[uuid(YOUR_UUID)]
STDMETHODIMP MyComLib::Add(long a, long b, long* result) {
*result = a + b;
return S_OK;
}
2. Delphi调用COM组件
uses
ComObj, SysUtils;
var
MyComLib: Variant;
Result: Integer;
begin
try
MyComLib := CreateOleObject('MyComLib.IMyComLib');
MyComLib.Add(1, 2, Result);
ShowMessage(IntToStr(Result));
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
七、常见问题与解决方案
DLL加载失败
检查DLL路径是否正确
确保DLL依赖的所有其他DLL都可用
使用Dependency Walker工具检查DLL导出函数
函数调用失败
确保调用约定一致(stdcall/cdecl)
检查函数名是否被修饰,必要时使用name指定修饰名
确保参数类型和顺序完全匹配
内存管理问题
谁分配内存谁释放原则
对于字符串,通常由调用方分配和释放
对于复杂结构,确保两边内存布局一致
调试技巧
在Delphi中设置宿主程序调试DLL
使用OutputDebugString输出调试信息
在C++ DLL中加入日志功能
八、最佳实践建议
接口设计原则
保持接口简单,尽量减少参数数量
使用基本数据类型和简单结构
避免在接口中使用C++特有特性(如类、模板等)
错误处理
使用HRESULT返回错误码
提供GetLastError类似函数获取详细错误信息
在Delphi侧进行充分的错误检查
性能考虑
减少跨语言调用次数
对于大数据传递,考虑使用内存映射文件
批量处理数据而非单条处理
兼容性考虑
确保DLL与Delphi应用的位数一致(32/64位)
考虑不同Delphi版本的兼容性
测试在不同Windows版本上的行为
通过上面方法,Delphi可以有效地调用C++编写的DLL,充分利用两种语言的优势构建强大的应用程序。实际开发中,建议先从简单函数调用开始,逐步验证基本机制,然后再实现更复杂的数据交换和功能集成。