收发Jabber消息流程

原创 2007年09月25日 14:44:00

 

1.   聊天对话框基本信息:
1)        对话框模板:  IDD_MSG
2)        窗口过程:  DlgProcMessage
3)        “发送”按钮: IDOK
 
在聊天对话框初始化的时候关于该联系人所用协议等信息已经做为该窗口的用户数据设置好了,用到的时候只要以GWL_USERDATA 为参数调用GetWindowLong就可以得到该用户数据的指针,具体的过程参考<Miranda UI分析>中的 聊天窗口 创建部分, 该文主要关注的是jabber消息的收发流程.
2.   发送消息:
2.1.IDOK 发送 按钮
case IDOK:
                if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDOK)))
                    break;
                {
                    int flags = SEND_FLAGS;
                    //this is a 'send' button
                    int bufSize = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MESSAGE)) + 1;
                    dat->sendBuffer = (char *) realloc(dat->sendBuffer, bufSize * (sizeof(TCHAR) + 1));
                    dat->bIsRtl = 0;
                    GetDlgItemTextA(hwndDlg, IDC_MESSAGE, dat->sendBuffer, bufSize);
                    if (dat->sendBuffer[0] == 0)
                        break;
                        dat->cmdList = tcmdlist_append(dat->cmdList, dat->sendBuffer);
                    dat->cmdListCurrent = 0;
                    if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) {
                        NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF);
                    }
 
                    if (dat->hContact == NULL)
                        break;      //never happens
                    dat->sendCount = 1;
                    //发送消息
                    dat->hSendId = (HANDLE) CallContactService(dat->hContact, MsgServiceName(dat->hContact), flags, (LPARAM) dat->sendBuffer);
                    EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
                    SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, TRUE, 0);
 
                    //create a timeout timer
                    SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL);
                    if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN))
                        ShowWindow(hwndDlg, SW_MINIMIZE);
                }
                return TRUE;
 
2.2.CallContactService
CallContactService
Proto_CallContactService
 JabberSendMessage
2.3.JabberSendMessage
int JabberSendMessage( WPARAM wParam, LPARAM lParam )
{
       CCSDATA *ccs = ( CCSDATA * ) lParam;
       JABBER_LIST_ITEM *item;
       int id;
 
       DBVARIANT dbv;
       if ( !jabberOnline || JGetStringT( ccs->hContact, "jid", &dbv )) {
              JSendBroadcast( ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE ) 1, 0 );
              return 0;
       }
 
       char* pszSrc = ( char* )ccs->lParam, *msg;
       int isEncrypted;
 
       char* pdest = strstr( pszSrc, PGP_PROLOG );//pdest-string+1 is index of first occurence
       if ( pdest != NULL ) {
              pdest = strstr( pszSrc, PGP_EPILOG );
              int result = ( pdest ) ? strlen( PGP_PROLOG ) : 0;
 
              char* tempstring = ( char* )alloca( strlen( pszSrc ));
              strncpy( tempstring, pszSrc+strlen(PGP_PROLOG), strlen(pszSrc)-strlen(PGP_EPILOG)-result );
              pszSrc = tempstring;
              isEncrypted = 1;
       }
       else isEncrypted = 0;
 
       if ( ccs->wParam & PREF_UNICODE )
              msg = JabberTextEncodeW(( wchar_t* )&pszSrc[ strlen( pszSrc )+1 ] );
       else
              msg = JabberTextEncode( pszSrc );
 
       if ( msg != NULL ) {
              char msgType[ 16 ];
              if ( JabberListExist( LIST_CHATROOM, dbv.ptszVal ) && _tcschr( dbv.ptszVal, '/' )==NULL )
                     strcpy( msgType, "groupchat" );
              else
                     strcpy( msgType, "chat" );
 
              //封装为xml格式消息
XmlNode m( "message" ); m.addAttr( "type", msgType );
              if ( !isEncrypted ) {
                     XmlNode* body = m.addChild( "body" );
                     body->sendText = msg;
              }
              else {
                     m.addChild( "body", "[This message is encrypted.]" );
                     XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:encrypted" );
                     x->sendText = msg;
              }
 
              XmlNode* active = m.addChild( "active" ); active->addAttr( "xmlns", _T("http://jabber.org/protocol/chatstates"));
 
              if ( !strcmp( msgType, "groupchat" ) || !JGetByte( "MsgAck", FALSE ) || !JGetByte( ccs->hContact, "MsgAck", TRUE )) {
                     if ( !strcmp( msgType, "groupchat" ))
                            m.addAttr( "to", dbv.ptszVal );
                     else {
                            id = JabberSerialNext();
                            TCHAR szClientJid[ 256 ];
                            JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
 
                            m.addAttr( "to", szClientJid ); m.addAttrID( id );
                            XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" ); x->addChild( "composing" );
                     }
 
                     jabberThreadInfo->send( m );
                     mir_forkthread( JabberSendMessageAckThread, ccs->hContact );
              }
              else {
                     id = JabberSerialNext();
                     if (( item=JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL )
                            item->idMsgAckPending = id;
 
                     TCHAR szClientJid[ 256 ];
                     JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
                     m.addAttr( "to", szClientJid ); m.addAttrID( id );
 
                     XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" );
                     x->addChild( "composing" ); x->addChild( "delivered" ); x->addChild( "offline" );
                     jabberThreadInfo->send( m );
       }     }
 
       JFreeVariant( &dbv );
       return 1;
}
 
