【问题标题】:Performing a memset on struct with a BSTR使用 BSTR 在 struct 上执行 memset
【发布时间】:2014-06-26 02:18:09
【问题描述】:

我目前有一个堆损坏导致我的应用程序崩溃。我的应用程序是 COM 服务器 (C++) 编组到 C# 客户端应用程序。作为 COM 清理的一部分,它看起来可能是从 C# 端调用的 SysFreeString。我似乎无法查明原因,但在 C++ 端对包含 BSTR 的结构执行 memset 0 是否会导致问题?

以下内容有效吗?

memset(pResult, 0, sizeof(RESULT)) where pResult is of type RESULT

typedef struct _Result
{
    DWORD dwResult;
    BSTR strData;
}   RESULT;

来自 WinDbg 的 STACK_TEXT:

00000000`77b4b518 00000000`77a7bc35 ntdll! ?? ::FNODOBFM::`字符串'+0xea19 00000000`77b4b520 000007fe`ffc41377 oleaut32!SysFreeString+0x53 00000000`77b4b528 000007fe`ffc46b2e oleaut32!BSTR_UserFree+0x1e 00000000`77b4b530 000007fe`fe354a1c rpcrt4!NdrUserMarshalFree+0x4c 00000000`77b4b538 000007fe`fe3566eb rpcrt4!NdrpFreeParams+0x207 00000000`77b4b540 000007fe`fe365362 rpcrt4!NdrStubCall2+0xedc 00000000`77b4b548 000007fe`fe84f16e ole32!CStdStubBuffer_Invoke+0x8b 00000000`77b4b550 000007fe`fe850ccd ole32!SyncStubInvoke+0x5d 00000000`77b4b558 000007fe`fe850c43 ole32!StubInvoke+0xdb 00000000`77b4b560 000007fe`fe70a4f0 ole32!CCtxComChnl::ContextInvoke+0x190 00000000`77b4b568 000007fe`fe8514d6 ole32!AppInvoke+0xc2 00000000`77b4b570 000007fe`fe85122b ole32!ComInvokeWithLockAndIPID+0x52b 00000000`77b4b578 000007fe`fe84fd6d ole32!ThreadInvoke+0x30d 00000000`77b4b580 000007fe`fe363254 rpcrt4!DispatchToStubInCNoAvrf+0x14 00000000`77b4b588 000007fe`fe3633b6 rpcrt4!RPC_INTERFACE::DispatchToStubWorker+0x146 00000000`77b4b590 000007fe`fe365b8b rpcrt4!RPC_INTERFACE::DispatchToStub+0x9b 00000000`77b4b598 000007fe`fe365acb rpcrt4!RPC_INTERFACE::DispatchToStubWithObject+0x5b 00000000`77b4b5a0 000007fe`fe365a62 rpcrt4!LRPC_SCALL::DispatchRequest+0x422 00000000`77b4b5a8 000007fe`fe36375d rpcrt4!LRPC_SCALL::HandleRequest+0x20d 00000000`77b4b5b0 000007fe`fe3809ff rpcrt4!LRPC_ADDRESS::ProcessIO+0x3bf 00000000`77b4b5b8 000007fe`fe3805b5 rpcrt4!LrpcIoComplete+0xa5 00000000`77b4b5c0 00000000`77a2b6bb ntdll!TppAlpcpExecuteCallback+0x26b 00000000`77b4b5c8 00000000`77a2ff2f ntdll!TppWorkerThread+0x3f8 00000000`77b4b5d0 00000000`7790652d kernel32!BaseThreadInitThunk+0xd 00000000`77b4b5d8 00000000`77a3c541 ntdll!RtlUserThreadStart+0x1d

【问题讨论】:

    标签: c# c++ marshalling memset bstr


    【解决方案1】:

    在 C++ 端对包含 BSTR 的结构执行 memset 0 是否可能会导致问题?

    不,BSTR 是指针类型 (typedef OLECHAR *BSTR;),因此使用 memset 清除它的值最多会导致内存泄漏,但不会导致崩溃。

    【讨论】:

    • 感谢您指出这一点。目前我唯一的另一个预感是在 C++ 和 C# 之间编组时使用 _bstr_t。在将其编组到 C# 应用程序时,我使用 _bstr_t 并将其分配给结构中的 BSTR。我的理解是 _bstr_t 在销毁时调用 SysFreeString。这是否意味着 SysFreeString 将被调用两次,一次在 C++ 端调用,一次从 C# 编组器调用?
    • 是的,这将是一个问题 _bstr_t 具有所有权语义所以是的,分配 BSTR 指针意味着 _bstr_t 对象现在拥有该字符串并将在销毁时删除。
    • 您认为在将 _bstr_t 对象编组到 C# 应用程序之前将其分配给 BSTR 的最佳方法是什么?我是否必须执行 Detach() 才能将其分配给要编组的结构上的 BSTR 对象?您认为这可能是 BSTR 被释放两次的罪魁祸首吗?
    • 是的,Detach 正是您要找的。在 C# 调用返回后,调用 Detach 以释放字符串的所有权 - 或者 - 如果性能不是问题,则复制字符串并将其填充到 _bstr_t 对象中。
    • 我有点没明白你的意思。如果从 C# 应用程序调用 C++ COM 函数并且 OUT 参数是 BSTR 类型会怎样。我是否只使用 _bstr_t Detach 分配 OUT 参数?例如 bstrOutputParm = _bstr_t("test").Detach()?如果我不执行 Detach,这会导致 _bstr_t 内的 BSTR 双重释放吗?
    【解决方案2】:

    自从我搞砸这些东西以来已经有一段时间了,但我认为BSTR 是可憎的,因为wchar_t * 指针不指向缓冲区的开头。 SysFreeString() 实际上会释放 (ptr-1) 或类似的东西。使用memset() 将其归零听起来是个坏主意。

    【讨论】:

    • OP 将 BSTR 变量中保存的指针值清零,而不是 BSTR 指向的内容。
    • 没错。 BSTR 不是(必然)以 nul 结尾的,所以它必须有一个长度字段。但他们希望能够无缝转换为 wchar_t。所以实际对象是 1 uint16 大小,后跟一些 wchar_t 字节。 BUT BSTR * ptr 指向 char 数据的开头,偏移量为 -1。在 SysFreeString() 的深处,隐约有些像 free(ptr-1) 的东西。我们说的是丑陋的黑魔法。考虑一下如果你通过SysFreeString(NULL)会发生什么。
    • 调用 SysFreeString(NULL) 导致无操作。
    • @CaptainObvlious - 你是对的。我刚刚检查了 VC2013 的反汇编,它测试了一个空 ptr。哦,好吧,我只是在推测一个可能的原因。我仍然说BSTR是可憎的。 :-)
    • 当 typedef 实际上是 OLECHAR* 时,BSTR 如何在内部工作真的很奇怪。那么对于其中包含 BSTR 的结构,memset 是不是一个坏主意?
    【解决方案3】:

    如果 COM 清理代码中出现异常。当然,这个结果结构的分配存在问题。

    请确保:

    这个参数是如何传递给函数的

    In Parameter => 如果此参数作为 In 参数 传递,则它是由客户端分配的,您不能重新分配此参数,否则您会得到这个例外。

    InOut 参数 => 如果这个参数是InOut 参数 比你可以重新分配这个参数。

    【讨论】:

    • 问题是因为它是一个COM调用我不知道是哪个RPC函数调用触发了这个问题。有没有办法找出我的接口上的哪个 COM 函数调用导致了这个堆损坏?
    • 您无需担心 COM 运行时功能,它们会正常工作。如果你能告诉我代码,我可以帮助你更多
    猜你喜欢
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    • 2012-09-25
    • 1970-01-01
    • 2011-09-03
    • 2018-09-29
    • 2012-12-21
    • 1970-01-01
    相关资源
    最近更新 更多