使用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 \n");

    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\n",::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\n",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\n",::GetLastError());
        return 1;
    }

    //start thread
    hThread = (HANDLE)_beginthreadex( NULL, 0, &fun, NULL, 0, &nThreadID );
    if(hThread == 0)
    {
        printf("start thread failed,errno:%d\n",::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\n",::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); //发送消息

 
 
 

转载于:https://www.cnblogs.com/shikamaru/p/7651525.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值