2009-01-19 29 views
9

Bạn sẽ cần một máy 64bit nếu bạn muốn xem ngoại lệ actuall. Tôi đã tạo ra một số lớp giả mà repro của vấn đề.TypeLoadException trên x64 nhưng tốt trên x86 với structlayouts

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
    public class InnerType 
    { 
     char make; 
     char model; 
     UInt16 series; 
    } 

[StructLayout(LayoutKind.Explicit)] 
    public class OutterType 
    { 
     [FieldOffset(0)] 
     char blah; 

     [FieldOffset(1)] 
     char blah2; 

     [FieldOffset(2)] 
     UInt16 blah3; 

     [FieldOffset(4)] 
     InnerType details; 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var t = new OutterType(); 
      Console.ReadLine(); 
     } 
    } 

Nếu tôi chạy này trên 64 clr, tôi nhận được một ngoại lệ kiểu tải,

System.TypeLoadException was unhandled 
    Message="Could not load type 'Sample.OutterType' from assembly 'Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field." 

Nếu tôi buộc các cpu mục tiêu đến 32, nó hoạt động tốt.

Ngoài ra, nếu tôi thay đổi InnerType từ một lớp thành cấu trúc, nó cũng hoạt động. Ai đó có thể giải thích những gì đang xảy ra hoặc những gì tôi đang làm sai?

nhờ

+0

Hãy ghi nhớ rằng char là 16-bit trong .NET. blah và blah2 chồng lên nhau một phần, cũng như blah2 và blah3. – dalle

Trả lời

19

Phần về các loại chồng chéo gây hiểu lầm ở đây. Vấn đề là trong các kiểu tham chiếu .Net phải luôn được căn chỉnh trên ranh giới kích thước con trỏ. Công đoàn của bạn hoạt động trong x86 vì bù trừ trường là 4 byte, là kích thước con trỏ cho hệ thống 32 bit nhưng không thành công trên x64 vì nó phải được bù trừ bội số của 8. Điều tương tự sẽ xảy ra nếu bạn đặt độ lệch thành 3 hoặc 5 trên nền tảng x86.

EDIT: Đối với những người hoài nghi - Tôi không thể tìm thấy một tài liệu tham khảo đã sẵn sàng trên internet nhưng hãy kiểm tra Expert .NET 2.0 IL Assembler By Serge Lidin trang 175.

+0

Câu trả lời hay. Rõ ràng, súc tích. Hoàn hảo. – Aaron

0

Nếu bạn đang muốn ra cấu trúc bên trong cấu trúc khác mà bản thân họ Layoutind.Explict bạn Nên sử dụng giá trị Kích thước rõ ràng (theo byte) nếu bạn mong đợi chúng hoạt động ở các chế độ bit khác nhau (hoặc trên các máy có yêu cầu đóng gói khác nhau) Điều bạn đang nói có "sắp xếp thứ tự tuần tự và không gói trong nội bộ mà sử dụng nhiều không gian như bạn thích ở cuối ". Nếu bạn không chỉ định Kích thước thời gian chạy là miễn phí để thêm nhiều không gian như nó thích.

Lý do từ chối nói chung không cho phép cấu trúc và loại đối tượng chồng lên nhau là thói quen GC phải được tự do di chuyển qua biểu đồ đối tượng trực tiếp. Trong khi làm điều này nó không thể biết nếu một trường hợp (chồng chéo) có ý nghĩa như là một tham chiếu đối tượng hoặc là bit thô (nói một int hoặc một phao). Vì nó phải duyệt qua tất cả các tham chiếu đối tượng trực tiếp để xử lý chính xác nó sẽ kết thúc các bit 'ngẫu nhiên' có thể trỏ bất kỳ vị trí nào trong heap (hoặc ra khỏi nó) như thể chúng là tài liệu tham khảo trước khi bạn biết .

Vì tham chiếu 32/64 sẽ chiếm 32 hoặc 64 bit theo thời gian chạy, bạn phải sử dụng Explict, chỉ tham chiếu liên kết với tham chiếu và loại giá trị có loại giá trị, đảm bảo các loại tham chiếu của bạn được căn chỉnh với ranh giới của cả hai nền tảng đích nếu chúng khác nhau (Lưu ý: Thời gian chạy phụ thuộc xem bên dưới) và thực hiện một trong các thao tác sau:

  1. Đảm bảo rằng tất cả các trường tham chiếu là mục cuối cùng trong cấu trúc - sau đó tự do làm cho cấu trúc lớn hơn/nhỏ hơn tùy thuộc vào bitness của môi trường thời gian chạy.
  2. Force tất cả các đối tượng tham chiếu đến tiêu thụ 64bits cho dù bạn đang ở trên một 32 hoặc 64bit môi trường

Lưu ý về sự liên kết: Apologies tôi là do lỗi trên các lĩnh vực tài liệu tham khảo unaligned - trình biên dịch loại bỏ các tải loại trừ khi tôi thực hiện một số hành động với cấu trúc.

[StructLayout(LayoutKind.Explicit)] 
public struct Foo 
{ 
    [FieldOffset(0)] 
    public byte padding; 
    [FieldOffset(1)] 
    public string InvalidReference; 
} 

public static void RunSnippet() 
{ 
    Foo foo; 
    foo.padding = 0; 
    foo.ValidReference = "blah"; 
    // Console.WriteLine(foo); // uncomment this to fail 
} 

Các chi tiết liên quan đang trong ECMA đặc điểm kỹ thuật http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf Xem phần 16.6.2 mà có quy định thành sự liên kết của các giá trị kích thước bản địa bao gồm &. Nó lưu ý rằng hướng dẫn tiền tố chưa ký hiệu tồn tại để làm việc vòng này nếu được yêu cầu.

Tuy nhiên, trên mono (cả OSX intel lẫn Win32 intel 32 bit), mã trên hoạt động.Hoặc là thời gian chạy không tôn trọng bố cục và âm thầm 'đúng' mọi thứ hoặc nó cho phép liên kết tùy ý (trong lịch sử chúng ít linh hoạt hơn thời gian chạy MS trong vấn đề này là đáng ngạc nhiên). Biểu mẫu trung gian CLI được tạo ra bởi mono không bao gồm bất kỳ tiền tố lệnh được chỉ định nào, vì vậy nó dường như không phù hợp với thông số kỹ thuật.

Điều đó sẽ dạy tôi chỉ kiểm tra mono.

+0

Nếu bạn chạy ví dụ RunSnippet của bạn, bạn sẽ nhận được cùng một TypeLoadException như trên. –

3

Tôi cũng đã nhận thấy rằng bạn đang đóng gói kiểu dữ liệu char của bạn thành 1 byte. Các loại Char trong .NET có kích thước 2 byte. Tôi không thể xác minh nếu đây là vấn đề thực tế, nhưng tôi sẽ kiểm tra lại điều đó.

0

Tôi gặp khó khăn với cùng một vấn đề và ghét tôi không thể tìm thấy một tham chiếu rõ ràng về chủ đề này trên MSDN. Sau khi đọc câu trả lời ở đây, tôi bắt đầu tập trung tôi vào sự khác biệt x86 và x64 trong .NET và tìm thấy những điều sau đây: Migrating 32-bit Managed Code to 64-bit. Ở đây họ nêu rõ rằng con trỏ là 4 byte trên x86 và 8 byte trên x64. Hy vọng nó có thể hữu ích cho người khác.

Có rất nhiều câu hỏi liên quan ở đây trên Stack Overflow. Tôi sẽ thêm hai trong số họ, đề cập đến những điều thú vị khác.

Các vấn đề liên quan