总结

本文介绍如何创建托盘程序,并实现与Skype的通信功能。内容涵盖托盘图标的添加、自定义消息处理、菜单事件响应及窗口最小化等步骤。同时,详细讲解了与Skype建立连接的过程,包括消息注册、接收与解析。
 
一、关于托盘制作
1.     在对话框头文件中添加一个NOTIFYICONDATA类型的成员数据,用来设定托盘消息处理的方法,如下:
NOTIFYICONDATA m_nid;
添加一个名为ToTray的函数,用来初始化m_nid和托盘的一些信息,在对话框头文件中添加函数原型:
void ToTray(void);
   函数定义如下:
void CSkypeTVDlg::ToTray(void)
{
    m_nid.cbSize=sizeof(NOTIFYICONDATA);
    m_nid.hWnd=this->GetSafeHwnd();
    m_nid.uID=IDR_MAINFRAME;
    m_nid.uFlags= NIF_ICON|NIF_MESSAGE|NIF_TIP;
    m_nid.uCallbackMessage=WM_CLICKONTRAY;//自定义的消息名称
    m_nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
    strcpy(m_nid.szTip,"SkypeTV");//信息提示条
    Shell_NotifyIcon(NIM_ADD,&m_nid);//在托盘区添加图标
}
其中WM_CLICKONTRAY为自定义消息,用来响应托盘的鼠标事件,下面将有介绍。
最后,OnInitDialog中加入语句:ToTray();使对话框开启后就初始化为托盘状态。
2.自定义消息:定义一个名为WM_CLICKONTRAY的消息,用来响应托盘的鼠标事件:
#define WM_CLICKONTRAY   WM_USER+100
   BEGIN_MESSAGE_MAPEND_MESSAGE_MAP宏之间添加消息映射:
ON_MESSAGE(WM_CLICKONTRAY,OnTray)
   实现自定义消息函数(OnTray函数),在对话框头文件中添加函数原型:
LRESULT OnTray(WPARAM wParam, LPARAM lParam);
   NOTE:关于自定义消息函数的格式,请参考自定义消息相关知识。
   最后,函数定义如下:
LRESULT CSkypeTVDlg::OnTray(WPARAM wParam, LPARAM lParam)
{
    if(wParam!=IDR_MAINFRAME)
        return 1;
    switch(lParam)
    {
    case WM_RBUTTONUP:
        //右键起来时弹出快捷菜单
        {         
            CPoint pp(LOWORD(lParam),HIWORD(lParam));
            ShowMenu(pp);
            //显示浮动菜单
        }
        break;
    case WM_LBUTTONUP:
        {
            …….
        }
        break;
    }
    return 0;
}
其中ShowMenu用来显示托盘菜单,具体代码如下:
void CSkypeTVDlg::ShowMenu(CPoint point)
{
    CMenu *pMenu = new CMenu;   
    if(pMenu)
    {
        if(pMenu->LoadMenu(IDR_MENU1))
        { 
            SetMenu(pMenu);
            CMenu *pSubMenu = pMenu->GetSubMenu(0);      
            if(m_bStartWithComputer)
                // 注意:菜单状态在此时(右键弹起时)就要确定好,而不是在单击// 菜单项时确定,下面有详细说明。
                pSubMenu->CheckMenuItem(ID_TRAY_STARTWITHCOMPUTER, MF_CHECKED | MF_BYCOMMAND);             
            CPoint pt;
            GetCursorPos(&pt);           
            ::SetForegroundWindow(m_nid.hWnd);     
            if(pSubMenu)
            {
                ::TrackPopupMenu(pSubMenu->m_hMenu, 0, pt.x, pt.y, 0,
                    m_nid.hWnd, NULL);
            }                     
            ::PostMessage(m_nid.hWnd, WM_NULL, 0, 0);
        }  
    }  
}
其中IDR_MENU1为菜单资源文件。
3.添加托盘菜单事件处理的响应函数,一般托盘都有一个“退出”菜单选项,其实现如下:
void CSkypeTVDlg::OnTrayExit()
{
    // TODO: Add your command handler code here
    DelIcon();
    DestroyWindow();
}
 
