使用带有C DLL的delphi数据类型的问题

时间:2014-06-13 16:41:02

标签: matlab delphi dll

我正在尝试使用一个用C语言编写的.dll(虽然它包含了一个matlab .ddl)

我尝试使用的功能在C中定义为:

 __declspec(dllexport) int ss_scaling_subtraction(double* time, double** signals, double* amplitudes, int nSamples, int nChannels, double* intensities);

.dll需要一个二维数组 - 当我尝试使用时:

Array of array of double

在声明中,编译器发出错误,因此我定义了自己的数据类型:

T2DArray = Array of array of double;

我在一个单位中初始化.dll函数:

 function ss_scaling_subtraction(const time: array of double; const signals: T2DArray; const amplituides : array of double; const nSamples: integer;const nChannels: integer; var intensities: array of double) : integer ; cdecl; external 'StirScanDLL.dll';

然而,当调用此函数时,我从.dll

获得访问冲突

创建新数据类型

T1DArray = array of double

并改变

Array of double

T1DArray

在声明中似乎使事情运行但结果仍然不正确。

我在这里读到,将delphi数据类型传递给.dll以不同语言编码会很危险,所以我认为这可能会导致问题。

但是当我必须首先使用它来正确地声明函数时,我怎么不使用delphi数据类型?!

额外的信息,我已经打开了matlab运行时编译器lib并打开了StirScanDLL.dll的入口点

1 个答案:

答案 0 :(得分:10)

这里的基本问题是二进制互操作不匹配之一。简单地说,指向数组的指针在二进制级别与Delphi开放数组参数不同。虽然它们在语义上都代表一个数组,但二进制表示不同。

C函数声明如下:

__declspec(dllexport) int ss_scaling_subtraction(
    double* time, 
    double** signals, 
    double* amplitudes, 
    int nSamples, 
    int nChannels, 
    double* intensities
);

在Delphi中声明你的函数:

function ss_scaling_subtraction(
    time: PDouble; 
    signals: PPDouble; 
    amplitudes: PDouble; 
    nSamples: Integer;
    nChannels: Integer; 
    intensities: PDouble
): Integer; cdecl; external 'StirScanDLL.dll';

如果您发现未声明PPDouble,请按此定义:

type
  PPDouble = ^PDouble;

即指针指向double的指针。

现在剩下的就是调用这些函数。在Delphi中将您的数组声明为动态数组。像这样:

var
  time, amplitudes, intensities: TArray<Double>; 
  signals: TArray<TArray<Double>>; 

如果你有一个较旧的pre-generics Delphi,那么声明一些类型:

type
  TDoubleArray = array of Double;
  T2DDoubleArray = array of TDoubleArray;

然后用适当的类型声明变量。

接下来,您需要分配数组,并填充从调用者传递给被调用者的任何数据。

SetLength(time, nSamples); // I'm guessing here as to the length
SetLength(signals, nSamples, nChannels); // again, guessing

最后是时候调用这个函数了。现在事实证明,Delphi的优秀设计师安排将动态数组存储为第一个元素的指针。这意味着它们不会被用作参数。

retval := ss_scaling_subtraction(
   PDouble(time),
   PPDouble(signals),
   PDouble(amplitudes),
   nSamples,
   nChannels,
   PDouble(intensities)
);

请注意,此处显示的动态数组的强制转换依赖于实现细节。因此,有些人可能认为最好使用,例如@time[0]等等,用于一维数组。并为幅度创建PDouble数组并复制内部数组的第一个元素的地址。就个人而言,我很依赖这个实现细节。它确实使编码变得更加简单。


最后一条建议。 Interop可能很棘手。这很容易出错。当你弄错了,代码编译,但在运行时死亡可怕。带有神秘的错误消息。导致头部刮伤。

所以,从最简单的界面开始。接收标量参数的函数。比如说,接收一个整数,并返回一个整数。证明你可以做到这一点。然后转到浮点标量。然后是一维数组。最后是二维数组。沿途的每一步,都会增加复杂性。当您遇到问题时,您会发现它已归结为最近添加的参数。

你没有采取这种做法。你已经直接进行了杀戮,并在你的第一次尝试中实现了一切。当它失败时,你不知道在哪里看。将问题分解成小块,并从那些较小的块中构建更复杂的问题。