2.4.jabberThreadInfo->send( m )
2.4.1.    ThreadData::send
int ThreadData::send( XmlNode& node )
{
       if ( this == NULL )
              return 0;
 
       char* str = node.getText();
       int size = strlen( str ), result;
 
       EnterCriticalSection( &iomutex );
 
       if ( ssl != NULL ) {
              if ( DBGetContactSettingByte( NULL, "Netlib", "DumpSent", TRUE ) == TRUE ) {
                     char* szLogBuffer = ( char* )alloca( size+32 );
                     strcpy( szLogBuffer, "( SSL ) Data sent/n" );
                     memcpy( szLogBuffer+strlen( szLogBuffer ), str, size+1 ); // also copy /0
                     Netlib_Logf( hNetlibUser, "%s", szLogBuffer );   // %s to protect against when fmt tokens are in szLogBuffer causing crash
              }
 
              result = pfn_SSL_write( ssl, str, size );
       }
       else result = JabberWsSend( s, str, size );
       LeaveCriticalSection( &iomutex );
 
       mir_free( str );
       return result;
}
 
 
 
 
2.4.1.1.          JabberWsSend
int JabberWsSend( JABBER_SOCKET hConn, char* data, int datalen )
{
       int len;
 
       if (( len=Netlib_Send( hConn, data, datalen, MSG_DUMPASTEXT ))==SOCKET_ERROR || len!=datalen ) {
              JabberLog( "Netlib_Send() failed, error=%d", WSAGetLastError());
              return FALSE;
       }
       return TRUE;
}
 
2.4.1.1.1.       Netlib_Send
static __inline int Netlib_Send(HANDLE hConn,const char *buf,int len,int flags) {
       NETLIBBUFFER nlb={(char*)buf,len,flags};
       return CallService(MS_NETLIB_SEND,(WPARAM)hConn,(LPARAM)&nlb);
}
 
2.4.1.1.1.1.   NetlibSend
int NetlibSend(WPARAM wParam,LPARAM lParam)
{
       struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
       NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam;
       int result;
 
       if(nlb==NULL) {
              SetLastError(ERROR_INVALID_PARAMETER);
              return SOCKET_ERROR;
       }
 
       if(!NetlibEnterNestedCS(nlc,NLNCS_SEND)) return SOCKET_ERROR;
       if(nlc->usingHttpGateway && !(nlb->flags&MSG_RAW)) {
              if(!(nlb->flags&MSG_NOHTTPGATEWAYWRAP) && nlc->nlu->user.pfnHttpGatewayWrapSend) {
                     NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags);
                     result=nlc->nlu->user.pfnHttpGatewayWrapSend((HANDLE)nlc,nlb->buf,nlb->len,nlb->flags|MSG_NOHTTPGATEWAYWRAP,NetlibSend);
              }
              else result=NetlibHttpGatewayPost(nlc,nlb->buf,nlb->len,nlb->flags);
       }
       else {
              NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags);
              result=send(nlc->s,nlb->buf,nlb->len,nlb->flags&0xFFFF);
       }
       NetlibLeaveNestedCS(&nlc->ncsSend);
       return result;
}
2.4.1.1.1.1.1.send
最终调用socket api函数send,发送消息
2.5.JabberSendMessageAckThread
 
3.   发送超时处理:
3.1.设置超时timer
case WM_COMMAND:
            ……
            switch (LOWORD(wParam))
{
            case IDOK:
                    ……
                    //create a timeout timer
                    SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL);
                    if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN))
                        ShowWindow(hwndDlg, SW_MINIMIZE);
                }
                return TRUE;
 
 
3.2.响应超时timer
    case WM_TIMER:
        if (wParam == TIMERID_MSGSEND)
{
            KillTimer(hwndDlg, wParam);
            ShowWindow(hwndDlg, SW_SHOWNORMAL);
            EnableWindow(hwndDlg, FALSE);
            CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGSENDERROR), hwndDlg, ErrorDlgProc, (LPARAM) strdup( Translate("The message send timed out.")));
        }
        ……
 
