Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert a C++ union to Delphi

I know this has been asked before, but I am unsuccessfully trying to convert some C++ structures/unions to Delphi to use the Hikvision SDK.

The C++ structures/unions I’m trying to convert are as follow:

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;

I have tried the following:

  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;

And I have also tried having it as a nested record (as suggested here 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;

I have seen similar questions on here (i.e. How do I translate a C union into Delphi? ), but the union in my example is in the middle of the structure, and as I understand the ‘end’ of the case statement also ends the record.

I think I understand the theory behind a variant record having the same memory allocation for the fields in the case statement, so the fields used would be either/or, but what I can’t work out is how to tell how the DLL is accessing these records, whether it is struName.unionName.fieldName or struName.fieldName and also how the union is defined (i.e. what is the selector of the case statement, and how to know what data type the selector is).

I have three similar structures to translate, and figure if I can crack one, I can crack them all.

With the records as described above, I keep getting the error message ‘Parameter error. Input or output parameter in the SDK API is NULL’ whilst calling a function from the DLL (if you need more information on this, please ask), which makes me think that my records have not been converted correctly.

I’m using the HCNetSDK.dll SDK version 5.0.3.20 and my IDE is XE7 if that helps.

Any help would be appreciated.

like image 727
tobi-wan Avatar asked Aug 25 '16 12:08

tobi-wan


1 Answers

Assuming that structure alignment settings match then both of your attempts to convert the union are correct. You can use whichever one you prefer. For what it is worth, I would prefer the first approach where you declare a type to represent the union.

Whatever your actual problem is, it does not appear to lie with the union conversion. A simple way to test this is to check that the size of the types match in the C++ and Delphi versions, and that the offsets to each member match.

For test the size of a type use sizeof in C++ and SizeOf in Delphi. For the offsets use the offsetof macro for C++ and the trick shown in my answer here for Delphi.

like image 59
David Heffernan Avatar answered Nov 06 '22 08:11

David Heffernan