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

Eri*_*ler 10 c# pinvoke pointers

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

我的结构看起来像这样:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.U1)]
    public bool Test;
}
Run Code Online (Sandbox Code Playgroud)

现在,当我打电话的时候

var mystruct = new MyStruct();
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned);
Run Code Online (Sandbox Code Playgroud)

我收到错误"对象包含非原始或非blittable数据".现在我明白bool字段是一个非blittable类型.但我的印象是,通过添加MarshalAs属性,我可以告诉marshaller如何转换类型.(我也试过UnmanagedType.Bool)

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

所以这是基本情景.

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

我试图使用,Marshal.StructureToPtr但这只创建一个副本,所以如果在我的托管类中我更新成员,当引发回调时,更新的值不存在.

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

谢谢

Han*_*ant 14

你在这里遇到了不止一个问题.使用结构是非常不可取的.它将在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; }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 7

你告诉编组人如何编组这种类型你是对的.

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

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

如果你想使用marshaller:

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

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

如果您不想使用marshaller:

不要使用任何需要编组的类型.正如Hans Passant所说,使用不同的类型,byte可能是一个不错的选择.

(我将不再评论在这里使用结构的优点和缺点,除了已经提出的关于它的要点非常值得阅读和理解.)