4.   接收消息:
JabberServerThread函数中有一个for(;;)死循环,用于接收消息
JabberServerThread
JabberServerThread 是Jabber里非常重要的一个线程, 注册登录接收消息等都在这个线程里, 接收消息的线程是在设置jabber用户在线状态(即登录)时创建出来的,详细参考<注册注销登录退出jabber>. 在程序里加了注释, 仔细分析代码吧:
void __cdecl JabberServerThread( ThreadData* info )
{
……
 
        //zhangcong//main recv loop, 出现异常时,将break loop
              for ( ;; )
        {
                     int recvResult = info->recv( buffer+datalen, jabberNetworkBufferSize-datalen);
            int bytesParsed;
                     JabberLog( "recvResult = %d", recvResult );
                     if ( recvResult <= 0 )
                            break;
                     datalen += recvResult;
 
                     buffer[datalen] = '/0';
                     if ( info->ssl && DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", TRUE ) == TRUE )
            {
                            // Emulate netlib log feature for SSL connection
                            char* szLogBuffer = ( char* )mir_alloc( recvResult+128 );
                            if ( szLogBuffer != NULL )
                {
                                   strcpy( szLogBuffer, "( SSL ) Data received/n" );
                                   memcpy( szLogBuffer+strlen( szLogBuffer ), buffer+datalen-recvResult, recvResult+1 /* also copy /0 */ );
                                   Netlib_Logf( hNetlibUser, "%s", szLogBuffer );   // %s to protect against when fmt tokens are in szLogBuffer causing crash
                                   mir_free( szLogBuffer );
                }    
            }
 
            //XmlParse接收到的数据
                     bytesParsed = JabberXmlParse( &xmlState, buffer );
                     JabberLog( "bytesParsed = %d", bytesParsed );
                     if ( bytesParsed > 0 )
            {
                            if ( bytesParsed < datalen )
                                   memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
                            datalen -= bytesParsed;
                     }
                     else if ( datalen == jabberNetworkBufferSize )
            {
                //如果缓冲区已满, 扩充缓冲区
                            jabberNetworkBufferSize += 65536;
                            JabberLog( "Increasing network buffer size to %d", jabberNetworkBufferSize );
                            if (( buffer=( char* )mir_realloc( buffer, jabberNetworkBufferSize+1 )) == NULL ) {
                                   JabberLog( "Cannot reallocate more network buffer, go offline now" );
                                   break;
                }    
            }
                     else
            {
                JabberLog( "Unknown state: bytesParsed=%d, datalen=%d, jabberNetworkBufferSize=%d", bytesParsed, datalen, jabberNetworkBufferSize );
            }
 
                     if (xmlStreamToBeInitialized)
                xmlStreamInitializeNow(info);
              }
 
 ……
 
}
 
 
 
 
 
 
 
 
 
 
 
显示接收到的消息
接收消息是相对简单的, 在聊天窗口显示消息则是经历了两万五千里的长征之后显示出来的,下面详细分析显示消息的过程:
JabberXmlParse
接收到的消息是xml格式的, 需要先解析接收到的xml格式的消息, Jabber是用SAX方式来解析xml的, 处理节点的时候会调用一些事先设置好的回调函数, 处理的过程比较复杂, 这里仅仅关心最重要的回调函数
 
JabberXmlProcessElem
static BOOL JabberXmlProcessElem( XmlState *xmlState, XmlElemType elemType, char* elemText, char* elemAttr )
{
       ……
       switch ( elemType ) {
       case ELEM_OPEN:
              ……
              break;
       case ELEM_OPENCLOSE:
              ……
              break;
       case ELEM_CLOSE:
              if ( node->name!=NULL && !strcmp( node->name, elemText )) {
                     node->state = NODE_CLOSE;
                     if ( node->depth==1 && xmlState->callback1_close!=NULL ) {
                            ( *( xmlState->callback1_close ))( node, xmlState->userdata1_close );
                            JabberXmlRemoveChild( parentNode, node );
                     }
                     else if ( node->depth==2 && xmlState->callback2_close!=NULL ) {
                            // 调用JabberProcessProtocol
                            ( *xmlState->callback2_close )( node, xmlState->userdata2_close );
                            JabberXmlRemoveChild( parentNode, node );
              }     }
              else {
                     JabberLog( "XML: Closing </%s> without opening tag", elemText );
                     return FALSE;
              }
              break;
 
       case ELEM_TEXT:
              JabberUtfToTchar( elemText, strlen( elemText ), node->text );
              break;
 
       default:
              return FALSE;
       }
 
       return TRUE;
}
JabberProcessProtocol
static void JabberProcessProtocol( XmlNode *node, void *userdata )
{
       ThreadData* info = ( ThreadData* ) userdata;
       if ( !strcmp( node->name, "proceed" )) {
              JabberProcessProceed( node, userdata );
              return;
       }
 
       if ( !strcmp( node->name, "stream:features" ))
              JabberProcessFeatures( node, userdata );
       else if ( !strcmp( node->name, "success"))
              JabberProcessSuccess( node, userdata );
       else if ( !strcmp( node->name, "failure"))
              JabberProcessFailure( node, userdata );
       else if ( !strcmp( node->name, "stream:error"))
              JabberProcessError( node, userdata );
       else if ( !strcmp( node->name, "challenge" ))
              JabberProcessChallenge( node, userdata );
       else if ( info->type == JABBER_SESSION_NORMAL ) {
              if ( !strcmp( node->name, "message" ))
                     JabberProcessMessage( node, userdata );
              else if ( !strcmp( node->name, "presence" ))
                     JabberProcessPresence( node, userdata );
              else if ( !strcmp( node->name, "iq" ))
                     JabberProcessIq( node, userdata );
              else
                     JabberLog( "Invalid top-level tag ( only <message/> <presence/> and <iq/> allowed )" );
       }
       else if ( info->type == JABBER_SESSION_REGISTER ) {
              if ( !strcmp( node->name, "iq" ))
                     JabberProcessRegIq( node, userdata );
              else
                     JabberLog( "Invalid top-level tag ( only <iq/> allowed )" );
}     }
JabberProcessMessage
static void JabberProcessMessage( XmlNode *node, void *userdata )
{
……
              char* buf = ( char* )alloca( cbAnsiLen+1 + (cbWideLen+1)*sizeof( WCHAR ));
              memcpy( buf, szAnsiMsg, cbAnsiLen+1 );
              memcpy( buf + cbAnsiLen + 1, wszMessage, (cbWideLen+1)*sizeof( WCHAR ));
 
       ……
 
              PROTORECVEVENT recv;
              recv.flags = PREF_UNICODE;
              recv.timestamp = ( DWORD )msgTime;
              recv.szMessage = buf;
              recv.lParam = 0;
 
              CCSDATA ccs;
              ccs.hContact = hContact;
              ccs.wParam = 0;
              ccs.szProtoService = PSR_MESSAGE;
              ccs.lParam = ( LPARAM )&recv;
              JCallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs );
 
              mir_free( szMessage );
}
}
MS_PROTO_CHAINRECV
static int Proto_ChainRecv(WPARAM wParam,LPARAM lParam)
{
       /* this will switch threads just like before */
       return CallServiceSync(MS_PROTO_CHAINRECV "ThreadSafe",wParam,lParam);
}
 
