2016-06-04 16 views
6

Tôi đang cố gắng tạo một số tệp vhd/vhdx bằng cách sử dụng API VHD trong C#.Liên minh C++ trong C# - hành vi kỳ lạ

Có một C++ đoàn trông như thế này:

typedef struct _CREATE_VIRTUAL_DISK_PARAMETERS 
{ 
    CREATE_VIRTUAL_DISK_VERSION Version; 

    union 
    { 
     struct 
     { 
      GUID     UniqueId; 
      ULONGLONG    MaximumSize; 
      ULONG     BlockSizeInBytes; 
      ULONG     SectorSizeInBytes; 
      PCWSTR    ParentPath; 
      PCWSTR    SourcePath; 
     } Version1; 

     struct 
     { 
      GUID     UniqueId; 
      ULONGLONG    MaximumSize; 
      ULONG     BlockSizeInBytes; 
      ULONG     SectorSizeInBytes; 
      ULONG     PhysicalSectorSizeInBytes; 
      PCWSTR     ParentPath; 
      PCWSTR     SourcePath; 
      OPEN_VIRTUAL_DISK_FLAG OpenFlags; 
      VIRTUAL_STORAGE_TYPE ParentVirtualStorageType; 
      VIRTUAL_STORAGE_TYPE SourceVirtualStorageType; 
      GUID     ResiliencyGuid; 
     } Version2; 

     struct 
     { 
      GUID     UniqueId; 
      ULONGLONG    MaximumSize; 
      ULONG     BlockSizeInBytes; 
      ULONG     SectorSizeInBytes; 
      ULONG     PhysicalSectorSizeInBytes; 
      PCWSTR     ParentPath; 
      PCWSTR     SourcePath; 
      OPEN_VIRTUAL_DISK_FLAG OpenFlags; 
      VIRTUAL_STORAGE_TYPE ParentVirtualStorageType; 
      VIRTUAL_STORAGE_TYPE SourceVirtualStorageType; 
      GUID     ResiliencyGuid; 
      PCWSTR     SourceLimitPath; 
      VIRTUAL_STORAGE_TYPE BackingStorageType; 
     } Version3; 
    }; 
} CREATE_VIRTUAL_DISK_PARAMETERS, *PCREATE_VIRTUAL_DISK_PARAMETERS; 

Tôi đang cố gắng để chuyển đổi đó đến C#, nhưng không có nhiều may mắn. Tôi không quan tâm đến Version3 chút nào, vì vậy tôi bỏ nó ra.

Tôi đã thử một số thứ và tốt nhất tôi có thể làm là sử dụng phiên bản 2 (bằng cách làm điều gì đó kỳ lạ), nhưng tôi chưa bao giờ quản lý phiên bản 1 và Version2 cùng một lúc.

