首页 文章

定义可变长度结构并向其投射

提问于
浏览
2

在C中,您有时会看到以下内容:

struct foobar
{
    int size;
    int data[1];
};

其中 data 成员不是't really have just one element; rather it' s意味着可变长度 . 如果你在D中做了类似的事情,它是否会让你,例如,阅读 myfoobar.data[4] ?我知道D有可变长度数组,例如 int[] myvarlenintarray; ,但如果你说're trying to interface with some code that already puts out a data structure in memory like the one above, and possibly much more complex than that? Let'在 int[3000] buffer; 的第一部分呢 . 是否有一种简单的方法可以将其转换为可用的结构而无需在内存中移动它?如果没有,是否有一种简单的方法可以将数据转换为类似的结构而无需手动解析结构的每个成员?

编辑:

我想我需要举一个实际的例子,以便你看到我来自哪里 .

import std.c.windows.windows;
import std.utf;
import std.stdio;

public struct REPARSE_DATA_BUFFER
{
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  union
  {
    struct SymbolicLinkReparseBuffer
    {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      ULONG Flags;
      WCHAR[1] PathBuffer;
    }
    SymbolicLinkReparseBuffer mySymbolicLinkReparseBuffer;
    struct MountPointReparseBuffer
    {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR[1] PathBuffer;
    }
    MountPointReparseBuffer myMountPointReparseBuffer;
    struct GenericReparseBuffer
    {
        UCHAR[1] DataBuffer;
    }
    GenericReparseBuffer myGenericReparseBuffer;
  }
}
alias REPARSE_DATA_BUFFER* PREPARSE_DATA_BUFFER;
enum MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16*1024;

// Values for 'ReparseTag' member of REPARSE_DATA_BUFFER:
enum : DWORD {
    IO_REPARSE_TAG_SYMLINK = 0xA000000C,
    IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 // which also defines a Junction Point
}
enum DWORD FSCTL_GET_REPARSE_POINT = 0x000900a8;
enum FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;

public extern(Windows) BOOL function(HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*) DeviceIoControl;

void main()
{
    DeviceIoControl = cast(BOOL function(HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*))GetProcAddress(LoadLibraryA("kernel32.dll"), "DeviceIoControl");
    auto RPHandle = CreateFileW((r"J:\Documents and Settings").toUTF16z(), 0, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT + FILE_FLAG_BACKUP_SEMANTICS, null);
    if (RPHandle == INVALID_HANDLE_VALUE)
    {
        printf("CreateFileW failed with error code %d.", GetLastError());
        return;
    }
    BYTE[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] reparsebuffer;
    uint reparsedatasize;
    auto getreparsepointresult = DeviceIoControl(RPHandle, FSCTL_GET_REPARSE_POINT, null, 0, cast(void*) reparsebuffer.ptr, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &reparsedatasize, null);
    if (getreparsepointresult == 0)
    {
        printf("DeviceIoControl with FSCTL_GET_REPARSE_POINT failed with error code %d.", GetLastError());
        return;
    }
    // Now what?
    // If I do this:
    auto ReparseDataPtr = cast(REPARSE_DATA_BUFFER*) reparsebuffer.ptr;
    printf("%d == %d\n", reparsebuffer.ptr, ReparseDataPtr); // Alright, data hasn't been copied.
    // But what good is a pointer?  Can I use a pointer to a struct to access one of its members apart from dereferencing?
    printf("%d == %d\n", &reparsebuffer[0], &(*ReparseDataPtr)); // Here, I dereference ReparseDataPtr, but nothing moves.
    printf("%d == %d\n", &reparsebuffer[0], &((*ReparseDataPtr).ReparseTag)); // Same here, so I can access members in a roundabout way.
    printf("%d == %d\n", &reparsebuffer[0], &(ReparseDataPtr.ReparseTag)); // And thanks to Jim's comment, here's a less roundabout way.
    auto ReparseData = *ReparseDataPtr; // But if I assign a name to the dereferenced ReparseDataPtr, 
    printf("%d != %d\n", &reparsebuffer[0], &(ReparseData.ReparseTag)); // the data is copied to a new location, leaving most of PathBuffer behind.
    REPARSE_DATA_BUFFER ReparseDataFn() {return *ReparseDataPtr;} // Similarly, this way
    printf("%d != %d\n", &reparsebuffer[0], &(ReparseDataFn().ReparseTag)); // copies stuff to a new location.

}

首先,对于我没有给出 *ReparseDataPtr 名称的情况,我不同意 .

其次,有没有办法让一个符号的类型是REPARSE_DATA_BUFFER,其数据位于reparsebuffer.ptr?

2 回答

  • 1

    您是否尝试在D中完成与在C中完全相同的操作?

    struct foobar { int size; int data[1]; };
    

    它工作...只需使用 data.ptr 而不是 data 来访问元素,否则它将执行长度为1的边界检查 .

  • 3

    您可以通过帮助方法访问它:

    struct foobar
    {
     public:
      int[] Data() { return data.ptr[0..size]; }
    
     private:
      int size;
      int data[1];
    }
    

    您可能还想在 foobar 的成员上放置一个静态 foreach ,它使用 static assert 来确保每个的偏移量小于 data 的偏移量 .

相关问题