CallServiceSync
int CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam)
{
 
       extern HWND hAPCWindow;
 
       if (name==NULL) return CALLSERVICE_NOTFOUND;
       // the service is looked up within the main thread, since the time it takes
       // for the APC queue to clear the service being called maybe removed.
       // even thou it may exists before the call, the critsec can't be locked between calls.
       if (GetCurrentThreadId() != mainThreadId) {
              TServiceToMainThreadItem item;
              item.wParam = wParam;
              item.lParam = lParam;
              item.name = name;
              item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
              QueueUserAPC(CallServiceToMainAPCFunc, hMainThread, (DWORD) &item);
              //post WM_NULL使hAPCWindow所属线程进入告警状态,从而使投递的异步函
//数CallServiceToMainAPCFunc得以执行
              PostMessage(hAPCWindow,WM_NULL,0,0); // let this get processed in its own time
              WaitForSingleObject(item.hDoneEvent, INFINITE);
              CloseHandle(item.hDoneEvent);
              return item.result;
       }
 
   return CallService(name, wParam, lParam);
}
 
CallServiceToMainAPCFunc
static void CALLBACK CallServiceToMainAPCFunc(DWORD dwParam)
{
       TServiceToMainThreadItem *item = (TServiceToMainThreadItem*) dwParam;
       // item->name : CallRecvChain
       item->result = CallService(item->name, item->wParam, item->lParam);
       SetEvent(item->hDoneEvent);
}
 
 CallRecvChain
