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

delphi调用c++的dll

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,充分利用两种语言的优势构建强大的应用程序。实际开发中,建议先从简单函数调用开始,逐步验证基本机制,然后再实现更复杂的数据交换和功能集成。

index-foot-banner-pc index-foot-banner-phone

点击一下 免费体验万千客户信任的许可优化平台

与100+大型企业一起,将本增效

与100+大型企业一起,将本增效

申请免费体验 申请免费体验