Các giải pháp mà đã nắm giữ các kết quả tốt nhất cho đến nay đã được điều này, nhưng phải có một cái gì đó sai đó vì version1 chỉ đơn giản là không làm việc, và SectorSizeInBytes trong version1 là một ulong hơn uint (nếu tôi thay đổi nó để uint như nó phải là, tôi phá vỡ Version2 và version1 vẫn không hoạt động!)

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParameters 
{ 
    [FieldOffset(0)] public CreateVirtualDiskParametersVersion1 Version1; 

    [FieldOffset(0)] public CreateVirtualDiskParametersVersion2 Version2; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParametersVersion1 
{ 
    public CreateVirtualDiskVersion Version; 
    public Guid UniqueId; 
    public ulong MaximumSize; 
    public uint BlockSizeInBytes; 
    public ulong SectorSizeInBytes; 
    public string ParentPath; 
    public string SourcePath; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParametersVersion2 
{ 
    public CreateVirtualDiskVersion Version; 
    public Guid UniqueId; 
    public ulong MaximumSize; 
    public uint BlockSizeInBytes; 
    public uint SectorSizeInBytes; 
    public uint PhysicalSectorSizeInBytes; 
    public string ParentPath; 
    public string SourcePath; 
    public OpenVirtualDiskFlags OpenFlags; 
    public VirtualStorageType ParentVirtualStorageType; 
    public VirtualStorageType SourceVirtualStorageType; 
    public Guid ResiliencyGuid; 
} 

tôi biết lý thuyết lĩnh vực Version nên được đặt bên ngoài các cấu trúc bản và tôi đã cố gắng đó là tốt, nhưng nó chỉ phá vỡ những thứ thậm chí còn vui hơn ...

Vì vậy, ai đó có thể tư vấn làm thế nào để đúng cách dịch các bên trên để C#, rời khỏi cấu trúc Version3 như đó là không cần thiết?

+0

'ULONGLONG (64 bit)' và 'ULONG (32 bit)' cả hai dường như được ánh xạ tới 'ulong (64 bit)'. – AlexD

+0

Bạn đã thử in các offset của các thành viên trong cấu trúc C++ của bạn chưa? –

+0

@TheodorosChatzigiannakis không hoàn toàn chắc chắn làm thế nào tôi sẽ làm điều đó mang trong tâm trí nó chứa enums/structs và dây. Ý tôi là, kích thước của chúng là bao nhiêu? Tôi đang mở để thử nó, cho tôi biết những gì giá trị để sử dụng và tôi sẽ cho nó một shot. – cogumel0

Trả lời

1

Sử dụng Pack = 1 đến StructLayout thuộc tính loại bỏ mọi khoảng đệm giữa struct thành viên. Trong kết nối TCP cấu trúc thường được thông qua xung quanh mà không có đệm để tất cả các chương trình bằng cách sử dụng cấu trúc có thể đồng ý về bố trí của nó trong bộ nhớ.

Tuy nhiên như @ David Heffernan đã chỉ ra rằng, có thể không phải là trường hợp khi truyền cấu trúc cho Windows DLL. Tôi đã không kiểm tra cuộc gọi thực tế đến CreateVirtualDisk bởi vì nó có vẻ hơi mạo hiểm, vì tôi chưa từng sử dụng cuộc gọi này trước đây và không muốn làm mất cắp đĩa của tôi nếu tôi mắc lỗi. Dường như đóng gói mặc định là 8 byte (Pack = 0 cho mặc định hoặc Pack = 8) có thể là cài đặt chính xác, dựa trên báo giá sau.

Xem 64-bit Windows API struct alignment caused Access Denied error on named pipe

SDK Windows dự kiến ​​đóng gói là 8 byte. Từ Using the Windows Headers

Dự án cần được biên dịch để sử dụng đóng gói cấu trúc mặc định, hiện là 8 byte vì loại tích phân lớn nhất là 8 byte. Làm như vậy đảm bảo rằng tất cả các loại cấu trúc trong các tệp tiêu đề được biên dịch vào ứng dụng với cùng một căn chỉnh mà Windows API mong đợi. Nó cũng đảm bảo rằng các cấu trúc với các giá trị 8 byte được căn chỉnh đúng và sẽ không gây ra lỗi liên kết trên các bộ xử lý thực thi căn chỉnh dữ liệu.

Version được chuyển lên đầu CreateVirtualDiskParameters. Hai đoàn thể sau đó làm theo. Cả hai đều có cùng độ lệch sizeof(CREATE_VIRTUAL_DISK_VERSION).

Ngoài ra SectorSizeInBytesuint thay vì ulong.

Bạn có thể để marshaller làm công việc của điền string thành viên sử dụng thuộc tính, ví dụ như

[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath; 

Hoặc, bạn có thể đại diện cho nó như nó xuất hiện trong bộ nhớ, mà là một con trỏ đến một chuỗi Unicode:

public IntPtr ParentPath; 

và sau đó trích xuất các chuỗi mình với

Marshal.PtrToStringAuto(vdp.Version1.ParentPath) 

Nếu bạn đang đi qua C# struct để một DLL bên ngoài, cư nó với một chuỗi không được quản lý

vdp.Version1.ParentPath = (IntPtr)Marshal.StringToHGlobalAuto("I am a managed string"); 

sau đó giải phóng chuỗi không được quản lý khi bạn đã hoàn tất với nó

Marshal.FreeHGlobal(vdp.Version1.ParentPath); 

Hãy thử điều này.

public enum CREATE_VIRTUAL_DISK_VERSION 
{ 
    CREATE_VIRTUAL_DISK_VERSION_UNSPECIFIED = 0, 
    CREATE_VIRTUAL_DISK_VERSION_1 = 1, 
    CREATE_VIRTUAL_DISK_VERSION_2 = 2 
}; 
public enum OPEN_VIRTUAL_DISK_FLAG 
{ 
    OPEN_VIRTUAL_DISK_FLAG_NONE = 0x00000000, 
    OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS = 0x00000001, 
    OPEN_VIRTUAL_DISK_FLAG_BLANK_FILE = 0x00000002, 
    OPEN_VIRTUAL_DISK_FLAG_BOOT_DRIVE = 0x00000004, 
    OPEN_VIRTUAL_DISK_FLAG_CACHED_IO = 0x00000008, 
    OPEN_VIRTUAL_DISK_FLAG_CUSTOM_DIFF_CHAIN = 0x00000010 
}; 

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] 
public struct VIRTUAL_STORAGE_TYPE 
{ 
    uint DeviceId; 
    Guid VendorId; 
}; 

[StructLayout(LayoutKind.Explicit, Pack = 8, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParameters 
{ 
    [FieldOffset(0)] 
    public CREATE_VIRTUAL_DISK_VERSION Version; 

    [FieldOffset(8))] 
    public CreateVirtualDiskParametersVersion1 Version1; 

    [FieldOffset(8))] 
    public CreateVirtualDiskParametersVersion2 Version2; 
} 

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParametersVersion1 
{ 
    public Guid UniqueId; 
    public ulong MaximumSize; 
    public uint BlockSizeInBytes; 
    public uint SectorSizeInBytes; 
    //public IntPtr ParentPath; // PCWSTR in C++ which is a pointer to a Unicode string 
    //public IntPtr SourcePath; //string 
    [MarshalAs(UnmanagedType.LPWStr)] public string ParentPath; 
    [MarshalAs(UnmanagedType.LPWStr)] public string SourcePath; 
} 

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] 
public struct CreateVirtualDiskParametersVersion2 
{ 
    public Guid UniqueId; 
    public ulong MaximumSize; 
    public uint BlockSizeInBytes; 
    public uint SectorSizeInBytes; 
    public uint PhysicalSectorSizeInBytes; 
    //public IntPtr ParentPath; //string 
    //public IntPtr SourcePath; //string 
    [MarshalAs(UnmanagedType.LPWStr)] public string ParentPath; 
    [MarshalAs(UnmanagedType.LPWStr)] public string SourcePath; 
    public OPEN_VIRTUAL_DISK_FLAG OpenFlags; 
    public VIRTUAL_STORAGE_TYPE ParentVirtualStorageType; 
    public VIRTUAL_STORAGE_TYPE SourceVirtualStorageType; 
    public Guid ResiliencyGuid; 
} 
+0

Cảm ơn bạn John D thật tuyệt, nhưng tôi có thể hỏi tại sao '[FieldOffset (sizeof (CREATE_VIRTUAL_DISK_VERSION))]'? 'CREATE_VIRTUAL_DISK_VERSION' là một enum (của int) vì vậy nó phải có kích thước 4 byte ngay bất kể bitness là gì? – cogumel0

+0

Các đường ống thường được căn chỉnh và không được đóng gói. Bạn chỉ cần làm điều này? –

+0

@cogumeIO Đó là sự thật hiện tại - nó đã cố gắng để xử lý các trường hợp mà 'CREATE_VIRTUAL_DISK_VERSION' có thể thay đổi (ví dụ như một số phiên bản lớn và một số nhỏ). Trong trường hợp đó, bạn sẽ thay thế bằng 'struct'. Định nghĩa struct C++ sử dụng 'CREATE_VIRTUAL_DISK_VERSION' thay vì' ULONG' để nó là một khả năng. –