P/Invoke:指针内存损坏

P/Invoke: Memory corruption with pointer

本文关键字:内存 损坏 指针 Invoke      更新时间:2023-10-16

我正在用Mono(所以COM、CLI不是选项)和一堆extern包装FBX SDK的一部分(已关闭,使用公共API),一切都很顺利,直到我不得不返回一个非指针实例。参见此处

关键的一点是,我必须将它返回到C++进行另一个调用。因为我不知道如果没有指针你会怎么做,我就这样返回了:

FBXAPI FbxProperty* Object_GetFirstProperty(FbxObject* obj)
{
    return &obj->GetFirstProperty();
}

直到我尝试了类似下一个片段的东西,我才得到"System.AccessViolationException:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。"消息。

FBXAPI const wchar_t* Property_GetName(FbxProperty* prop)
{
    int c = prop->GetSrcPropertyCount();
    return L"Test";
}

如果我在C++中使用几乎相同的代码,使用相同的调用,那也没关系。我用同样的方式又做了大约20个函数调用,但不必"pointerfy",它们也都很好,所以我不认为我的DllImport是罪魁祸首。那么,如果引用是罪魁祸首,我还能怎么做呢?我肯定不会因为有人从API调用了全局静态引用就把它存储在某个地方吗?

感谢您的帮助,C/C++及其处理内存的显式方式对我来说是新的。

我认为您的程序正在崩溃,因为您获取指针的属性已不存在。让我澄清并从剖析以下内容开始:

FBXAPI FbxProperty* Object_GetFirstProperty(FbxObject* obj)
{
    return &obj->GetFirstProperty();
}

我查阅了FBX的文档,FbxObject::GetFirstProperty()的返回类型为FbxProperty。请注意,返回值不是任何指针或引用?这意味着你得到了一个所谓的"自动变量",或者在这种情况下是一个"临时变量"。这种对象只会持续到您离开作用域,在本例中,作用域是包装器的Object_GetFirstProperty()。之后,对象将被清理并从内存堆栈中删除。FbxObject::GetFirstProperty()为您提供了属性的副本,而不是实际的引用。在内部,它可能有所不同,但包装器关心的是属性对象本身,而不是它的内容。

因此,当您将指针传递给Property_GetName()时,您将得到一个指向以后不再有效的地址的指针。

在对象生存期方面,C++的行为与C#不同。C#中一个名为MyObj的对象可以被认为是像MyObject*一样的C++指针类型——它就像一个引用值。在C#中,还有类似struct等的值类型,它们等效于C++自动变量。所有自动变量在其生存期范围保留时都会被销毁。

为了解决这个问题,你必须直接保存从FbxObject::GetFirstProperty()获得的对象,而不是指向它的指针。你基本上必须将对象编组到一个合适的.NET类中,这样它的内容就不会丢失。

或者,您可以分配动态内存,并将从FbxObject::GetFirstPoperty()获得的对象复制到那里,然后返回一个指向您自己内存的指针。当然,您以后必须手动删除此内存。这里有一个简单的例子:

FBXAPI FbxProperty* Object_GetFirstProperty(FbxObject* obj)
{
    // Allocate custom memory.
    char* myMem = new char[sizeof(FbxProperty)];
    // Copy the property's content there.
    std::memcpy(myMem, &obj->GetFirstProperty(), sizeof(FbxProperty));
    // Return custom memory address.
    return reinterpret_cast<FbxProperty*>(myMem);
}

这应该可以解决您的内存损坏问题。但在C++中,当你完成属性时,你必须手动释放内存:

FBXAPI void Property_Free(FbxProperty* prop)
{
    // Free previously allocated memory
    delete[] prop;
}

但这种尝试可能会导致其他问题,这取决于实际的FbxProperty如何处理其内部的数据。当然,您正在创建对象的副本,但如果原始临时性/自动变量在销毁时删除了重要内存,则会出现与现在类似的问题。

如果你真的很聪明,你可以为你需要的每一种FBX类型编写真正的包装器类,并整理整个类类型,而不是每次你想要获得值或属性时都必须生成p/Invoke的独立C函数。