Search code examples
delphiwinapidelphi-7

How to declare union inside a record structure?


I am trying to define the TWaveFormatExtensible type, but I am not sure if am I declaring correctly the Samples union. Here is the original declaration from header file (Windows SDK 10.0.17763.0):

typedef struct {
    WAVEFORMATEX    Format;
    union {
        WORD wValidBitsPerSample;       /* bits of precision  */
        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
        WORD wReserved;                 /* If neither applies, set to zero. */
    } Samples;
    DWORD           dwChannelMask;      /* which channels are */
                                        /* present in stream  */
    GUID            SubFormat;
}

And this is what I've tried:

type
  TWAVEFORMATEX = record
    wFormatTag: Word;
    nChannels: LongWord;
    nSamplesPerSec: Word;
    nAvgBytesPerSec: LongWord;
    nBlockAlign: Word;
    wBitsPerSample: Word;
    cbSize: Word;
  end;

  TWaveFormatExtensible = record
    Format: TWAVEFORMATEX;
    dwChannelMask: LongWord;
    SubFormat: Integer;
    case Word of
      0: (wValidBitsPerSample: Word;);
      1: (wSamplesPerBlock: Word;);
      2: (wReserved: Word;);
  end;

But that's not correct. How would one declare a union inside a record structure in Delphi?


Solution

  • The fields of the structure must be in the same order as in the original (C++) declaration. But there's a problem: the original declaration puts the Samples variant in the middle of the record and that is not allowed in Delphi.

    You can solve this by declaring the variant part as a separate record and then include that record as a field in the final structure.

    TWaveFormatExtensibleSamples = record
    case Word of
      0: (wValidBitsPerSample: Word;);
      1: (wSamplesPerBlock: Word;);
      2: (wReserved: Word;);
    end;
    

    and then construct the final structure:

    TWaveFormatExtensible = record
      Format: TWAVEFORMATEX;
      Samples: TWaveFormatExtensibleSamples;
      dwChannelMask: DWORD;
      SubFormat: TGUID; 
    end;
    

    edit: The documentation for records with variant parts, state:

    A record type can have a variant part, which looks like a case statement. The variant part must follow the other fields in the record declaration.

    This concerns variant parts without an enclosing record declaration.

    However, as Remy Lebeau pointed out, a record with the variant part can be directly declared in the TWaveFormatExtensible declaration as part of the structure, in between other fields:

    TWaveFormatExtensible = record
      Format: TWAVEFORMATEX;
      Samples: record
        case Word of
        0: (wValidBitsPerSample: Word;);
        1: (wSamplesPerBlock: Word;);
        2: (wReserved: Word;);
      end;
      dwChannelMask: DWORD;
      SubFormat: TGUID;
    end;
    

    So this can be used as well as the separately declared TWaveFormatExtensibleSamples record.