【问题标题】:CLI/C++: void* to System::ObjectCLI/C++: void* 到 System::Object
【发布时间】:2011-08-01 19:15:58
【问题描述】:

这是一个与this SO post 类似的问题,我一直无法用它来解决我的问题。我在这里包含了一些代码,希望能帮助某人将其他帖子所传达的信息带回家。

我想编写一个 CLI/C++ 方法,该方法可以将 void 指针作为参数并返回它指向的托管对象(我知道其类型)。我有一个托管结构:

public ref struct ManagedStruct { double a; double b;};

我正在尝试编写的方法,它将指向托管结构的 void 指针作为参数并返回该结构。

ManagedStruct^ VoidPointerToObject(void* data)
{   
    Object^ result = Marshal::PtrToStructure(IntPtr(data), Object::typeid);
    return (ManagedStruct^)result;
}

这里调用方法:

int main(array<System::String ^> ^args)
{   
    // The instance of the  managed type is created:
    ManagedStruct^ myData = gcnew ManagedStruct();
    myData->a = 1;  myData->b = 2;      

    // Suppose there was a void pointer that pointed to this managed struct
    void* voidPtr = &myData;

    //A method to return the original struct from the void pointer
    Object^ result = VoidPointerToObject(voidPtr);  
    return 0;
}

它在调用PtrToStructure 时在VoidPointerToObject 方法中崩溃,并出现错误:指定的结构必须是blittable 或具有布局信息

我知道这样做很奇怪,但这种情况我已经遇到过几次了,尤其是当非托管代码对托管代码进行回调并将 void* 作为参数传递时。

【问题讨论】:

    标签: pointers c++-cli command-line-interface


    【解决方案1】:

    (原文如下)

    如果您需要通过本机代码将托管句柄作为void* 传递,您应该使用

    void* voidPtr = GCHandle::ToIntPtr(GCHandle::Alloc(o)).ToPointer();
    
    // ...
    
    GCHandle h = GCHandle::FromIntPtr(IntPtr(voidPtr));
    Object^ result = h.Target;
    h.Free();
    

    (或使用 C++/CLI 辅助类 gcroot


    Marshal::PtrToStructure 适用于值类型

    在 C++/CLI 中,这意味着 value classvalue struct。您正在使用ref struct,它是一个引用类型,尽管使用了关键字struct

    一个相关的问题:

    void* voidPtr = &myData;
    

    不指向对象,它指向句柄。

    为了在托管堆上创建指向数据的本机指针,您需要使用 pinning。因此,void*Object^ 之间的转换并不像乍一看那样有用。

    【讨论】:

    • 另外,Object::typeid在调用Marshal::PtrToStructure的时候是没用的;一个应该传递他们实际编组的结构的类型,而不是 System::Object 的类型。
    • @ildjarn 感谢您的帮助。我试图传递ManagedStruct::typeid 而不是System::Object 的类型,但我得到一个无法从'System::Object ^' 转换为'ManagedStruct' 错误,所以一直在使用我的解决方案作为解决方法发布。知道这可能是什么吗?
    • @Rory :该错误表明您正在尝试类似(ManagedStruct)Marshal::PtrToStructure(IntPtr(data), ManagedStruct::typeid) 的东西,如果ManagedStruct 是值类型(正如Ben 指出的那样),这将是正确的。但是,因为您的 ManagedStruct 是一个引用类型,所以这种转换是无意义的——(ManagedStruct^)Marshal::PtrToStructure(IntPtr(data), ManagedStruct::typeid) 会编译,但由于 Ben 列出的原因在运行时会失败。
    • @Rory:我刚刚重读了你的最后一句话。您不需要指向托管对象的指针,您需要一个可用于恢复托管对象的指针值,并防止垃圾收集器收集它。这就是GCHandle 的用途。见编辑。
    • 值得补充的是 GCHandle 不在默认命名空间中。为了编译该代码,您需要:using namespace System::Runtime::InteropServices;
    猜你喜欢
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 2012-07-26
    • 2022-06-22
    • 1970-01-01
    相关资源
    最近更新 更多