void CSkypeTVDlg::DelIcon(void)
{
    m_nid.cbSize=sizeof(NOTIFYICONDATA);
    m_nid.hWnd=this->m_hWnd;
    m_nid.uID=IDR_MAINFRAME;
    Shell_NotifyIcon(NIM_DELETE,&m_nid);
    CDialog::OnCancel();  
}
其中OnTrayExit是添加菜单事件处理时得到的消息映射函数,在菜单资源文件中右键点击“退出”菜单项,选择“Add Event Handler……”选项,将自动生成相应的菜单消息函数。(其它菜单项操作相同)。
下面是另一个菜单项的消息映射函数,主要用来示范怎么样改变菜单的状态(是否”CHECK”,例如:有的菜单项前面有个勾或点之类的,就已经CHECK了)。
void CSkypeTVDlg::OnTrayChangeMenuState()
{
    // TODO: Add your command handler code here
    HKEY hkey;
    DWORD dwValue = 0;
    TCHAR path[256]="HKEY_CURRENT_USER//Software
//VideoHome//SkypeTV";
 
    OpenRegKey(path,&hkey);
 
    if ( m_bMenuChecked == FALSE )
    {
        m_bMenuChecked = TRUE;
        dwValue = 1;
        RegSetValueEx(hkey,"MenuChecked",0,REG_DWORD,(const BYTE*)&dwValue,sizeof(DWORD));
    }
    else
    {
        m_bMenuChecked = FALSE;    
        RegSetValueEx(hkey,"MenuChecked",0,REG_DWORD,(const BYTE*)&dwValue,sizeof(DWORD));
    }
    RegCloseKey(hkey);
}
需要注意的是:点击托盘任一菜单项(其实所有弹出式菜单都一样)后,菜单将立即消失(退出),而菜单状态的初始化或改变应该在菜单显示或弹出时进行。因此不能在此时(菜单项的消息映射函数中)调用CheckMenuItem来改变菜单项的状态(如果此时在菜单项的消息映射函数中进行CheckMenuItem操作将不起作用),此时在菜单项的消息映射函数中必须记录(而不是改变)菜单项的状态,可以用一个BOOL型的变量(例如bMenuChecked)来保存菜单项的状态,鼠标每点击一次就进行FALSETRUE之间的转换(注意:在最后退出托盘程序时,必须将菜单项的状态保存到注册表或INI文件中,以便托盘程序下次启动时初始化菜单项状态)。而菜单状态的初始化动作应该在右键单击托盘图标弹起后进行,此时通过读取在菜单项消息映射函数中记录的菜单项状态来决定CHECK MENU还是不CHECK MENU.(例如:bMenuCheckedTRUE时,就在相应的菜单项前面进行打勾操作(CHECK MENE,bMenuCheckedFALSE时,就将菜单项前面的勾取消掉)
 
4.最后将对话框窗口最小化并隐藏。在OnInitDialog中添加如下代码:
   PostMessage(WM_SYSCOMMAND,MAKEWPARAM(SC_MINIMIZE,0),0);
 并在OnSysCommand中添加下面的代码:
   if ((nID & 0xFFF0)==SC_MINIMIZE)
    {      
        ShowWindow(SW_HIDE);
}
 不要在OnInitDialog中直接调用ShowWindow(SW_HIDE);因为此时窗口还没有完全Init
 
二、             关于与Skype的通信
BOOL CSkypeTVDlg::Init ()
{
    HWND hwnd = this->GetSafeHwnd();
  
    m_MsgID_SkypeAttach =
RegisterWindowMessage("SkypeControlAPIAttach");
    m_MsgID_SkypeDiscover = RegisterWindowMessage("SkypeControlAPIDiscover");
    if ( m_MsgID_SkypeAttach == 0 || m_MsgID_SkypeDiscover == 0 )    
    {
         return FALSE;
    }
      
    if(::PostMessage(HWND_BROADCAST,m_MsgID_SkypeDiscover, (WPARAM)hwnd, 0)==0)
      return FALSE;
   
   return TRUE;
}
其中m_MsgID_SkypeAttachm_MsgID_SkypeDiscoverCSkypeTVDlg的成员变量,类型为UINT
 
因为需要处理Skype发过来的消息,所以重载了WindowProc(或DefWindowProc,在工程的properties window中选择overrides,再选择需要重载的函数):
LRESULT CSkypeTVDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class
    if ( message == m_MsgID_SkypeAttach
        || message == m_MsgID_SkypeDiscover
        || message == WM_COPYDATA )
    {
        if ( TranslateMessage ( message, wParam, lParam ) )
            return TRUE;
    }
    return CDialog::WindowProc(message, wParam, lParam);
}
其中TranslateMessage定义如下:
BOOL CSkypeTVDlg::TranslateMessage ( UINT message, WPARAM wParam, LPARAM lParam )
{   
    if ( message == WM_COPYDATA )
    {
        if( m_hWnd_Skype == (HWND)wParam )
        {          
            PCOPYDATASTRUCT poCopyData = (PCOPYDATASTRUCT)lParam;
            //解析返回的字符信息;
            ParseSkypeMsg(LPCTSTR(poCopyData->lpData));     
            return TRUE;
        }      
    }    
    if ( message == m_MsgID_SkypeAttach )
    {
         switch ( lParam )
         {
                case SKYPECONTROLAPI_ATTACH_SUCCESS:     
                      m_hWnd_Skype=(HWND)wParam;//得到skype的句柄                
                      break; 
                case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
                      break;
                case SKYPECONTROLAPI_ATTACH_REFUSED:          
                      break;
                case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
                      break;
                case SKYPECONTROLAPI_ATTACH_API_AVAILABLE:             
                      break;
         }
    }
    return TRUE;  
}
其中m_hWnd_SkypeCSkypeTVDlg的成员变量,类型为HWND.
其中ParseSkypeMsg定义如下:
BOOL CSkypeTVDlg :: ParseSkypeMsg (LPCTSTR lpszMsg)
{
     if ( !lpszMsg || strlen(lpszMsg) < 1 )
         return FALSE;
     CStringArray StrAry;
     INT_PTR nStrNum = PartStringAndAddToStrAry ( (char*)lpszMsg, StrAry, " ,;/t" );
     if ( nStrNum < 1 ) return FALSE;
     int nCmdPos = 0;
     // Get the current chat's ID
     if ( StrAry.GetAt ( nCmdPos ) == "CHAT")
     {      
         ………………          
     }
     else if (StrAry.GetAt ( nCmdPos ) == "MESSAGE")
     {
          ………………
     }
     else if (StrAry.GetAt ( nCmdPos ) == "CALL")
     {
          ………………
     }
     StrAry.RemoveAll ();
     return TRUE;
}
其中PartStringAndAddToStrAry定义如下:
INT_PTR PartStringAndAddToStrAry ( char *pStr, CStringArray &StrAry, char *seps/*="/t/r/n"*/ )
{
     StrAry.RemoveAll();
     char *token;
     token = strtok( pStr, seps );
     while( token != NULL )
     {
         /* While there are tokens in "string" */
         StrAry.Add ( token );
         /* Get next token: */
         token = strtok( NULL, seps );
     }
     return StrAry.GetSize();
}
 
 
最后一个函数用来与Skype通信,向Skype发送消息:
BOOL CSkypeTVDlg::SendMsg ( LPCTSTR lpszMsg, ... )
{
     if ( !lpszMsg || strlen(lpszMsg) < 1 ) return FALSE;
    
     COPYDATASTRUCT CopyData = {0};
     char buf[1024] = {0};
    
     va_list va;
     va_start ( va, lpszMsg );
     _vsnprintf ( buf, sizeof(buf) - 1, (char*)lpszMsg, va);
     va_end(va);
 
     CopyData.dwData = 0;
     CopyData.lpData = (PVOID)buf;
     CopyData.cbData = strlen(buf)+1;
 
      HWND hwnd = this->GetSafeHwnd();
     if ( ::SendMessage ( m_hWnd_Skype, WM_COPYDATA, WPARAM(hwnd),
         LPARAM(&CopyData)) == 0 )
     {     
         return FALSE;
     }
     return TRUE;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值