2014-12-27 42 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可能是一个不错的选择。

(我从评论上的优势,并利用结构在这里,除了缺点是避免已经做出关于它的点是非常值得阅读和理解。)