当结构包含bool时,如何将GCHandle分配给结构

时间:2014-12-27 19:39:46

标签: c# pointers pinvoke

我一直在尝试创建一个结构类型的句柄,因为我需要一个指向它的固定指针,但我收到错误" Object包含非原始或非blittable数据"

我的结构如下:

[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属性,我可以告诉marshaller如何转换类型。 (我也试过UnmanagedType.Bool

这个结构必须全局定义,因为我的课程需要它。我需要指针的唯一原因是因为我有一个非托管API,必须将此结构作为指针传递。然后我必须在回调和读取/更新成员中获得该结构。

所以这是基本情景。

  1. 在托管类中全局创建结构
  2. 获得结构指针
  3. 将结构指针传递给API
  4. API调用静态方法回调,然后我需要获取我的结构并读取/更新成员。
  5. 我尝试使用Marshal.StructureToPtr,但这只会创建一个副本,因此如果在我的托管类中我更新了成员,则在引发回调时,更新的值不存在。

    有谁知道如何获得指向我的结构的固定指针,以便我可以阅读/修改公共成员并让它们在回调中可用?

    由于

2 个答案:

答案 0 :(得分:14)

你在这里遇到了不止一个问题。使用 struct 是非常不可取的。它将在GCHandle.Alloc()调用和 盒装对象被固定之前被装箱。您无法通过 mystruct 变量看到任何更新。改为使用

并且避免 bool ,由于其高度可变的实现,它是一种非blittable类型。它是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; }
    }
}

答案 1 :(得分:7)

你是对的,你告诉编组人员如何编组这种类型。

但是当你试图绕过编组时,这对你没有任何好处。

您需要决定是否要使用编组程序,或者是否希望非托管代码直接写入托管内存。

如果你想使用编组:

通常,处理此问题的一个好方法是在两个方向上使用它。您可以使用Marshal.StructureToPtr(如您所见),调用外部函数,然后使用Marshal.PtrToStructure将其转换回您的托管代表。

或者您可以使用以自动编组方式设置的方法,而无需手动指定。例如,调用带有ref MyStruct参数的本机方法将允许这种情况发生。

如果您不想使用编组:

请勿使用任何需要编组的类型。正如Hans Passant评论的那样,使用不同的类型,byte可能是一个不错的选择。

(我不会评论在这里使用结构的优点和缺点,除了已经有关它的要点非常值得阅读和理解。)