static int CallRecvChain(WPARAM wParam,LPARAM lParam)
{
       CCSDATA *ccs=(CCSDATA*)lParam;
       int i,ret;
       char str[10];
       DBVARIANT dbv;
 
       if(wParam==(WPARAM)(-1)) return 1;   //shouldn't happen - sanity check
       if(wParam==0) {      //begin processing by finding end of chain
              for(;;wParam++) {
                     _itoa(wParam,str,10);
                     if(DBGetContactSetting(ccs->hContact,"_Filter",str,&dbv)) break;
                     mir_free(dbv.pszVal);
              }
       }
       else wParam--;
       for(i=wParam-1;i>=0;i--) {
              _itoa(i,str,10);
              if(DBGetContactSetting(ccs->hContact,"_Filter",str,&dbv)) return 1; //never happens
              if((ret=CallProtoService(dbv.pszVal,ccs->szProtoService,i+1,lParam))!=CALLSERVICE_NOTFOUND) {
                     //chain was started, exit
                     mir_free(dbv.pszVal);
                     return ret;
              }
              mir_free(dbv.pszVal);
       }
       //end of chain, call network protocol again
       if(DBGetContactSetting(ccs->hContact,"Protocol","p",&dbv)) return 1;
       if((ret=CallProtoService(dbv.pszVal,ccs->szProtoService,(WPARAM)(-1),lParam))!=CALLSERVICE_NOTFOUND) {
              mir_free(dbv.pszVal);
              return ret;
       }
       mir_free(dbv.pszVal);
       return 1;
}
    JabberRecvMessage
int JabberRecvMessage( WPARAM wParam, LPARAM lParam )
{
       CCSDATA *ccs = ( CCSDATA* )lParam;
       PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam;
 
       DBEVENTINFO dbei = { 0 };
       dbei.cbSize = sizeof( dbei );
       dbei.szModule = jabberProtoName;
       dbei.timestamp = pre->timestamp;
       dbei.flags = pre->flags&PREF_CREATEREAD?DBEF_READ:0;
       dbei.eventType = EVENTTYPE_MESSAGE;
       dbei.cbBlob = strlen( pre->szMessage ) + 1;
       if ( pre->flags & PREF_UNICODE )
              dbei.cbBlob *= ( sizeof( wchar_t )+1 );
 
       dbei.pBlob = ( PBYTE ) pre->szMessage;
       //保存到历史消息记录,同时在richedit中显示消息
       JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
       return 0;
}
 
      MS_DB_EVENT_ADD
//Db3x/dbevents.c
static int AddEvent(WPARAM wParam,LPARAM lParam)
{
       DBEVENTINFO *dbei=(DBEVENTINFO*)lParam;
       struct DBContact dbc;
       struct DBEvent dbe,*dbeTest;
       DWORD ofsNew,ofsModuleName,ofsContact,ofsThis;
 
       if(dbei==NULL||dbei->cbSize!=sizeof(DBEVENTINFO)) return (int)NULL;
       if(dbei->timestamp==0) return (int)NULL;
       if (NotifyEventHooks(hEventFilterAddedEvent,wParam,lParam)) {
              return (int)NULL;
       }
       EnterCriticalSection(&csDbAccess);
       if(wParam==0) ofsContact=dbHeader.ofsUser;
       else ofsContact=wParam;
       dbc=*(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL);
       if(dbc.signature!=DBCONTACT_SIGNATURE) {
              LeaveCriticalSection(&csDbAccess);
            return (int)NULL;
       }
       ofsNew=CreateNewSpace(offsetof(struct DBEvent,blob)+dbei->cbBlob);
       ofsModuleName=GetModuleNameOfs(dbei->szModule);
 
       dbe.signature=DBEVENT_SIGNATURE;
       dbe.ofsModuleName=ofsModuleName;
       dbe.timestamp=dbei->timestamp;
       dbe.flags=dbei->flags;
       dbe.eventType=dbei->eventType;
       dbe.cbBlob=dbei->cbBlob;
       //find where to put it - sort by timestamp
       if(dbc.eventCount==0) {
              dbe.ofsPrev=wParam;
              dbe.ofsNext=0;
              dbe.flags|=DBEF_FIRST;
              dbc.ofsFirstEvent=dbc.ofsLastEvent=ofsNew;
       }
       else {
              dbeTest=(struct DBEvent*)DBRead(dbc.ofsFirstEvent,sizeof(struct DBEvent),NULL);
              // Should new event be placed before first event in chain?
              if (dbei->timestamp < dbeTest->timestamp) {
                     dbe.ofsPrev=wParam;
                     dbe.ofsNext=dbc.ofsFirstEvent;
                     dbe.flags|=DBEF_FIRST;
                     dbc.ofsFirstEvent=ofsNew;
                     dbeTest=(struct DBEvent*)DBRead(dbe.ofsNext,sizeof(struct DBEvent),NULL);
                     dbeTest->flags&=~DBEF_FIRST;
                     dbeTest->ofsPrev=ofsNew;
                     DBWrite(dbe.ofsNext,dbeTest,sizeof(struct DBEvent));
              }
              else {
                     // Loop through the chain, starting at the end
                     ofsThis = dbc.ofsLastEvent;
                     dbeTest = (struct DBEvent*)DBRead(ofsThis, sizeof(struct DBEvent), NULL);
                     for(;;) {
                            // If the new event's timesstamp is equal to or greater than the
                            // current dbevent, it will be inserted after. If not, continue
                            // with the previous dbevent in chain.
                            if (dbe.timestamp >= dbeTest->timestamp) {
                                   dbe.ofsPrev = ofsThis;
                                   dbe.ofsNext = dbeTest->ofsNext;
                                   dbeTest->ofsNext = ofsNew;
                                   DBWrite(ofsThis, dbeTest, sizeof(struct DBEvent));
                                   if (dbe.ofsNext == 0)
                                          dbc.ofsLastEvent = ofsNew;
                                   else {
                                          dbeTest = (struct DBEvent*)DBRead(dbe.ofsNext, sizeof(struct DBEvent), NULL);
                                          dbeTest->ofsPrev = ofsNew;
                                          DBWrite(dbe.ofsNext, dbeTest, sizeof(struct DBEvent));
                                   }
                                   break;
                            }
                            ofsThis = dbeTest->ofsPrev;
                            dbeTest = (struct DBEvent*)DBRead(ofsThis, sizeof(struct DBEvent), NULL);
                     }
              }
       }
       dbc.eventCount++;
       if(!(dbe.flags&(DBEF_READ|DBEF_SENT))) {
              if(dbe.timestamp<dbc.timestampFirstUnread || dbc.timestampFirstUnread==0) {
                     dbc.timestampFirstUnread=dbe.timestamp;
                     dbc.ofsFirstUnreadEvent=ofsNew;
              }
       }
       DBWrite(ofsContact,&dbc,sizeof(struct DBContact));
       DBWrite(ofsNew,&dbe,offsetof(struct DBEvent,blob));
       DBWrite(ofsNew+offsetof(struct DBEvent,blob),dbei->pBlob,dbei->cbBlob);
       DBFlush(0);
       LeaveCriticalSection(&csDbAccess);
       log1("add event @ %08x",ofsNew);
       // dbaddedevent将被调用
       NotifyEventHooks(hEventAddedEvent,wParam,(LPARAM)ofsNew);
       return (int)ofsNew;
}
        Dbaddedevent
 
