[英]Using Delphi DLL with dynamic array from C#
我有一個包含以下類型的Delphi DLL:
type
TStepModeType = (smSingle, smMultiStep);
TParameter = record
Number: Integer;
end;
TStruct = record
ModType: PAnsiChar;
ModTypeRev: Integer;
ModTypeID: Integer;
RecipeName: PAnsiChar;
RecipeID: Double;
RootParamCount: Integer;
StepMode: TStepModeType;
ParamCount: Integer;
Parameters: array of TParameter;
end;
我需要從C#調用這個DLL,傳遞一個對應於DLL將填充並返回的Delphi類型的ref對象。 我在C#代碼中定義了這樣的結構:
enum stepModeType
{
Single,
MultiStep
}
[StructLayout(LayoutKind.Sequential)]
struct parameter
{
public int Number;
}
[StructLayout(LayoutKind.Sequential)]
struct recipe
{
public string modType;
public int modTypeRev;
public int modTypeId;
public string recipeName;
public double recipeId;
public int rootParamCount;
public stepModeType stepMode;
public int paramCount;
public IntPtr parameters;
}
我一直很好,直到我在Delphi代碼中遇到動態數組(參數:TParameter數組)。 我知道動態數組是一個只有Delphi的構造,所以我選擇在我的C#代碼中使用IntPtr,希望得到一個指向數組的指針並拉出內容。 不幸的是,我對這個互操作的東西比較新,我不知道如何處理IntPtr。
假設Delphi DLL使用2個參數項填充動態數組。 有人可能會告訴我C#代碼,一旦它從Delphi DLL傳遞回我的C#調用應用程序,它將從數組中獲取這兩個參數項嗎?
更新:好吧,因為它發生的Delphi代碼是一個簡化版本。 我們的一位Delphi開發人員認為簡化版本比實際版本更容易,實際版本包含動態數組動態數組的動態數組。 無論如何,我現在完全在我頭上。 我只知道Delphi是危險的。 下面是Delphi代碼中真實結構的代碼。 如何從我的C#調用應用程序處理這些結構的任何進一步指導將不勝感激。 對於動態數組的嵌套,它們甚至可能是不可能的。
type
TStepModeType = (smSingle, smMultiStep);
TParamValue = record
strVal: String;
fVal: Double;
Changed: Boolean;
end;
TSteps = array of TParamValue;
TRule = record
Value: String;
TargetEnabled: Boolean;
end;
TParamInfo = record
Caption: String;
Units: String;
RuleCount: Integer;
Rules: array of TRule;
end;
TParameter = record
Info: TParamInfo;
Steps: TSteps;
end;
TStruct = record
ModType: PAnsiChar;
ModTypeRev: Integer;
ModTypeID: Integer;
RecipeName: PAnsiChar;
RecipeID: Double;
RootParamCount: Integer;
StepMode: TStepModeType;
ParamCount: Integer;
Parameters: array of TParameter;
end;
我假設DLL有一個解除recipe
結構的函數。 這是你不可能希望用C#做的事情。 稍后將詳細介紹這一點。
Delphi動態數組不是有效的互操作類型。 它實際上應該只在內部用於使用單個版本的編譯器編譯的Delphi代碼。 公開公開它類似於從DLL導出C ++類。
在理想的世界中,您將重新處理Delphi代碼,以便使用適當的互操作類型導出數組。 但是,在這種情況下,在不調整Delphi代碼的情況下進行編組實際上相對容易。
德爾福動態數組在Delphi 4中引入,從那時起它們的實現一直保持不變。 array of T
動態數組變量數組實際上是指向第一個元素的指針。 元素按順序排列在內存中。 動態數組變量還保持(在負偏移處)引用計數和數組的大小。 您可以放心地忽略這些,因為您既不修改動態數組也不需要確定它的大小。
使用IntPtr
作為Parameters
字段是完美的。 因為TParameter
只包含一個32位整數,所以可以使用Marshal.Copy
將其直接復制到int[]
數組。
因此,當Delphi DLL返回時,您可以使用Marshal.Copy
執行最后的編組步驟。
if (theRecipe.paramCount>0)
{
int[] parameters = new int[theRecipe.paramCount];
Marshal.Copy(theRecipe.parameters, parameters, 0, theRecipe.paramCount);
... do something with parameters
}
這涉及動態數組,但實際上你的代碼存在另一個問題。 您在C#結構中將兩個字符串聲明為string
。 這意味着編組器將負責釋放兩個PAnsiChar
字段中Delphi DLL返回的內存。 它將通過調用CoTaskMemFree
。 我很確定這不會與Delphi代碼中的PAnsiChar
字段的分配相匹配。
如上所述,我希望這個接口的合同是你調用另一個DLL函數來釋放recipe
結構引用的堆內存。 也就是說,兩個字符串和動態數組。
要從C#處理此問題,您需要確保marshaller不會嘗試釋放PAnsiChar
字段。 您可以通過在C#結構中將它們聲明為IntPtr
來實現這一點。 然后調用Marshal.PtrToStringAnsi
轉換為C#字符串。
為了寫上面的內容,我必須對Delphi代碼和C#代碼之間的契約做一些假設。 如果我的任何假設不正確,請更新問題,我會盡力使這個答案匹配! 我希望這有幫助。
我懷疑的行話混亂,我的第一個想法很簡單。
public parameter []參數;
有兩個選項:要么你弄清楚動態數組是如何存儲的,要么在c#端匹配,或者更好地在Delphi端創建一組基本方法,可以從c#端調用來操作數組和記錄,例如getItem和setItem等。當語言障礙中存在不兼容的類型時,通常會執行此操作。 我會使用后面的方法,因為你不知道在未來的某個時候動態數組的內存結構是否會發生變化。
順便說一句,為什么你將TParameter定義為記錄,你可以使用TParameter = integer?
我發現這個鏈接有關於Delphi動態數組結構的說法:
http://www.programmersheaven.com/mb/delphikylix/262971/262971/dynamic-array-memory-storage/
此鏈接有更多細節。 結構比簡單的數組復雜一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.