Search code examples
delphiwinapistructdelphi-xe2code-conversion

How to translate RETRIEVAL_POINTERS_BUFFER structure to Delphi?


I'm working with Windows API and have to recreate a structure inside a Delphi record. I think I have it down, but this one was a little confusing and I need to make sure I did this right.

Here's the original C++ structure:

typedef struct RETRIEVAL_POINTERS_BUFFER {
  DWORD         ExtentCount;
  LARGE_INTEGER StartingVcn;
  struct {
    LARGE_INTEGER NextVcn;
    LARGE_INTEGER Lcn;
  } Extents[1];
} RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;

Notice that there's an array struct within this struct. This is where I got lost. If I'm not mistaken, the Delphi version should look like this:

  TExtent = record
    NextVcn: Integer;
    Lcn: Integer;
  end;

  TExtents = array of TExtent;

  PRETRIEVAL_POINTERS_BUFFER = ^TRETRIEVAL_POINTERS_BUFFER;
  TRETRIEVAL_POINTERS_BUFFER = record
    ExtentCount: DWORD;
    StartingVcn: Integer;
    Extents: TExtents;
  end;

When I use this structure in the Windows API, it does appear to work. But, because of this structure array inside of the structure, I'm a little hesitant that I did this correctly. Does this look right?


Solution

  • The Extents field is a variable length array inlined in a struct. The actual struct will have ExtentCount elements. You cannot use a Delphi dynamic array here. In fact you can never use a Delphi dynamic array in interop.

    So, declare it as array [0..0] just as the C code does. In order to access it you'll need to disable range checking. An actual instance of this record will have valid data in indices 0..ExtentCount-1.

    For your integral types, map DWORD in C to DWORD in Delphi. And LARGE_INTEGER in C to LARGE_INTEGER in Delphi. Neither of those are the same as Delphi Integer. The former is unsigned, and the latter is 64 bits wide.

    PRetrievalPointersBuffer = ^TRetrievalPointersBuffer;
    TRetrievalPointersBuffer = record
      ExtentCount: DWORD;
      StartingVcn: LARGE_INTEGER;
      Extents: array [0..0] of record
        NextVcn: LARGE_INTEGER;
        Lcn: LARGE_INTEGER;
      end;
    end;
    

    The LARGE_INTEGER type is rather awkward to work with. You may prefer to declare those fields as Int64 instead.


    This type of struct is invariably heap allocated. The heap allocation code has to work out the size needed to fit ElementCount items in the variable length array. If you are allocating the buffer then you'll need the inner record in a separately defined type so that you can conveniently name it to pass to SizeOf. If the API allocates then you are fine as above.