static int dbaddedevent(WPARAM wParam, LPARAM lParam) {
       if (wParam) {
              HWND h = WindowList_Find(g_dat->hMessageWindowList, (HANDLE)wParam);
              if(h) SendMessage(h, HM_DBEVENTADDED, wParam, lParam);
       }
       return 0;
}
 
          HM_DBEVENTADDED
Srmm/msgdialogs.c line 1374
 
DlgProcMessage
{
       ……
           case DM_APPENDTOLOG:   //takes wParam=hDbEvent
              StreamInEvents(hwndDlg, (HANDLE) wParam, 1, 1);
              break;
           case HM_DBEVENTADDED:
              if ((HANDLE) wParam != dat->hContact)
                     break;
              {
                     DBEVENTINFO dbei = { 0 };
 
                     dbei.cbSize = sizeof(dbei);
                     dbei.cbBlob = 0;
                     CallService(MS_DB_EVENT_GET, lParam, (LPARAM) & dbei);
                     if (dat->hDbEventFirst == NULL)
                            dat->hDbEventFirst = (HANDLE) lParam;
                     if (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ))
                            break;
                     if (DbEventIsShown(&dbei, dat)) {
                            if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
                                   if (GetForegroundWindow()==hwndDlg)
                                          SkinPlaySound("RecvMsgActive");
                                   else SkinPlaySound("RecvMsgInactive");
                            }
                            if (dbei.eventType == EVENTTYPE_MESSAGE && dat->hwndStatus && !(dbei.flags & (DBEF_SENT))) {
                                   dat->lastMessage = dbei.timestamp;
                                   SendMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0);
                            }
                            if ((HANDLE) lParam != dat->hDbEventFirst && (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, lParam, 0) == NULL)
                                   SendMessage(hwndDlg, DM_APPENDTOLOG, lParam, 0);
                            else
                                   SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
                            if ((GetActiveWindow() != hwndDlg || GetForegroundWindow() != hwndDlg) && !(dbei.flags & DBEF_SENT)
                                   && dbei.eventType != EVENTTYPE_STATUSCHANGE) {
                                          SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL);
                                   }
                     }
                     break;
              }……
}
 
            StreamInEvents
