我在Windows 7上使用Delphi XE和Matlab 2012B编译器。
我正在尝试编写几个包装函数,因此可以更轻松地从Delphi XE调用使用Matlab 2012b编译器创建的DLL文件。我发现在使用MCR时我应该使用_proxy函数,这确实允许我成功调用几个函数。我也可以通过将它们作为PAnsiChar传递给Matlab而没有问题。
我目前正在尝试使用一些字段名创建StructArray。 由于我已经成功创建了数值数组和矩阵,我很确定前2个参数是正常的。我希望最后一个导致错误,但我不知道如何解决这个问题。查看Matlab帮助和示例文件,我正在做应该做的事情。显然我错了......
我知道使用Matlab r13我们必须将字段名称作为array[0..n] of pAnsiChar
而不是array of pAnsiChar
传递。我在这里试过这个也无济于事。
有人可以告诉我,我是否确实已将正确的函数映射到mxCreateStructArray(_730_proxy),并且我是否按预期传递参数?
type
mxArray = pointer;
// mxArray *mxCreateStructArray(mwSize ndim, const mwSize *dims, int nfields, const char **fieldnames);
function MCRdll_CreateStructArray(aDimCount: integer; aDims: pointer; aFieldCount: integer; aFields: PPAnsiChar): mxArray; cdecl; external 'mclmcrrt8_0.dll' name 'mxCreateStructArray_730_proxy';
function MCR_CreateStructArray(aFieldNames: TArray<string>): mxArray;
var
i: integer;
lstDims: array of integer;
lstNames: array of pAnsiChar;
begin
SetLength(lstNames, Length(aFieldNames));
for i := 0 to Length(aFieldNames) - 1 do
lstNames[i] := ToPAnsiChar(aFieldNames[i]); //Creates a new PAnsiChar with the content of aFieldNames[i]
SetLength(lstDims, 2);
lstDims[0] := 1;
lstDims[1] := Length(aFieldNames);
//This call raises an "External Exception" from Matlab.
Result := MCRdll_CreateStructArray(Length(lstDims), @lstDims, Length(lstNames), @lstNames);
end;
答案 0 :(得分:1)
MATLAB C API函数是:
mxArray *mxCreateStructArray(mwSize ndim, const mwSize *dims,
int nfields, const char **fieldnames);
据我了解,mwSize
默认与int
相同。这转化为Delphi中的Integer
。 const char**
参数是const C字符串数组的地址。把它翻译成德尔福,你有:
function MCRdll_CreateStructArray(ndim: Integer; dims: PInteger;
nFields: Integer; fieldnames: PPAnsiChar): mxArray; cdecl;
external 'mclmcrrt8_0.dll' name 'mxCreateStructArray_730_proxy';
现在,如何获取参数。好吧,假设你想要一个向量,dims
是一个长度为2的数组,ndim
就是那个长度。我将其声明为静态数组:
var
dims: array [0..1] of Integer;
对于字段名称,这些是可变长度。所以你需要一个PAnsiChar
的动态数组。那就是:
var
fieldnames: array of PAnsiChar;
您还需要将struct数组的向量长度传递给函数。这使你的功能是这样的:
function MCR_CreateStructArray(len: Integer;
const aFieldNames: array of AnsiString): mxArray;
var
i: integer;
dims: array [0..1] of Integer;
fieldnames: array of PAnsiChar;
begin
if Length(aFieldNames)=0 then
begin
Result := nil;
exit;
end;
dims[0] := 1;
dims[1] := len;
SetLength(fieldnames, Length(aFieldNames));
for i := 0 to high(fieldnames) do
fieldnames[i] := PAnsiChar(aFieldNames[i]);
Result := MCRdll_CreateStructArray(Length(dims), @lstDims[0],
Length(fieldnames), @fieldnames[0]);
end;
最终参数的替代方法是传递PPAnsiChar(fieldnames)
。这是有效的,因为动态数组变量是第一个元素的地址。
那么,您的版本出了什么问题?您犯的最大错误是对传递给MCRdll_CreateStructArray
的两个数组使用无类型指针。这意味着编译器无法检查您的间接是否正确。你没有。
首先,在代码中将@lstDims
传递给第二个参数。现在lstDims
是代码中的动态数组。其实现有lstDims
是指向第一个元素的指针。因此,非正式地,lstDims
具有类型^Integer
。因此@lstDims
的类型为^^Integer
。这是间接的一个层次。你在最后一个参数中犯了同样的错误。
最后一点。我已经更改了函数的签名以接收AnsiString
数组。这是我编写代码的简单方法,因为我不需要担心UTF-16到ANSI的转换,并且可以使用简单的PAnsiChar
强制转换。你可能会受益于这个帮手:
function ToAnsiStringArray(const arr: array of string): TArray<AnsiString>;
var
i: Integer;
begin
SetLength(Result, Length(arr));
for i := 0 to high(Result) do
Result[i] := AnsiString(arr[i]);
end;
我没有编译任何这些,所以可能有一些不精确。我相信你不会因此而被推迟。