win32 ShellExecute 从队列中删除消息

问题描述 投票:0回答:1

调用 ShellExecute 似乎会从 Message Queue 中删除消息。下面提供了用于测试此行为的 C 程序的源代码。请注意,我使用 ShellExecute 在用户的默认浏览器中打开 URL(似乎是执行此操作的推荐 API)。

我有问题:

  • 这是一个错误吗?
  • 如果是一个错误,它会影响多少个版本的 Windows?
  • 如果是错误,我如何向 Microsoft 报告?

一方面,这似乎不太可能是一个错误,因为它可能会影响足够多的程序来修复(如果它发生在足够多的 Windows 版本上)。另一方面,删除消息只会在某些时候引起问题(即,如果队列为空或当时只有不重要的消息,则不会出现问题)。我也找不到任何提及此行为的文档,也找不到在具有消息队列的线程上调用 ShellExecute 存在任何问题的文档。此外,ShellExecute 可能不会经常被调用,这使得此清单更加间歇性。

如果您能够在另一个版本的 Windows(我的版本是 10.0.19045)上自行编译/运行该程序并报告结果,这将很有帮助。如果它重现了问题,您将在末尾看到“错误:”消息。

我还尝试通过“开发者平台”>“API 反馈”类别中的“反馈中心”向 Microsoft 提交错误,但如果有人知道提交此潜在错误的更合适的位置,请告诉我。

// Compile from Visual Studio Command Prompt with:
//
//     cl main.c
//
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "shell32.lib")

#define UNICODE
#include <windows.h>
#include <stdio.h>

static unsigned DropMessages()
{
    unsigned count = 0;
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        printf("    droping message %d\n", msg.message);
        count++;
    }
    if (count == 0) {
        printf("    empty (no messages were dropped)\n");
    }
    return count;
}

// NOTE: using wWinMain doesn't help
//int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, WCHAR *pCmdLine, int nCmdShow)
int main()
{
    //
    // Show that the message queue starts out empty
    //
    printf("dropping messages, it should start out empty:\n");
    {
        unsigned dropped = DropMessages();
        if (dropped != 0) abort();
    }

    //
    // Post a message to the queue and show that it's in there
    //
    printf("posting message\n");
    if (!PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0)) {
        printf("PostThreadMessage failed, error={}\n", GetLastError());
        return -1;
    }
    printf("dropping messages, it should drop 1:\n");
    {
        unsigned dropped = DropMessages();
        if (dropped != 1) abort();
    }
    
    //
    // Post a message to the queue, call ShellExecute which clears the queue!?!
    //
    printf("posting message\n");
    if (!PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0)) {
        printf("PostThreadMessage failed, error={}\n", GetLastError());
        return -1;
    }

    static const int enable_bug = 1;
    if (enable_bug) {
        printf("calling ShellExecute, this clears the message queue!?!\n");
        INT_PTR result = (INT_PTR)ShellExecute(NULL, NULL, NULL, NULL, NULL, 0);
        if (result <= 32) {
            printf("ShellExecute failed, result={}, error={}\n", result, GetLastError());
            return -1;
        }
    }
    
    printf("dropping messages, you'd think it should have 1 but:\n");
    {
        unsigned dropped = DropMessages();
        if (dropped == 1) {
            printf("Success!\n");
        } else {
            printf("Error: expected to drop exactly 1 message but dropped %d\n", dropped);
        }

    }
    return 0;
}
winapi message-queue shellexecute
1个回答
0
投票

正如 IInspectable 所说,

ShellExecute
将始终在调用线程上实例化 COM 对象。由于您无法再控制线程而丢失消息。

文档对此进行了解释:由于 ShellExecute 可以将执行委托给使用组件对象模型 (COM) 激活的 Shell 扩展(数据源、上下文菜单处理程序、动词实现),因此应在调用 ShellExecute 之前初始化 COM。某些 Shell 扩展需要 COM 单线程单元 (STA) 类型。

而且它并不特定于

ShellExecute
。启动模式对话框将表现出相同的行为

所以丢失消息是设计使然。

© www.soinside.com 2019 - 2024. All rights reserved.