void StreamInEvents(HWND hwndDlg, HANDLE hDbEventFirst, int count, int fAppend)
{
       EDITSTREAM stream = { 0 };
       struct LogStreamData streamData = { 0 };
       struct MessageWindowData *dat = (struct MessageWindowData *) GetWindowLong(hwndDlg, GWL_USERDATA);
       CHARRANGE oldSel, sel;
 
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, TRUE, 0);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM) & oldSel);
       streamData.hContact = dat->hContact;
       streamData.hDbEvent = hDbEventFirst;
       streamData.dlgDat = dat;
       streamData.eventsToInsert = count;
       streamData.isEmpty = fAppend ? GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG)) == 0 : 1;
       stream.pfnCallback = LogStreamInEvents;
       stream.dwCookie = (DWORD_PTR) & streamData;
       if (fAppend) {
              sel.cpMin = sel.cpMax = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG));
              SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & sel);
       }
       else dat->bIsFirstAppend = TRUE;
 
       strcpy(szSep2, fAppend ? "//par//sl0" : "//sl1000");
       strcpy(szSep2_RTL, fAppend ? "//rtlpar//rtlmark//par//sl1000" : "//sl1000");
       //更新richedit数据, 详细参考下面的 RichEdit参考
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_STREAMIN, fAppend ? SFF_SELECTION | SF_RTF : SFF_SELECTION | SF_RTF, (LPARAM) & stream);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & oldSel);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, FALSE, 0);
       dat->hDbEventLast = streamData.hDbEventLast;
       if (GetWindowLong(GetDlgItem(hwndDlg, IDC_LOG), GWL_STYLE) & WS_VSCROLL)
              PostMessage(hwndDlg, DM_SCROLLLOGTOBOTTOM, 0, 0);
}
             LogStreamInEvents
static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
{
       struct LogStreamData *dat = (struct LogStreamData *) dwCookie;
 
       if (dat->buffer == NULL) {
              dat->bufferOffset = 0;
              switch (dat->stage) {
                     case STREAMSTAGE_HEADER:
                            dat->buffer = CreateRTFHeader(dat->dlgDat);
                            dat->stage = STREAMSTAGE_EVENTS;
                            break;
                     case STREAMSTAGE_EVENTS:
                            if (dat->eventsToInsert) {
                                   do {
                                          dat->buffer = CreateRTFFromDbEvent(dat->dlgDat, dat->hContact, dat->hDbEvent, dat);
                                          if (dat->buffer)
                                                 dat->hDbEventLast = dat->hDbEvent;
                                          dat->hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) dat->hDbEvent, 0);
                                          if (--dat->eventsToInsert == 0)
                                                 break;
                                   } while (dat->buffer == NULL && dat->hDbEvent);
                                   if (dat->buffer) {
                                          dat->isEmpty = 0;
                                          break;
                                   }
                            }
                            dat->stage = STREAMSTAGE_TAIL;
                            //fall through
                     case STREAMSTAGE_TAIL:
                            dat->buffer = CreateRTFTail(dat->dlgDat);
                            dat->stage = STREAMSTAGE_STOP;
                            break;
                     case STREAMSTAGE_STOP:
                            *pcb = 0;
                            return 0;
              }
              dat->bufferLen = lstrlenA(dat->buffer);
       }
       *pcb = min(cb, dat->bufferLen - dat->bufferOffset);
       CopyMemory(pbBuff, dat->buffer + dat->bufferOffset, *pcb);
       dat->bufferOffset += *pcb;
       if (dat->bufferOffset == dat->bufferLen) {
              free(dat->buffer);
              dat->buffer = NULL;
       }
       return 0;
}
            调试时从dat->buffer中抓到的rtf数据:
内容是: 小图标 + testzc@im.hc: + 111
RTF数据(注意每行后面的回车换行符号是为了容易阅读后来添加的,实际的数据是一个整个的文本串,中间没有回车换行符号):
 
