2015-09-15 19 views
5

Hãy nói rằng tôi có cấu trúc như sau trong C++C# marshaling C++ struct thừa kế

struct Base 
{ 
    USHORT size; 
} 

struct Inherited : public Base 
{ 
    BYTE type; 
} 

Tôi muốn marshal Inherited trong C# nhưng thừa kế struct không hoạt động trong C#. Làm như sau thích hợp?

public interface IBase 
{ 
    ushort Size { get; set; } 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct Inherited : IBase 
{ 
    public ushort Size { get; set; } 
    public byte Type { get; set; } 
} 

Tôi đã đơn giản hóa vấn đề ở đây và các cấu trúc của tôi lớn hơn khiến việc xác thực kết quả trở nên khó khăn hơn. Ngoài ra, các cấu trúc đến từ một phần mềm khác không phải là tài liệu quá tốt khiến việc xác thực kết quả thậm chí còn khó hơn nữa. Khi sử dụng thừa kế trong C + +, là các trường lớp cơ sở trước hoặc sau cấu trúc con?

Tôi đang sử dụng IBase làm cách để thực thi các trường cơ sở.

Thật không may, tôi không có quyền kiểm soát bên C++ (SDK cho hệ thống bên ngoài mà tôi tích hợp với).

+0

Tôi nghĩ rằng nó không phải là bất hợp lý để mong đợi cơ sở cơ bản để đến đầu tiên trong bố trí bộ nhớ C++, như thể nó là thành viên đầu tiên (nhưng tôi không nghĩ rằng nó được chuẩn hóa). Bạn đã thử chưa? –

+0

@TheodorosChatzigiannakis Có tất nhiên tôi đã thử và chạy mã nhưng như tôi đã nói: "[...] cấu trúc của tôi là cách lớn hơn làm cho nó khó khăn để xác nhận kết quả." Đó là lý do tại sao tôi đang cố gắng để xác nhận hypotesis của tôi. Cảm ơn bạn đã nhập! – KOTIX

+0

Có lẽ bạn nên sử dụng các trường thay vì các thuộc tính, nhưng tôi không biết điều này có hiệu quả hay không. –

Trả lời

2

Từ "thích hợp" không áp dụng chính xác cho các khai báo C# này. Đến nay cách tốt nhất để tránh tai nạn là bởi không dựa vào chi tiết triển khai thuộc tính và giao diện. Cấu trúc này phải được khai báo nội bộ và chỉ sử dụng các trường đơn giản.

Đoạn mã không thể hiện chế độ lỗi vì vậy tôi sẽ phải giả định rằng đây là phiên bản đơn giản của tuyên bố thực sự rằng có sự cố. Cách kiểm tra xem mã C# có được quyền khai báo cấu trúc hay không là xác minh rằng kích thước của cấu trúc và độ lệch của trường cuối cùng là giống nhau trong cả C++ và C#. Bắt đầu bằng cách viết một chương trình kiểm tra ít để kiểm tra xem, C++ phiên bản dành cho đoạn này sẽ trông như thế này:

#include <Windows.h> 
#include <stddef.h> 

struct Base { 
    USHORT size; 
}; 

struct Inherited : public Base { 
    BYTE type; 
}; 


int main() 
{ 
    int len = sizeof(Inherited); 
    int ofs = offsetof(Inherited, type); 
    return 0; 
} 

Và sử dụng các trình gỡ lỗi để kiểm tra việc lenofs biến, 4 và 2 trong này trường hợp. Thực hiện chính xác điều tương tự trong C#:

using System; 
using System.Runtime.InteropServices; 

class Program { 
    static void Main(string[] args) { 
     var len = Marshal.SizeOf(typeof(Inherited)); 
     var ofs = Marshal.OffsetOf(typeof(Inherited), "<Type>k__BackingField"); 
    } 
} 
public interface IBase { 
    ushort Size { get; set; } 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct Inherited : IBase { 
    public ushort Size { get; set; } 
    public byte Type { get; set; } 
} 

Vẫn 4 và 2, do đó, kết hợp hoàn hảo và pinvoke phải tốt. Khi bạn nhận được một sự không phù hợp trên khai báo thực sự, làm việc theo cách của bạn lùi về biến số , bạn sẽ khám phá ra các thành viên đã được tuyên bố sai. Do lưu ý hậu quả của việc sử dụng thuộc tính, buộc phải kiểm tra tên chiến thắng của trường sao lưu. Mã này sẽ ít phức tạp hơn khi cấu trúc được khai báo bằng cách sử dụng các trường thay vì thuộc tính, được khuyến khích mạnh mẽ.

1

Bạn đang đưa ra giả định về cách trình biên dịch C++ sẽ bố trí lớp trong bộ nhớ. Tùy thuộc vào trình biên dịch của bạn & những cờ bạn đang sử dụng, điều này có thể thay đổi. Cũng tùy thuộc vào các lĩnh vực bạn đang sử dụng. Ví dụ, một số trình biên dịch sẽ được hoàn toàn hợp lý để sắp xếp một đối tượng như thế này:

struct Obj 
{ 
    char c; // <-- starts at 0 byte 
    int i; // <-- starts at 4 byte - 4 byte alignment improves performance. 
} 

Vì vậy, bạn có thể thấy cách C++ lớp có thể không lập bản đồ như thế nào bạn mong đợi họ được đặt ra trong C#.

Có cờ để kiểm soát điều này - bạn có thể đặt đóng gói trong C++ thành 0, sau đó sử dụng bố cục tuần tự trong C#, và sau đó cách tiếp cận của bạn là hợp lý.

Việc sử dụng thuộc tính của bạn không phải là vấn đề lớn - miễn là bạn hiểu cách quan trọng và quan trọng hơn là trong đó trường sao lưu ẩn sẽ được trình biên dịch tạo ra cho bạn.

0

Cuối cùng tôi đã tìm thấy vấn đề thực sự trong những gì tôi đang cố gắng làm. Gọi lại tôi có từ SDK tôi đang sử dụng gửi cho tôi số struct Base và bằng cách phân tích các trường bên trong, tôi xác định loại được thừa kế. Sau đó, tôi phải "đúc" loại cơ sở cho loại được kế thừa. Đây là những gì tôi đã làm ban đầu:

static T CopyStruct<T>(ref object s1) 
{ 
    GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned); 
    T typedStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    handle.Free(); 
    return typedStruct; 
} 

Cách làm này sẽ không bao giờ ra ngoài kích thước của struct Base. Do đó, tất cả các trường bổ sung của loại Inherited sẽ không được khởi tạo chính xác (đọc bên ngoài bộ nhớ được sao chép). Tôi đã kết thúc làm việc đó một cách không an toàn như sau:

fixed (Base* basePtr= &base) 
{ 
    inherited= *(Inherited*) basePtr; 
} 

Bằng cách này, inherited điểm vào khối nhớ ban đầu và có thể đọc bên ngoài kích thước base.

Cảm ơn tất cả câu trả lời trước của bạn! Tôi thực sự đã xây dựng một ứng dụng C++ để xác nhận kích thước và bù đắp của tất cả các mô hình C++ mà tôi có.