P/Invoke:内存损坏与指针

Posted

技术标签:

【中文标题】P/Invoke:内存损坏与指针【英文标题】:P/Invoke: Memory corruption with pointer 【发布时间】:2014-11-10 12:28:29 【问题描述】:

我正在用 Mono(所以 COM、CLI 不是选项)和一堆 extern 封装 FBX SDK(已关闭,带有公共 API)的一部分,一切进展顺利,直到我不得不返回一个非指针实例。 See here

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

FBXAPI FbxProperty* Object_GetFirstProperty(FbxObject* obj)

    return &obj->GetFirstProperty();

..直到我尝试类似下一个 sn-p 的操作,我才得到 “System.AccessViolationException : Attempted to read or write protected memory。这通常表明其他内存已损坏。” 消息。

FBXAPI const wchar_t* Property_GetName(FbxProperty* prop)

    int c = prop->GetSrcPropertyCount();
    return L"Test";

如果我在 C++ 中使用相同的调用使用几乎相同的代码,那很好。我以同样的方式又做了大约 20 个函数调用,但不必“指针”它,它们也都很好,所以我不认为我的 DllImport 应该受到责备。因此,如果应归咎于参考,我该怎么做呢?我肯定不会因为有人从 API 调用它而在某处存储全局静态引用吗?

感谢任何帮助,C/C++ 和它处理内存的显式方式对我来说是新的。

【问题讨论】:

en.wikipedia.org/wiki/Dangling_pointer 我发现您的问题令人困惑:标题表明问题是关于非指针返回值,然后您继续解释您需要如何返回指针。此外,问题是关于 P/Invoke 但您只提供 C++ 签名,而不是相应的 C# P/Invoke 签名。 我们不知道 FbxProperty 是什么。而且我们无法判断您返回的地址是否仍然有效。而且你没有显示任何 c#。 C++/CLI 是否适合您?如果是这样,这注定要在 Windows 上编写一个 C# 包装器;通过正确的 P/Invoke 为您省去所有麻烦。 感谢 cmets,我已经澄清了新的问题标题,并澄清这是为了跨平台(单声道)分发,所以 CLI 不是一个选项 - 我希望是这样!跨度> 【参考方案1】:

我假设您的程序崩溃了,因为您获得指针的属性不再存在。让我澄清一下,并从剖析以下内容开始:

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 类型编写真正的包装类,并编组整个类类型,而不是生成单独的 C 函数,每次你想获得一个值或一个属性时你都必须 P/Invoke .

【讨论】:

以上是关于P/Invoke:内存损坏与指针的主要内容,如果未能解决你的问题,请参考以下文章

P/Invoke今日谈

char 指向字符串的指针,然后进入字符串数组。 *** `./a.out' 中的错误:malloc():内存损坏:0x0900c3b0 ***

进程内存和内存损坏

基本线程导致 malloc():内存损坏:[关闭]

字符串的动态数组,但我检测到堆损坏

指针函数参数损坏,堆栈损坏?