{/rtf1/ansi/deff0
{/fonttbl
{/f0/fnil/fcharset134 宋体;}
{/f1/fnil/fcharset134 宋体;}
{/f2/fnil/fcharset134 宋体;}
{/f3/fnil/fcharset134 宋体;}
{/f4/fnil/fcharset134 宋体;}
{/f5/fnil/fcharset134 宋体;}
{/f6/fnil/fcharset134 宋体;}
{/f7/fnil/fcharset134 宋体;}
{/f8/fnil/fcharset134 宋体;}
{/f9/fnil/fcharset134 宋体;}}
{/colortbl /red106/green106/blue106;/red0/green0/blue0;
/red89/green89/blue89;/red0/green0/blue0;/red215/green0/blue0;
/red0/green0/blue0;/red90/green90/blue160;/red0/green0/blue128;}
/par/ltrpar/f0/fs14{/pict/dibitmap0/wbmbitspixel24/wbmplanes1
/wbmwidthbytes32/picw10/pich10
280000000A0000000A00000001001800000000004001000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFF8E42358E42358E4235FFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFF8E42358E4235FFFFFF8E4235CF864F8E4235FFFFFFFFFFFFFFFFFF0000FFFFFF8E4235CF864F8E4235FFFFFF8E4235CF864F8E4235FFFFFFFFFFFF0000FFFFFFFFFFFF8E4235E2A6728E4235FFFFFF8E4235E2A6728E4235FFFFFF0000FFFFFFFFFFFF8E4235E9B7858E4235FFFFFF8E4235E9B7858E4235FFFFFF0000FFFFFF8E4235F2C18E8E4235FFFFFF8E4235F2C18E8E4235FFFFFFFFFFFF0000FFFFFF8E42358E4235FFFFFF8E4235F2C18E8E4235FFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFF8E42358E42358E4235FFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000}
/f6/cf6/b0/i0/fs18 {/uc1 14:56:47}
/f5/cf5/b0/i0/fs18 {/uc1 testzc@im.hc}
/f7/cf7/b0/i0/fs18 :
/f1/cf1/b0/i0/fs18 {/uc1 111}
RichEdit 参考
EM_STREAMIN
The EM_STREAMIN message replaces the contents of a rich edit control with a stream of data provided by an application defined–EditStreamCallback callback function.
To send this message, call the SendMessage function with the following parameters.
SendMessage( 
  (HWND) hWnd,              // handle to destination window 
  EM_STREAMIN,              // message to send
 (WPARAM) wParam,          // format options
 (LPARAM) lParam           // data (EDITSTREAM *)
);
Parameters
wParam
Specifies the data format and replacement options. This value must be one of the following values.

Value
Meaning
SF_RTF
Rich Text Format (RTF)
SF_TEXT
Text

 
In addition, you can specify the following flags.

Value
Meaning
SFF_PLAINRTF
If specified, only keywords common to all languages are streamed in. Language-specific RTF keywords in the stream are ignored. If not specified, all keywords are streamed in. You can combine this flag with the SF_RTF flag.
SFF_SELECTION
If specified, the data stream replaces the contents of the current selection. If not specified, the data stream replaces the entire contents of the control. You can combine this flag with the SF_TEXT or SF_RTF flags.
SF_UNICODE
Rich Edit 2.0 and later: Indicates Unicode text. You can combine this flag with the SF_TEXT flag.

 
lParam
Pointer to an EDITSTREAM structure. On input, the pfnCallback member of this structure must point to an application defined–EditStreamCallback function. On output, the dwError member can contain a nonzero error code if an error occurred.
 
EDITSTREAM
The EDITSTREAM structure contains information that an application passes to a rich edit control in a EM_STREAMIN or EM_STREAMOUT message. The rich edit control uses the information to transfer a stream of data into or out of the control.
typedef struct _editstream { 
    DWORD_PTR dwCookie; 
    DWORD dwError; 
    EDITSTREAMCALLBACK pfnCallback; 
} EDITSTREAM; 
Members
dwCookie
Specifies an application-defined value that the rich edit control passes to the EditStreamCallback callback function specified by the pfnCallback member.
dwError
Indicates the results of the stream-in (read) or stream-out (write) operation. A value of zero indicates no error. A nonzero value can be the return value of the EditStreamCallback function or a code indicating that the control encountered an error.
pfnCallback
Pointer to an EditStreamCallback function, which is an application-defined function that the control calls to transfer data. The control calls the callback function repeatedly, transferring a portion of the data with each call.
 

相关文章推荐

live555 RTSP服务器建立及消息处理流程

DynamicRTSPServer::creatnew():    1.调用继承自RTPSever::setUpOurSocket:        1.调用 GroupsockHelper 的 s...

点播消息流程实例

  • 2013年01月23日 23:49
  • 9KB
  • 下载

WM_NOTIFY消息流程实例分析

  • 2012年10月10日 21:26
  • 856KB
  • 下载

ZigBee 中 z-Stack协议中的任务、事件、消息处理流程

1,系统上电以后在main函数的最后会调用osal_start_system()方法来启动系统,这个方法是个死循环,只循环里面只做一件事:不断的检测任务,看任务里面有没有事件需要处理;    检测方...
  • PZ0605
  • PZ0605
  • 2017年03月17日 11:53
  • 571

CControlBar CDockBar 内部消息流程

  • 2009年08月11日 11:55
  • 26KB
  • 下载

H.248呼叫消息流程

  • 2010年12月13日 17:13
  • 2.33MB
  • 下载

Qualcomm平台qcril初始化及消息处理流程(原)

本节主要来介绍Qcril的初始化流程以及消息在Qcril中如何传递。 Android平台不同厂商的AP侧可以相同,但是Modem侧肯定会有很大的差异,RILC要解决一个问题就是适配不同厂商的Modem...

短消息的工作流程.rar

  • 2012年04月21日 19:08
  • 69KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:收发Jabber消息流程
举报原因:
原因补充:

(最多只允许输入30个字)