【问题标题】:How to return a WM_COPYDATA message instantly and also call a function?如何立即返回 WM_COPYDATA 消息并调用函数?
【发布时间】:2021-09-18 18:34:40
【问题描述】:

如何在收到WM_COPYDATA消息“即时”时返回响应并调用函数?

我尝试使用chrono,但发送消息的应用程序仅在sendCommand函数执行后才收到响应。

#include <thread>
#include <future>
#include <iostream>

void sendCommand(std::chrono::seconds delay, std::string cmd)
{
     std::this_thread::sleep_for( delay );
     std::cout << "\nThe waited command is =" << cmd;
}


switch (msg)
{
    case WM_COPYDATA:
    {
        OutputDebugStringW(L"\nWM_COPYDATA!");
        PCOPYDATASTRUCT pcds = reinterpret_cast<PCOPYDATASTRUCT>(lParam);
        //....

        auto s1 = std::async(std::launch::async, sendCommand, std::chrono::seconds(5), "Command1");

        return 1;
    }
}

【问题讨论】:

  • 在您的代码示例中,您没有对pcds 做任何事情(它指向WM_COPYDATA 提供的数据)。函数sendCommand 是否可以访问这些数据?您的问题的答案在很大程度上取决于此。在我的回答中,我假设您不想丢弃 WM_COPYDATA 传递的数据,但您希望在函数 sendCommand 被调用时访问该数据。这使得必须复制该数据,因为原始指针仅在 WM_COPYDATA 处理程序内有效。

标签: c++ winapi


【解决方案1】:

如果你不介意阻塞接收WM_COPYDATA的线程,你可以简单地使用ReplyMessage()

#include <iostream>

void sendCommand(std::chrono::seconds delay, std::string cmd)
{
    std::this_thread::sleep_for( delay );
    std::cout << "\nThe waited command is =" << cmd;
} 

switch (msg)
{
    case WM_COPYDATA:
    {
        OutputDebugStringW(L"WM_COPYDATA!");
        PCOPYDATASTRUCT pcds = reinterpret_cast<PCOPYDATASTRUCT>(lParam);
        //...

        ReplyMessage(1);
        sendCommand(std::chrono::seconds(5), "Command1");

        return 1;
    }
}

【讨论】:

    【解决方案2】:

    在这个答案中,我假设您不想丢弃 WM_COPYDATA 发送的数据,但希望您调用的函数能够访问这些数据。

    如果您允许WM_COPYDATA处理程序在返回之前复制数据,则不必使用多线程来解决此问题。

    WM_COPYDATA 处理程序可以做的是

    1. 动态分配内存用于复制数据,
    2. 然后实际复制数据,
    3. 然后使用PostMessage将包含指向复制数据的指针的用户定义消息发布到消息队列,
    4. 然后立即返回,无需调用函数处理数据。

    稍后在线程的消息循环中处理用户定义的消息时,用户定义的消息的处理程序可以调用函数来处理数据,然后在函数返回后释放动态分配的数据。

    必须在WM_COPYDATA处理程序中复制数据,因为一旦处理程序返回,指向数据的指针就会失效。

    【讨论】:

    • 使用多线程有什么缺点吗?
    • @Razec:是的,使用多线程有很大的缺点,例如在访问共享变量时需要使用thread synchronization 来防止race conditions。但是,使用多线程也有一些优势,例如一次可以使用多个 CPU。这一切都取决于情况。
    • @Razec:在你的情况下,如果你想让你的线程做的只是睡眠一定的秒数,打印一条消息并退出,那么使用多线程可能是可以的。然而,更复杂的东西,特别是如果它涉及与其他线程共享数据,就不是那么容易了。因此,除非您有特殊原因,否则通常不应使用多线程。
    【解决方案3】:

    您要查找的内容称为 lambda 函数 (https://en.cppreference.com/w/cpp/language/lambda),如下所示:

    使用 std::thread:

      std::thread t([]
      {
        sendCommand(std::chrono::seconds(5), "Command1");
      });
    

    使用 std::async 具有共享未来的解决方案,通过捕获它,未来的生命周期被延长到 lambda 的生命周期。 如果您在 windows/msvc 上,则使用线程池中的线程有一点好处。否则就使用线程解决方案。

        #include <memory> 
        
        auto ft = std::make_shared<std::future<void>>();
        *ft = std::async(std::launch::async, [ft]    
        {
            sendCommand(std::chrono::seconds(5), "Command1");
        });
    

    【讨论】:

    • 同理,发送应用只在函数执行后才收到消息。
    • 哦,是的,令人讨厌的细节:en.cppreference.com/w/cpp/thread/async 如果从 std::async 获得的 std::future 没有从引用中移动或绑定到引用,则 std::future 的析构函数将阻塞在完整表达式的末尾,直到异步操作完成,本质上使如下代码同步。你也可以使用 std::thread... lemme check
    • 好吧,我自己也遇到过这个问题。我想出了一个解决方案,我把它放在了答案中。但是,如果您只是 std::thread,对您来说可能更容易理解。示例更新
    猜你喜欢
    • 1970-01-01
    • 2014-04-08
    • 2015-11-21
    • 2016-08-11
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多