【问题标题】:How can you tell if SendMessage() was successful (message was delivered)?如何判断 SendMessage() 是否成功(消息已传递)?
【发布时间】:2019-05-02 06:02:19
【问题描述】:

是否可以确定 SendMessage() 调用是否成功并将我的消息传递到目标窗口?

Windows API 中的 SendMessage() 描述对此似乎很安静,只说以下内容:

返回值指定消息处理的结果;这取决于发送的消息。

这显然是指返回码反映了目标窗口的wndproc返回的值。

但是,如果消息根本没有传递(例如,由于访问控制,或者由于窗口在此期间被破坏),返回码会是什么?我怎样才能检测到这种情况?

【问题讨论】:

  • 取决于消息。你能回复一下吗?
  • 这是我自己的代码的 WM_USER 消息,所以我在这里可以完全控制。一个简单的响应(如 wndproc 返回 TRUE)是不行的,因为如果传递失败,SendMessage() 的返回值似乎是未定义的。因此,似乎无法保证 SendMessage() 在这种情况下不会返回 TRUE。
  • 我的意思是让另一个应用程序SendMessage 回来。
  • SendMessage 有一个返回值,不能用来可靠地区分成功和失败。另一方面,SendMessageTimeout 会返回成功/错误指示符以及消息处理的结果。
  • SMTO_NOTIMEOUTIFNOTHUNG: “只要接收线程正在处理消息,该函数就不会强制超时。”

标签: winapi


【解决方案1】:

没有办法可靠地判断对SendMessage 的调用是成功还是失败。毕竟它只有一个返回值,并且使用了整个值域。没有指定的错误值。

对调用线程的最后一个错误代码进行欺骗也行不通。 this answer 中提出的方案(在调用之前清除最后一个错误代码并在调用之后对其进行评估)是脆弱的。很容易看出,在向调用线程拥有的窗口发送消息时,它可能会失败(因为该窗口的窗口过程可以随意更改最后一个错误)。

这并不是众所周知的,当向另一个线程拥有的窗口发送消息时,这也会失败。该代码表明,调用线程中不会发生任何中间 API 调用。然而,在这种情况下,SendMessage 还将分派传入的跨线程消息(请参阅When can a thread receive window messages?),从而允许线程的最后一个错误代码发生变化。

唯一的选择是使用SendMessageTimeoutW,因为该 API 会报告调用结果以及错误/成功指示器。传递SMTO_NOTIMEOUTIFNOTHUNG 标志可确保任意选择的超时不会对结果产生不利影响。

【讨论】:

    【解决方案2】:

    如果SendMessage失败,它会设置最后一个win32错误代码,我们可以通过调用GetLastError()来取回。并返回错误代码,万一失败,永远不会是 0 (NOERROR),尽管这很明显没有记录。但是如何检测到 SendMessage 是失败的?这里我们不能基于返回值。但我们可以在调用SendMessage 之前SetLastError(NOERROR) 并在之后检查GetLastError():

    • 如果 SendMessage 失败 - GetLastError() 返回的值将是 不是 0。(在这种情况下最常见 ERROR_INVALID_WINDOW_HANDLE 或 ERROR_ACCESS_DENIED)。
    • 如果我们仍然得到 0 作为最后一个错误 - 这意味着该消息是 顺利交付。
    • 但是,可能的情况是,在SendMessage 调用期间,某些窗口过程 在当前线程中将被调用并且 窗口过程中的代码,将最后一个错误设置为非 0 值。所以我们 得到最后不是 0 最后一个错误,但不是因为 SendMessage 失败。 区分这两种情况很成问题

    -

        SetLastError(NOERROR);
        LRESULT lr = SendMessageW(hwnd, *, *, *);
        ULONG dwErrorCode = GetLastError();
        if (dwErrorCode == NOERROR)
        {
            DbgPrint("message was delivered (%p)\n", lr);
        }
        else
        {
            DbgPrint("fail with %u error\n", dwErrorCode);
        }
    

    【讨论】:

    • 如果这是一个跨线程调用,当然它会工作得很好,因为没有可以调用SetLastError()的中间API调用。但是如果发送到当前线程拥有的窗口,窗口过程不能做一些改变最后一个错误代码的事情,并且无法区分SendMessage()本身设置的最后一个错误代码吗?
    • @BenVoigt - 是的,你说得对,如果我们向当前线程拥有的窗口发送消息 - 将在当前线程上调用窗口过程,并且可能将错误代码更改为非 0。我不认为关于这个案子的开始。真的这是打破我的“解决方案”。需要多想。仅可能是远程窗口的主要问题 - 对于当前线程窗口,这通常不是实际问题
    • @ben:即使在SendMessage 是跨线程调用的情况下,这也不能可靠地工作,因为SendMessage 将调度传入的跨线程消息(请参阅When can a thread receive window messages?)。
    • @IInspectable - 是的,你是对的。不幸的是现在不能删除自我回答(因为它被接受了)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多