我有一个包含以下类型的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;
Run Code Online (Sandbox Code Playgroud)
我需要从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;
}
Run Code Online (Sandbox Code Playgroud)
我一直很好,直到我在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;
Run Code Online (Sandbox Code Playgroud)
我假设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
}
Run Code Online (Sandbox Code Playgroud)
这涉及动态数组,但实际上你的代码存在另一个问题.您正在声明stringC#结构中的两个字符串.这意味着marshaller将负责释放Delphi DLL在两个PAnsiChar字段中返回的内存.它会通过电话来实现CoTaskMemFree.我很确定这不会PAnsiChar与Delphi代码中的字段分配相匹配.
如上所述,我希望这个接口的合同是你调用另一个DLL函数来释放recipestruct 引用的堆内存.也就是说,两个字符串和动态数组.
要从C#处理此问题,您需要确保marshaller不会尝试释放PAnsiChar字段.您可以通过IntPtr在C#结构中声明它们来实现这一点.然后调用Marshal.PtrToStringAnsi转换为C#字符串.
为了写上面的内容,我必须对Delphi代码和C#代码之间的契约做一些假设.如果我的任何假设不正确,请更新问题,我会尽力使这个答案匹配!我希望这有帮助.
| 归档时间: |
|
| 查看次数: |
2079 次 |
| 最近记录: |