【问题标题】:How to convert a C++ union to Delphi如何将 C++ 联合转换为 Delphi
【发布时间】:2017-01-01 20:36:04
【问题描述】:

我知道以前有人问过这个问题,但是我尝试将一些 C++ 结构/联合转换为 Delphi 以使用海康威视 SDK 没有成功。

我尝试转换的 C++ 结构/联合如下:

struct{
  BYTE                           byEnable;
  BYTE                           byRes1[3];
  DWORD                          dwTriggerType;
  NET_ITC_TRIGGER_PARAM_UNION    uTriggerParam;
  BYTE                           byRes[64];
}NET_ITC_SINGLE_TRIGGERCFG,*LPNET_ITC_SINGLE_TRIGGERCFG;

union{
  DWORD                              uLen[1070];
  NET_ITC_POST_IOSPEED_PARAM         struIOSpeed;
  NET_ITC_POST_SINGLEIO_PARAM        struSingleIO;
  NET_ITC_POST_RS485_PARAM           struPostRs485;
  NET_ITC_POST_RS485_RADAR_PARAM     struPostRadar;
  NET_ITC_POST_VTCOIL_PARAM          struVtCoil;
  NET_ITC_EPOLICE_IOTL_PARAM         struIOTL;
  NET_ITC_EPOLICE_RS485_PARAM        struEpoliceRs485;
  NET_ITC_EPOLICE_RS485_PARAM        struPERs485;
}NET_ITC_TRIGGER_PARAM_UNION,*LPNET_ITC_TRIGGER_PARAM_UNION;

我尝试了以下方法:

  PNetItcSingleTriggerCfg = ^TNetItcSingleTriggerCfg;
  TNetItcSingleTriggerCfg = record
    byEnable:      Byte;
    byRes1:        array [0..2] of Byte;
    dwTriggerType: DWord;
    uTriggerParam: TNetItcTriggerParamUnion;
    byRes:         array [0..63] of Byte;
  end;

  PNetItcTriggerParamUnion = ^TNetItcTriggerParamUnion;
  TNetItcTriggerParamUnion = record
    case integer of
      0: (uLen:             array [0..1069] of DWord);
      1: (struIOSpeed:      TNetItcPostIOSpeedParam);
      2: (struSingleIO:     TNetItcPostSingleIOParam);
      3: (struPostRs485:    TNetItcPostRS485Param);
      4: (struPostRadar:    TNetItcPostRS485RadarParam);
      5: (struVtCoil:       TNetItcPostVTCoilParam);
      6: (struHvt:          TNetItcPostHvtParam);
      7: (struIOTL:         TNetItcEPoliceIOTLParam);
      8: (struEpoliceRs485: TNetItcEPoliceRS485Param);
      9: (struPERs485:      TNetItcEPoliceRS485Param);          
      10:(struPostMpr:      TNetItcPostMprParam);
      11:(struViaVtCoil:    TNetDvrViaVtCoilParam);
      12:(struPostImt:      TNetItcPostImtParam);
      13:(struPostPrs:      TNetItcPostPrsParam);
      14:(struIpcHvt:       TNetIpcPostHvtParam);
      15:(struHvtV50:       TNetIpcPostHvtParamV50);
  end;

