【问题标题】:Check COM Interface still alive?检查 COM 接口是否仍然存在?
【发布时间】:2011-11-21 21:19:55
【问题描述】:

在 COM 中,如何验证指向 COM 对象的指针在另一端仍然具有有效对象?

我有一个问题,以下代码尝试检查m_pServer 指针是否仍然存在,但是当暴露该接口的应用程序被终止时,这段代码会使应用程序崩溃。任何人都可以建议在使用之前如何检查指针吗?

if (FAILED(m_pServer->StillAlive())) { // do something }

如果 m_pServer 不再在内存中,此代码将失败。

编辑:

异常:Client40.exe 中 0x7728fbae (kernel32.dll) 的第一次机会异常:0x800706BA:RPC 服务器不可用。

调用堆栈:

    kernel32.dll!RaiseException()  + 0x58   
    rpcrt4.dll!RpcRaiseException()  + 0x3e  
    rpcrt4.dll!NdrProxyErrorHandler()  + 0x28   
    rpcrt4.dll!NdrProxySendReceive()  + 0xa4    
    rpcrt4.dll!NdrProxySendReceive()  + 0x119   
    rpcrt4.dll!NdrComplexArrayMarshall()  + 0x26d   
--> Client40.exe!SlaveDriver::run()  Line 97 + 0x14 C++  //Runs while loop, to handle requests
    Client40.exe!DThread::tfunc(void * thisptr=0x0047e694)  Line 56 + 0xd   C++
    Client40.exe!_threadstartex(void * ptd=0x01b20e00)  Line 241 + 0xd  C
    kernel32.dll!BaseThreadInitThunk()  + 0x12  
    ntdll.dll!RtlInitializeExceptionChain()  + 0x63 
    ntdll.dll!RtlInitializeExceptionChain()  + 0x36 

【问题讨论】:

  • 应用程序究竟是如何崩溃的?
  • 在上面提到的那一行抛出如下异常: 0x800706BA: RPC 服务器不可用。我可以防止这种情况吗?
  • 不清楚为什么会抛出异常。进入代码 (F11) - 也许有一些包装器代码会引发异常。你是#import .tlb 还是 .dll 文件?
  • 当我杀死暴露这个 m_pServer 接口的应用程序时抛出异常,现在这个应用程序试图调用一个不再可用的方法。我正在尝试防止引发此异常。
  • 客户端进程中某些代码抛出异常。我在问你该代码是什么以及它究竟是如何引发异常的,因为这对于解决你的问题至关重要。

标签: c++ com


【解决方案1】:

您在这里尝试做的事情根本不可能。因为m_pServer 存在于另一个进程中,所以您实际上是在问以下问题

进程 XXX 是否仍在运行?

这在 windows(或 linux / unix)的世界中根本不是一个可以回答的问题。您永远无法可靠地回答问题,因为一旦回答了问题,其结果就可能无效。进程可以在任何时间点终止,包括在您检查和访问 COM 对象之间。

但是,这个问题的答案略有不同的版本

进程 XXX 是否仍在运行?

解决此类问题的唯一方法是执行有问题的操作并期望它会失败。如果成功,那么您已经回答了问题的修改版本。如果失败,那么您需要解释失败以确定操作是否失败或进程已消失。

这是正确处理这种情况的唯一方法。

【讨论】:

    【解决方案2】:

    由您来管理 COM 对象的生命周期。只要你有一个指向接口的实时指针,你必须在接口上至少有一个 AddRef() 调用。最后的 Release() 调用将删除对象并且指针变得陈旧。之后使用它会使您的程序随机崩溃,通常使用 AV。没有办法检测它是否过时。

    您可以在进行最后的 Release() 调用时将 m_pServer 设置为 NULL。

    【讨论】:

    • 所以如果暴露这个接口的应用程序死掉了,那么使用它的人在尝试使用它之前永远不会知道???或者有没有办法防止这种情况???
    • 我猜他的问题是out-proc服务器被强制终止了。
    • @Tony,你会从 RPC 得到一个 HRESULT 错误代码,通常是 RPC_E_SERVERDIED。杀死服务器并不会杀死它的代理。你可以通过不杀死服务器来防止它。
    【解决方案3】:

    来自 COM 的第一次机会异常被代理捕获并报告为错误代码。如果您继续跳过第一次机会异常,您应该会看到代理返回 RPC_SERVER_UNAVAILABLE,您应该适当地处理它。

    【讨论】:

      【解决方案4】:

      从根本上说,你永远不会检查这个。它只能给出一个无用的答案。考虑以下假设示例:

      if (CoCheckAlive(ptr)) {
        ptr->Foo();
      }
      

      即使 CoCheckAlive 返回了true,该结果也不能保证远程服务器能够存活足够长的时间来进行下一次调用。这就是为什么您只需拨打电话,然后处理失败案例(包括 RPC_E_SERVERDIED)。

      【讨论】:

      • 所以 FAILED(m_pServer->StillAlive()) 基本上没用??
      • 如果它的服务器不再可用,如何正确杀死客户端(在 proc com 服务器之外)???
      • 是的,->StillAlive 的检查方式同样没用。它也只告诉你服务器是活着的,而不是当你进行真正的调用时它是否会活着。我不明白你的第二个问题,但这似乎是一个好问题。把它写清楚,然后找 StackOverflow。很有可能它已经回答了,或者你有一个很好的新问题。
      • 这种行为有一些有效的用例,目的是提供明智的反馈,而不是保证以后的调用。考虑一个空闲的客户端,它检查一个计时器,该计时器写入错误日志或引发一个对话框,指出服务器连接何时丢失。
      • 不是没用,只是小心的暗示。适当的名称可能是IsServerDead(ptr)。 true 表示服务器已死。 IsServerDead(ptr) == false 不暗示服务器是否处于活动状态。
      【解决方案5】:

      既然它是一个例外,你就不能 __try 吗?您可以设置一个异常过滤器来捕获 RPC-Server 不可用异常。有关如何执行此操作的进一步说明,请参阅 http://msdn.microsoft.com/en-us/library/s58ftw19%28VS.80%29.aspx。

      【讨论】:

        【解决方案6】:

        只需编写一个包装方法来检查失败并捕获异常并将其报告为失败。

        【讨论】:

          【解决方案7】:

          1 - 检查代码中的 HRESULT - 而不仅仅是一般的 FAILED - 这将为您提供可以向用户报告更多信息和/或启动替代操作的位置(例如优雅地关闭应用程序)。实际的 HRESULT 可能完全是 RPC_SERVER_UNAVAILABLE,也可能不完全是 RPC_SERVER_UNAVAILABLE - 所以首先将它分配给一个 var 以便您可以在调试器中看到它(并且不要在第一次出现异常时跳转 - 它们用于调试较低层的代码)。

          2 - 如果你真的在你的 C++ 代码中抛出了异常,那么首先在它周围放置一个通用的 catch,然后在调试器中查看它。这种情况可能来自某种智能指针试图变得太聪明:-)

          在任何情况下,为您的代码留下一个标志以了解发生了什么并优雅地关闭 COM 引用 - 仅仅因为服务器死亡并不意味着您必须泄漏:-)

          【讨论】:

            猜你喜欢
            • 2012-11-30
            • 2015-04-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-01-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多