【问题标题】:How do I allocate GCHandle to structure when structure contains bool当结构包含布尔时,如何将 GCHandle 分配给结构
【发布时间】:2015-02-24 13:45:44
【问题描述】:

我一直在尝试创建结构类型的句柄,因为我需要一个指向它的固定指针,但我收到错误消息“对象包含非原始或非 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 属性,我可以告诉编组器如何转换类型。 (我也试过UnmanagedType.Bool

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

所以这是基本场景。

  1. 结构是在托管类中全局创建的
  2. 获得了指向结构的指针
  3. 指向结构的指针被传递到 API 中
  4. API 调用静态方法回调,然后我需要在其中获取结构并读取/更新成员。

我尝试使用Marshal.StructureToPtr,但这只会创建一个副本,因此如果在我的托管类中更新成员,则在引发回调时,更新的值不存在。

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

谢谢

【问题讨论】:

标签: c# pointers pinvoke


【解决方案1】:

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

并避免 bool,由于其高度可变的实现,它是一种非 blittable 类型。它在 C 中为 4 个字节,在 C++ 中为 1 个字节,在 COM 中为 2 个字节。只需将其设为 byte 即可。您可以编写一个属性将其恢复为布尔值。

所以:

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

【讨论】:

  • 我确实也尝试过将它作为一个类,但我试图强制它使用 bool 类型。我读了@hvd 的答案,他是对的。看起来 MarshalAs 在那里没有做任何事情。我曾尝试将它作为一个 int 来查看我是否可以创建一个 GCHandle,我可以,但我不喜欢它的想法。我将此标记为答案,因为我没有考虑使用属性来包装成员。我会试试这个!谢谢!
  • ECMA-335 11.7.4 表示“布尔值。4 字节整数值,其中任何非零值表示 TRUE,0 表示 FALSE。”对我来说,这听起来变化不大。我将假设这是一个疏忽,或者可能是因为语义不同于常规整数类型而不是技术决定而决定的。
【解决方案2】:

你是对的,你告诉编组器如何编组类型。

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

您需要决定是要使用编组器,还是希望非托管代码直接写入托管内存。

如果你想使用编组器:

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

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

如果您不想使用编组器:

不要使用任何需要编组的类型。作为 Hans Passant 的 cmets,请改用其他类型,byte 可能是一个不错的选择。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-07
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 2014-06-21
    • 2018-12-07
    • 2012-07-29
    • 1970-01-01
    相关资源
    最近更新 更多