我也尝试过将它作为嵌套记录(这里建议http://rvelthuis.de/articles/articles-convert.html#unions

  PNetItcSingleTriggerCfg = ^TNetItcSingleTriggerCfg;
  TNetItcSingleTriggerCfg = record
    byEnable:      Byte;
    byRes1:        array [0..2] of Byte;
    dwTriggerType: DWord;
    uTriggerParam: record
      case integer of
        0: (uLen:             array [0..1069] of DWord);
        1: (struIOSpeed:      TNetItcPostIOSpeedParam);
        2: (struSingleIO:     TNetItcPostSingleIOParam);
        3: (struPostRs485:    TNetItcPostRS485Param);
        4: (struPostRadar:    TNetItcPostRS485RadarParam);
        5: (struVtCoil:       TNetItcPostVTCoilParam);
        6: (struHvt:          TNetItcPostHvtParam);
        7: (struIOTL:         TNetItcSingleIOTLParam);
        8: (struEpoliceRs485: TNetItcEPoliceRS485Param); 
        9: (struPERs485:      TNetItcEPoliceRS485Param);
        10:(struPostMpr:      TNetItcPostMprParam);
        11:(struViaVtCoil:    TNetDvrViaVtCoilParam);
        12:(struPostImt:      TNetItcPostImtParam);
        13:(struPostPrs:      TNetItcPostPrsParam);
        14:(struIpcHvt:       TNetIpcPostHvtParam);
        15:(struHvtV50:       TNetIpcPostHvtParamV50);
      end;
    byRes:         array [0..63] of Byte;
  end;

我在这里看到过类似的问题(即How do I translate a C union into Delphi?),但我的示例中的联合位于结构的中间,据我所知,案例语句的“结束”也结束了记录。

我想我理解变体记录背后的理论,它为 case 语句中的字段分配相同的内存,所以使用的字段将是非此即彼,但我不知道如何判断DLL 正在访问这些记录,无论是 struName.unionName.fieldName 还是 struName.fieldName 以及联合是如何定义的(即 case 语句的选择器是什么,以及如何知道选择器是什么数据类型)。

我有三个相似的结构要翻译,我想如果我能破解一个,我就能破解它们。

根据上述记录,我不断收到错误消息“参数错误。 SDK API 中的输入或输出参数为 NULL,同时从 DLL 调用函数(如果您需要更多信息,请询问),这让我认为我的记录没有正确转换。

我正在使用 HCNetSDK.dll SDK 版本 5.0.3.20,如果有帮助的话,我的 IDE 是 XE7。

任何帮助将不胜感激。

【问题讨论】:

  • FWIW,更多关于转换记录和联合在这里:rvelthuis.de/articles/articles-convert.html#unions 但正如@DavidHeffernan 已经说过的,你的转换是好的,但对齐可能会关闭。那篇文章中也有关于对齐的部分。
  • 真的不是骗子。提问者进行了很好的研究,并想知道为什么生成的代码看起来不像预期的那样运行。
  • 检查C结构是否用#pragma pack编译。
  • @Rudy 我确实读过你的文章(我什至在我的问题中引用了它)——这就是让我走上正轨的原因:)

标签: c++ delphi delphi-xe ip-camera


【解决方案1】:

假设结构对齐设置匹配,那么您转换并集的两次尝试都是正确的。您可以使用您喜欢的任何一个。对于它的价值,我更喜欢第一种方法,即声明一个类型来表示联合。

无论您的实际问题是什么,它似乎都与联合转换无关。一个简单的测试方法是检查 C++ 和 Delphi 版本中类型的大小是否匹配,以及每个成员的偏移量是否匹配。

为了测试类型的大小,在 C++ 中使用 sizeof,在 Delphi 中使用 SizeOf。对于偏移量,对于 C++ 使用 offsetof 宏,对于 Delphi 使用 my answer here 中显示的技巧。

【讨论】:

  • 其实想一想:一个合适的union转换大概应该使用packed record。 AFAIK,C 标准规定所有元素必须在同一地址对齐,但没有 Delphi 标准,我不知道当前使用的所有编译器是否都是如此。打包记录确保所有部分对齐同一个地址。
  • @RudyVelthuis 我们之前已经讨论过很多次了。 Packed 强制记录对齐。这不会对布局产生影响,因为变体记录总是将变体成员放置在相同的偏移量处。但会导致结构错位。例如,包含一个 int 和一个 double 的 union 的大小为 8,对齐方式为 8。对齐时,它应始终放置在 8 字节边界上。如果你打包它,那将不会发生。如果 C++ 代码强制联合对齐为 1,则打包它,否则不要。
  • @David 感谢您花时间回答我的问题。至少现在我可以到别处看看我的错误在哪里。那么是什么定义了联合的选择器呢?
  • 没有真正的选择器。在经典的 Pascal 变体类型中,您可以在记录中写入 case ValueType: Integer,这将在变体部分之前声明一个变量。但是没有什么可以强制您引用与ValueType 的实际值相对应的字段。只需将其视为冗长的 Pascal 样板语法,您必须反刍才能建立联合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多