2014-03-27 65 views
0

我使用德爾福XE和Windows 7 Matlab的2012B編譯德爾福XE創建使用MCR用於在Matlab使用編譯好的DLL

我試着寫一些包裝函數與創建這樣的DLL文件結構數組Delphi 2012b編譯器可以更容易地從Delphi XE調用。我發現在使用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; 

回答

1

的MATLAB C API函數爲:

mxArray *mxCreateStructArray(mwSize ndim, const mwSize *dims, 
    int nfields, const char **fieldnames); 

據我瞭解,mwSize默認情況下是一樣的int。這在Delphi中轉換爲Integerconst 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; 

我沒有任何編譯這所以有可能會出現一些不準確。我相信你不會因此而拖延。

+0

我複製了一個名爲ToPAnsiChar的現有函數來完成轉換。經過我的調查,我確實打算將包裝器更改爲函數ToPAnsiArray(字符串:TArray ):TArray 'lstDims'的奇怪之處在於它確實適用於其他調用,例如'mxCreateNumericArray'。你知道這是爲什麼嗎? – deColaman

+0

正如問題'@ lstDims'映射到'int **'所寫的那樣。它從來沒有工作。也許你之前使用的是固定長度的數組。 –