2010-01-09 84 views
1

我正在通過P/Invoke使用非託管庫,它使用三個結構(雖然它們都具有相同的基本佈局,所以我只會發佈一個):託管和非託管結構的大小不一樣

struct Agraph_t { 
    int tag:4; 
    int kind:4; 
    int handle:24; 
    char **attr; 
    char *didset; 
    char *name; 
    Agdata_t *univ; 
    Dict_t *nodes, *inedges, *outedges; 
    Agraph_t *root; 
    Agnode_t *meta_node; 
    Agproto_t *proto; 
    Agraphinfo_t u; 
}; 

由於我的包裝使用這些對象的方式,我必須參考Agraph_t中的結構爲IntPtr s。我添加了訪問比特字段值的屬性。

public struct Agraph_t { 
    public uint tag_kind_handle; 
    public IntPtr attr; 
    public string didset; 
    public string name; 
    public IntPtr univ; 
    public IntPtr nodes, inedges, outedges; 
    public IntPtr root; 
    public IntPtr meta_node; 
    public IntPtr proto; 
    public IntPtr u; 

    public uint Tag { 
     get { return (tag_kind_handle & 15u); } 
    } 

    public uint Kind { 
     get { return (tag_kind_handle & 240u)/16; } 
    } 

    public uint Handle { 
     get { return (tag_kind_handle & 4294967040u)/256; } 
    } 
} 

在做任何事情之前,我必須通過給三個結構體中每一個的大小初始化非託管庫。

aginitlib(Marshal.SizeOf(typeof(Agraph_t)), ..., ...); 

這樣做時我不會收到錯誤,我可以使用該庫就好了。但是,庫的一部分使用非託管結構的大小自己調用aginitlib(我無法控制這個)。此時,圖書館警告說,它已經初始化爲兩種不同的尺寸,這使得它不穩定(在某些操作之後拋出AccessViolationException)。

是否將添加的屬性考慮在結構的大小中並使其大於非託管版本?我會刪除它們,看看會發生什麼,但是我的代碼在很大程度上依賴於它們,這使得這很難。

我需要使用StructLayoutAttributeSize屬性嗎?唯一令我困惑的是IntPtr s。庫是嚴格的32位,所以我可以繼續,並安全地假設這些字段將始終是32位?

回答

1

區別是因爲u的聲明。非託管聲明有這樣的:

Agraphinfo_t u; 

這意味着Agraphinfo_t在Agraph_t結構直列分配。如果Agraphinfo_t的大小是16字節,那麼它對sizeof(Agraph_t)貢獻16個字節。

然而,在你的管理聲明,聲明ü這樣的:

public IntPtr u; 

這意味着指針在Agraph_t結構分配。在32位系統上,這將對sizeof(Agraph_t)貢獻4個字節。因此,爲Agraph_t計算的兩種尺寸不同步。

要解決此問題,聲明管理等同於Agraphinfo_t和創造Agraph_t的該實例:

public struct Agraphinfo_t 
{ 
    // fields go here as per unmanaged definition 
} 

public struct Agraph_t 
{ 
    // .... 
    public Agraphinfo_t u; 
} 
+0

哦,我明白了。我沒有注意到缺少'*'。這個解決方案的問題在於'Agraphinfo_t'結構是MASSIVE(根據字段數量),並根據庫的編譯方式使用不同的字段。我不知道使用了哪些編譯器標誌,所以我不確定要包含哪些字段。有小費嗎? – 2010-01-09 01:51:30

+0

不幸的不是。爲了獲得大小匹配,你需要在該插槽中提供* something *,它與非託管的'Agraphinfo_t'結構佔用相同數量的大小(在結構中)。你可能可以使用一個字節數組(使用合適的編組標誌來確保它被視爲內聯空間而不是引用),但是這仍然需要你計算大小......但我想你可以通過編寫一個小C程序來打印'sizeof(Agraphinfo_t)'。沒有承諾,但 - 對不起! – itowlson 2010-01-09 01:56:01

+0

聽起來可行。與此同時,我添加了這樣一個要求,那就是應該在調用'aginitlib'之前調用它自己調用的庫的一部分,並且無法從託管包裝器手動調用它。它有效,但絕對不是我想要保留的要求。 – 2010-01-09 02:01:25

1

IntPtr在32位代碼中寬度爲4字節,在64位模式下長度爲8字節。

您可以使用IntPtr.Size屬性來確定大小,該屬性報告當前運行時的值的大小。

至於你的其他問題,你應該真的使用StructLayoutAttribute來確保託管和非託管結構以相同的方式佈局在內存中,具有相同的填充和字段大小。

您還需要從結構中刪除屬性,因爲它們會影響內存中結構的大小。

+0

「你還需要從結構去除性能,因爲它們會影響的大小記憶中的結構「。這是不正確的。屬性是元數據,佔用每個類型的空間*,而不是每個實例。 (如果這些屬性有自己的支持字段,那麼你的評論會是真實的,但它們不會:它們只是通過tag_kind_handle字段的getter方法。) – itowlson 2010-01-09 01:28:40