• 使用PostThreadMessage在Win32线程间传递消息


    PostThreadMessage的原型是这样的

    BOOL PostThreadMessage( DWORD idThread,
        UINT Msg,
        WPARAM wParam,
        LPARAM lParam
    );

    PostThreadMessage可以用于线程之间的异步通讯,因为它不用等待调用者返回,
    这也许是线程通讯中最简单的一种方法了。

    但是要注意以下问题
    1 .PostThreadMessage有时会失败,报1444错误(Invalid thread identifier. )
    其实这不一定是线程不存在的原因,也有可能是线程不存在消息队列(message queue)造成的。
    事实上,并不是每个thread都有message queue,那如何让thread具有呢?
    答案是,至少调用message相关的function一次,比如GetMessage,PeekMessage。

    2.如果是post动态分配的memory给另外一个thread,要注意内存的正确释放。

    3.PostThreadMessage不能够post WM_COPYDATE之类的同步消息,否则会报错

    4.最好不要使用PostThreadMessage post message给一个窗口,使用PostMessage替代。

    下面是我写的一个比较严整的例子,仅供参考。

    #include <windows.h>
    #include <cstdio>
    #include <process.h>

    #define MY_MSG WM_USER+100
    const int MAX_INFO_SIZE = 20;

    HANDLE hStartEvent; // thread start event

    // thread function
    unsigned __stdcall fun(void *param)
    {
        printf("thread fun start ");

        MSG msg;
        PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

        if(!SetEvent(hStartEvent)) //set thread start event 
        {
            printf("set start event failed,errno:%d ",::GetLastError());
            return 1;
        }
        
        while(true)
        {
            if(GetMessage(&msg,0,0,0)) //get msg from message queue
            {
                switch(msg.message)
                {
                case MY_MSG:
                    char * pInfo = (char *)msg.wParam;
                    printf("recv %s ",pInfo);
                    delete[] pInfo;
                    break;
                }
            }
        };
        return 0;
    }

    int main()
    {
        HANDLE hThread;
        unsigned nThreadID;

        hStartEvent = ::CreateEvent(0,FALSE,FALSE,0); //create thread start event
        if(hStartEvent == 0)
        {
            printf("create start event failed,errno:%d ",::GetLastError());
            return 1;
        }

        //start thread
        hThread = (HANDLE)_beginthreadex( NULL, 0, &fun, NULL, 0, &nThreadID );
        if(hThread == 0)
        {
            printf("start thread failed,errno:%d ",::GetLastError());
            CloseHandle(hStartEvent);
            return 1;
        }

        //wait thread start event to avoid PostThreadMessage return errno:1444
        ::WaitForSingleObject(hStartEvent,INFINITE);
        CloseHandle(hStartEvent);

        int count = 0;
        while(true)
        {
            char* pInfo = new char[MAX_INFO_SIZE]; //create dynamic msg
            sprintf(pInfo,"msg_%d",++count);
            if(!PostThreadMessage(nThreadID,MY_MSG,(WPARAM)pInfo,0))//post thread msg
            {
                printf("post message failed,errno:%d ",::GetLastError());
                delete[] pInfo;
            }
            ::Sleep(1000);
        }

        CloseHandle(hThread);
        return 0;
    }
     
     
     

    提示:有关函数及其参数介绍大家直接百科就行了,这里只介绍具体用法。

    一、SendMessage

    首先我们来学习一下如何使用SendMessage函数,这个函数在线程中调用后,消息发出到消息接收函数,

    该线程必须等到消息接收函数执行完毕才能继续向下执行。这也是它和PostThreadMessage最大的区别。

    后者只需要抛出消息,然后继续执行,不需等待接受消息函数执行完。

    比较完整的做法,六步就可执行完毕,希望读者有些耐心,并且能真正理解其用法的含义。

    1.

    //首先要自定义一个用户消息,我们一般用WM_USER +n来定义消息ID,n要大于100,小于100的已经被系统消息ID占用。

    该操作一般位于头文件中。

    #define WM_USERLOGIN_RSP WM_USER+102 //账号登录响应

    2.

    //声明一个函数用于接收消息。一般也放在头文件中

    afx_msg LRESULT OnUserLogin(WPARAM wParam,LPARAM lParam);

    3.

    //给消息绑定接收函数

    ON_MESSAGE(WM_USERLOGIN_RSP,OnUserLogin)

    如果是基于MFC编程,在

    //将消息和函数绑定

    BEGIN_MESSAGE_MAP(CTraVariety, CDialog)

    //{{AFX_MSG_MAP(CTraVariety)

    、、、、、、、、

    //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

    里面添加这句代码即可

    4.

    //定义消息接收函数,该函数位于下面的第5步中 句柄所代表的区域

    LRESULT CTraVariety::OnUserLogin(WPARAM wParam,LPARAM lParam)//登录成功消息

    {

    CString *nameMsg = (CString *)wParam;

    /*********/

    }

    5.该句柄要包含上述第4步中的消息接收函数

    //确定一个用来接收消息的句柄,

    比如我们可以这样做:

    HWND m_hMainWnd;//句柄变量

    //给句柄赋值

    void CTraVariety::CTPInitWnd(CTraderSpi* UserSpi)

    {

    // 将pUserSpi的消息响应窗口设置为本窗口  //传递主窗口句柄:

    UserSpi->SetHwnd(this->m_hWnd);

    }

    SetHwnd()函数定义如下:

    void CTraderSpi::SetHwnd(HWND hWnd)

    {

    m_hMainWnd = hWnd;

    }

    6.最后,你就可以随意使用SendMessage了,第一个参数是你想让这个消息发送到哪个句柄,第二个参数是消息的名,

    第三个参数是发送的数据

    //发送自定义消息

    ::SendMessage(m_hMainWnd,WM_USERLOGIN_RSP,(WPARAM)nameMsg,NULL);

    二、PostThreadMessage用法

    学会了SendMessage的用法,PostThreadMessage其实是一样的,不过SendMessage函数的第一个参数是句柄,

    是struct HWND__ *类型,但是PostThreadMessage的第一个参数是DWORD类型,用来表示线程ID,

    所以其使用一般是结合多线程来使用,所以我们有必要谈一下CreateThread()这个函数。

    CreateThread()属于Windows Api,其实并不建议大家使用,更经常使用的是_beginThread函数,

    这个函数属于CRT(c running time)函数,是编译器带的,但是底层同样调用了CreateThread,

    这个我们先不讨论,有关_beginThread使用方法大家可以参考我的另一篇文章——使用_beginThread创建自己的Thread基类。

    (不发链接了(*^__^*) 每次发链接都被审核,童鞋们有兴趣就自己翻一翻)。

    比如现在我们要创建一个日志线程,专门用来存储日志,TradeProc()作为消息接收入口函数,

    接收其他各个线程发送来的数据,并不影响其他线程继续工作。

    1.

    //首先要声明变量:

    HANDLE  m_hTradeThread; //用来表示线程

    DWORD m_dwTradeId; //用来记录线程ID

    2.

    //使用CreateThread将线程和ID绑定到函数TradeProc上,函数名就是入口地址(不多解释)

    m_hTradeThread = CreateThread( NULL,0,TradeProc,(LPVOID)this,0,&m_dwTradeId); //日志线程

    if (m_hTradeThread == NULL)

    {

    AfxMessageBox("创建日志记录线程失败!");

      }

    3.入口函数的定义

    //当然函数要提前声明或者位置要在绑定的前方

    DWORD WINAPI TradeProc(LPVOID lpParameter) //日志线程函数

    {

    MSG msg;

    while(GetMessage(&msg,NULL,0,0))

    {

    //做你想做的事情

    //such as  :   Public.CreateRecord(LogRecord);

    }

    return 1;

    }

    其中的GetMessage()专门用来接收PostThreadMessage()发出的消息,当没有消息发出时,该线程一直处于阻塞状态,

    一旦接收到消息,就可以根据消息类型或内容做你想做的事儿。我一般的做法是:创建一个主接收消息线程,

    给不同的消息(数据)标识不同的类型,接收到消息后根据消息类型立即抛出到相对应的子线程中,

    不影响下一次的消息接收,并可以效率比较高,但是可能会耗费内存喔。当然这些设计思想要根据实际的需求,最后还得取决于你。

    4.

    最后,你又可以随意使用PostThreadMessage()了:

    PostThreadMessage(m_dwTradeId,0,(WPARAM)m_tuse,1); //发送消息

     
     
     
  • 相关阅读:
    java Jquery表单校验代码jsp页面
    IntelliJ IDEA 2016.1.1(64) 长时间激活教程
    maven 仓库
    java学习路线
    json 源码包
    centos 安装docker
    实现高并发
    将MongoDB安装成为Windows服务
    给mongodb设置密码权限
    MongoDB的win安装教程
  • 原文地址:https://www.cnblogs.com/shikamaru/p/7651525.html
Copyright © 2020-2023  润新知