一、关于托盘制作
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_MAP
和
END_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
)来保存菜单项的状态,鼠标每点击一次就进行
FALSE
和
TRUE
之间的转换(注意:在最后退出托盘程序时,必须将菜单项的状态保存到注册表或
INI
文件中,以便托盘程序下次启动时初始化菜单项状态)。而
菜单状态的初始化动作应该在右键单击托盘图标弹起后进行,
此时通过读取在菜单项消息映射函数中记录的菜单项状态来决定
CHECK MENU
还是不
CHECK MENU.(
例如
:
当
bMenuChecked
为
TRUE
时,就在相应的菜单项前面进行打勾操作(
CHECK MENE
)
,
当
bMenuChecked
为
FALSE
时,就将菜单项前面的勾取消掉
)
。
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_SkypeAttach
和
m_MsgID_SkypeDiscover
为
CSkypeTVDlg
的成员变量,类型为
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_Skype
为
CSkypeTVDlg
的成员变量,类型为
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;
}