2014-12-27 97 views
10

我一直在嘗試創建一個結構類型的句柄,因爲我需要一個釘住指針,但我得到的錯誤「對象包含非原始的或非原始的, blittable數據「我如何分配GCHandle結構當結構包含布爾

我的結構是這樣的:

[StructLayout(LayoutKind.Sequential)] 
public struct MyStruct 
{ 
    [MarshalAs(UnmanagedType.U1)] 
    public bool Test; 
} 

現在,當我打電話,

var mystruct = new MyStruct(); 
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned); 

我得到的錯誤」對象包含非基本或非blittable數據」。現在我明白了bool字段是一個非blittable類型。但我的印象是,通過添加MarshalAs屬性,我可以告訴編組人員如何轉換類型。 (我也試過UnmanagedType.Bool

這個結構必須在全局範圍內定義,因爲它在整個班級都是需要的。我需要指針的唯一原因是因爲我有一個非託管API,必須將此結構作爲指針傳遞。然後,我必須在回調中獲取該結構並讀取/更新成員。

所以這是基本情況。

  1. 結構是
  2. 指向結構得到
  3. 指向結構傳遞到API
  4. API調用靜態方法回調,我則需要獲得管理類全球範圍內創建我的結構和閱讀/更新成員。

我試圖用Marshal.StructureToPtr但這隻能創建一個副本,因此,如果在我的管理類我更新的構件時,在回調上升,更新的價值是不存在的。

有誰知道我怎麼能得到一個固定的指針,我的結構,所以我可以讀/修改公共成員,並讓他們在回調中可用?

謝謝

+2

這裏是Blittable型列表HTTP:/ /msdn.microsoft.com/en-us/library/75dwhxf7.aspx – Mayank 2014-12-27 20:06:20

回答

13

你在這裏有多個問題。使用struct是非常不可取的。它會在GCHandle.Alloc()調用之前裝箱,並且盒裝對象被固定。您無法通過您的mystruct變量看到它的任何更新。改爲使用

並避免bool,由於其高度可變的實現,它是一種不可擦除的類型。它是C中的4個字節,C++中的1個字節,COM中的2個字節。改爲使用字節。你可以寫一個屬性讓它回到布爾。

所以:

[StructLayout(LayoutKind.Sequential)] 
public class MyStruct 
{ 
    private byte _test; 
    public bool Test { 
     get { return _test != 0; } 
     set { _test = value ? 1 : 0; } 
    } 
} 
+0

我的確也嘗試過它作爲一個類,但我試圖強制它使用bool類型掛了。我讀了@ hvd的答案,他是對的。看起來MarshalAs只是沒有做任何事情。我曾試圖將它看作一個int,看看我能不能創建一個'GCHandle',我可以,但我不喜歡它的想法。我將其標記爲答案,因爲我沒有考慮使用屬性來包裝成員。我會試試這個!謝謝! – 2014-12-27 23:52:34

+0

ECMA-335 11.7.4表示「布爾值,其中任何非零的 值表示TRUE,並且0表示 FALSE。」4個字節的整數值。這聽起來不是很明顯。我會認爲這是一個疏忽或者可能是因爲語義與普通整數類型不同而不是技術決定。 – 2016-11-04 23:23:50

7

你說得對,你告訴編組如何封送類型。

但是當你嘗試繞過編組時,這並不會對你有任何好處。

您需要決定是否要使用編組器,或者是否要非託管代碼直接寫入托管內存。

如果你想使用編組:

一般來說,處理一個很好的辦法是在兩個方向上使用它。您可以使用Marshal.StructureToPtr(如您所找到的),調用外部函數,然後使用Marshal.PtrToStructure將其轉換回您的託管表示。

或者您可以使用以編組自動發生的方式設置的方法,而無需手動指定。例如,調用參數爲ref MyStruct的本地方法將允許發生這種情況。

如果你不想使用編組:

不要使用需要編組的任何類型。正如Hans Passant所說,改用其他類型,byte可能是一個不錯的選擇。

(我從評論上的優勢,並利用結構在這裏,除了缺點是避免已經做出關於它的點是非常值得閱讀和理解。)