tags:

views:

176

answers:

3

I need help to convert this C type declaration to Delphi:

typedef struct _IO_STATUS_BLOCK {
  union {
    NTSTATUS Status;
    PVOID    Pointer_;
  } ;
  ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

Thanks in advance.

+6  A: 

From Hardlinks.pas in the JCL:

type
  IO_STATUS_BLOCK = record
    case integer of
      0:
       (Status: NTSTATUS);
      1:
       (Pointer: Pointer;
        Information: ULONG); // 'Information' does not belong to the union!
  end;
  // PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
TOndrej
+2  A: 

A C Union can be translated with a case selector inside a record, problem here is that Delphi doesn't allow anything after the Case statement. This can be solved with a nested Case statement, like this:

  _IO_STATUS_BLOCK = record
    case Integer of
      0: (
        case Integer of
        0: (
          Status: NTSTATUS);
        1: (
          Pointer_: Pointer);
      2: (Information: ULONG_PTR));
  end;
  IO_STATUS_BLOCK = _IO_STATUS_BLOCK;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;

/EDIT: To make the Offsets correct (see comments below) padding is needed, the corrected version is below. It doesn't offer much advantage here but it would if more field or even unions follow behind the first union:

  _IO_STATUS_BLOCK = record
    case Integer of
      0: (
        case Integer of
          0: (Status: NTSTATUS);
          1: (Pointer_: Pointer));
      1: (
        Pad: DWORD;
        Information: ULONG_PTR);
  end;
  IO_STATUS_BLOCK = _IO_STATUS_BLOCK;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;

/EDIT2: A better version looks like this:

  _IO_STATUS_BLOCK = record
    case Integer of
      0: (
        Status: NTSTATUS;
        Pointer_: Pointer);
      1: (
        case Padding: DWORD of
          0: (
            Information: ULONG_PTR));
  end;
  IO_STATUS_BLOCK = _IO_STATUS_BLOCK;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
Remko
In this record the Union and 'Information' should occupy the same space and the record size would be 4 instead of 8. Test it, set anything to 'Information' and read it from 'Status'. IOW it's the same thing with a case statement with 3 expressions, and I think that's why the compiler is complaining of a 'duplicate tag value'.
Sertac Akyuz
@Tondrej: I typed it without testing in the compiler, I get the same error and same fix as you (edited the answer).
Remko
@Sertac: I think you're right.
TOndrej
@Sertac Akyuz: SizeOf returns 4 for this record, also when I set Status to 1 then both Pointer_ and Information are $1 so imho all should be okay?
Remko
@Remko: The problem is that the correct translation should be: (offset 0): Status/Pointer_, followed by (offset 4): Information, altogether 8 bytes.
TOndrej
@Remko - No. Forgive my English, I couldn't tell what I intended to.. 'Information' is an independent field, refer to TOndrej's comment.
Sertac Akyuz
@Sertec and @TOndrej: see my edits, the last example makes clear that Information does not belong to the union and would make it easy to add more field if this were needed. Sadly I don't see a way to loose the padding.
Remko
@Remko - In your last record 'Status' and 'Pointer_' are not sharing the same memory, there *have* to be a 'Case' around them. Instead 'Status' is sharing memory with 'Padding' and 'Pointer_' is with 'Information'. The previous one was correct AFAICS.
Sertac Akyuz
@Sertec: Oh how could I miss that, you are right obviously!
Remko
+3  A: 

In addition to the other posts, you might want to read Pitfalls of Conversion. Rudy's articles are a goldmine of information regarding Delphi / C / C++ interoperability.

Mihai Limbășan