/***********************************************************************************************************************************

学习课程:孙鑫老师的VC++教程的第十五课《多线程编程》

遭遇问题:课程的源代码是在VC6.0中编写的,在VC++2010Unicode(默认)编码的开发环境中不能通过,有的地方需要作出相应的改变。

 ****************************************************************************************************************************************/

 

改动1课程中用到一个自写的消息映射,在头文件的声明与在源文件中添加的消息映射分别如下:

1.声明:afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);

2.消息映射:ON_MESSAGE(WM_RECVDATA,OnRecvData)

VC++2010中,消息的检查更为严格,这样的消息映射在检查时不允许通过。在VC++2010中,OnMessage返回值必须为LRESULT,其形式为:

afx_msg LRESULT OnMessage(WPARAM, LPARAM);

相应的消息映射应改为:

         ON_MESSAGE(WM_RECVDATA,&CChatRoomDlg::OnRecvData)

改动2在发送按钮的消息相应函数中,有如下代码:

         sendto(m_socket,strSend,strSend.GetLength()+1,0,

                   (SOCKADDR*)&addrTo,sizeof(SOCKADDR));

sreSendCString对象,而sendto的第二个参数所要类型为char*CString在多字节编码的开发环境中可以默认转换为char*类型,但在Unicode环境中便会报错。所以需要进行强制转换,改动后的代码如下:

         sendto(m_socket,(char*)strSend.GetBuffer(),strSend.GetLength()*2+2,0, 

//UNICODE下不支持(char*)strSend这样的强制转换

//发送的长度也应为原来的两倍

                   (SOCKADDR*)&addrTo,sizeof(SOCKADDR));

         strSend.ReleaseBuffer();          //当调用GetBuffer后在合适的地方ReleaseBuffer是个好习惯

改动3在线程函数RecvProc中,最后把相关数据用SetDlgItemText函数显示在编辑框中时,由于SetDlgItemText需要的是宽字符类型,接收到的IP信息与消息内容必须都转换为宽字符类型,详细请参考改动后的注释。

课程原来的代码如下:

DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)

{

         SOCKET sock=((RECVPARAM*)lpParameter)->sock;

         HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;

         delete lpParameter; 

 

         SOCKADDR_IN addrFrom;

         int len=sizeof(SOCKADDR);

 

         char recvBuf[200];

         char tempBuf[300];

         int retval;

         while(TRUE)

         {

                   retval=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);

                   if(SOCKET_ERROR==retval)

                            break;

                   sprintf(tempBuf,"%s: %s",inet_ntoa(addrFrom.sin_addr),recvBuf);

                   ::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

         }

         return 0;

}

改动后的代码如下:

DWORD WINAPI CChatRoomDlg::RecvProc(LPVOID lpParameter)

{

         SOCKET sock=((RECVPARAM*)lpParameter)->sock;

         HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;

         delete lpParameter;

 

         SOCKADDR_IN addrFrom;

         int len=sizeof(SOCKADDR);

 

         wchar_t recvBuf[200];

         wchar_t tempBuf[200];

         //wchar_t ipstr[100];

         //DWORD i=100;

 

         int retval;

         while(TRUE)

         {

                   retval=recvfrom(sock,(char*)recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);

                   if(SOCKET_ERROR==retval)

                   break;

//也可以用这个函数来获取发送端的地址信息,

//地址信息将保存在ipstr中,不仅包括ip地址,还有端口

                   //ipstr的类型为宽字符类型,在格式化输出的时候不需要做类型转换

 

                   //WSAAddressToString((SOCKADDR*)&addrFrom,len,NULL,ipstr,&i);

                   //因为inet_ntoa函数返回的ip地址是char*类型,

//需要转换成wchar_T*类型以便格式

                   //化输出,用函数MultiByteToWideChar来实现

                   char *ip=inet_ntoa(addrFrom.sin_addr);

                   wchar_t *wIp=new wchar_t[100];  //比如长度100

                   memset(wIp,0,sizeof(wchar_t*));

                   MultiByteToWideChar(CP_ACP,0,ip,-1,wIp,16);      //点分10进制格式地址最长也就15                                                       

                                                                                                                //位(如"192.233.156.256"),故最后一个参数传16足够

 

                   wsprintf(tempBuf,L"%s说:%s",wIp/*ipstr*/,recvBuf);

                    delete[] wIP;                                      //注意释放空间

                   ::PostMessageA(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

         }

         return 0;

}