//补充菜单资源定义IDM_CONTEXTMAIN MENU DISCARDABLE BEGIN POPUP "_Tray_" BEGIN MENUITEM "恢复视窗(&R)", ID_RESTORE MENUITEM SEPARATOR MENUITEM "关于...", ID_APP_ABOUT MENUITEM "退出(&Exit)", ID_APP_EXIT END END
放在通知栏我知道如何做了但是我想实现的是Winamp的那种 System tray only的效果,就是只出现在 System tray,而不出现在Task bar。to wistaria(听风听雨)好象NT下没有 RegisterServieceProcess()吧。
LONG GetWindowLong(HWND hWnd,int nIndex);
LONG SetWindowLong(HWND hWnd,int nIndex,LONG dwNewLong);
另一种很简便的是MFC的写法:在程序框架类的预创建窗口函数里通过直接对CREATESTRUCT结构对象的逻辑操作而将程序属性进行改变:
cs.style=WS_POPUP;
cs.dwExStyle|=WS_EX_TOOLWINDOW;
这两种写法虽然表现形式各不相同,其本质都是一样的。
三、程序在任务列表中的隐藏原理
任务列表(Ctrl+Alt+Del时弹出的对话框)显示了当前系统正在运行的一些应用程序,如果实现了上一步,虽然在任务栏看不见程序,但有经验的用户可以通过观察任务列表而发现一些值得怀疑的应用程序而在此将其关闭。所以大多数黑软也都通过较复杂的手段实现了自身在任务列表中的隐藏,使被发现的机会大大降低。
在Win9x中,一般每个应用程序都要通过一个API(应用程序接口)函数RegisterServiceProcess()向系统申请注册成为一个服务进程,并且也是通过这个函数注销其服务进程来结束这个服务进程的运行。如果一个进程注册为一个服务进程,通过Ctrl+Alt+Del就可以在任务列表里看见该进程的标题。而如果一个进程运行了但没有向系统申请注册成为服务进程那么就不会在任务列表里显示。黑软也正是利用这个原理使自身在运行时能在任务列表中实现隐藏。该函数存放于系统内核Kernel32.dll中,具体声明如下:
DWORD RegisterServiceProcess(DWORD dwProcessId,DWORD dwType);
其第一个参数指定为一个服务进程的进程标识,如果是0则注册当前的进程;第二个参数指出是注册还是注销当前的进程,其状态分别为:RSP_SIMPLE_SERVICE和RSP_UNREGISTER_SERVICE。黑软一般是在程序启动初始化时首先从Kernel32.dll动态连接库中将RegisterServiceProcess()函数加载到内存,然后再通过该函数将程序从任务列表中隐藏:
//从Kernel32.dll中加载RegisterServiceProcess()
HMODULE m_hKernel=::GetModuleHandle("Kernel32.DLL");
RSP m_rsp=(RSP)::GetProcAddress(m_hKernel,"RegisterServiceProcess");
m_rsp(::GetCurrentProcessId(),1);//此时为隐藏,当第二个参数为0时显示
另外,还有一部分黑软是通过ShowWindowAsync()函数启动一个新的线程来显示一个新窗口的。该函数的原形为:
BOOL ShowWindowAsync(HWND hWnd,int nCmdShow);
程序在任务栏的隐藏比较简单,首先要保证程序主界面的隐藏,一般是通过修改应用程序类的初始化实例函数InitInstance()的ShowWindow()语句的SW_SHOW参数为SW_HIDE来实现的。主界面隐藏的同时任务栏虽然也会消失,但在程序启动时会闪一下,因此需要修改程序的扩展属性。一种方法是SDK的写法,即直接利用GetWindowLong()获取到当前的扩展属性然后通过逻辑运算去掉原有的WS_EX_APPWINDOW属性,并新添加一个WS_EX_TOOLWINDOW属性,这样系统会将其认为是一个工具条窗口而不会再在任务栏中加以显示。最后需要将修改过的扩展属性通过SetWindowLong()函数将其写回。这两个函数的声明分别如下:
LONG GetWindowLong(HWND hWnd,int nIndex);
LONG SetWindowLong(HWND hWnd,int nIndex,LONG dwNewLong);
另一种很简便的是MFC的写法:在程序框架类的预创建窗口函数里通过直接对CREATESTRUCT结构对象的逻辑操作而将程序属性进行改变:
cs.style=WS_POPUP;
cs.dwExStyle|=WS_EX_TOOLWINDOW;
这两种写法虽然表现形式各不相同,其本质都是一样的。
三、程序在任务列表中的隐藏原理
任务列表(Ctrl+Alt+Del时弹出的对话框)显示了当前系统正在运行的一些应用程序,如果实现了上一步,虽然在任务栏看不见程序,但有经验的用户可以通过观察任务列表而发现一些值得怀疑的应用程序而在此将其关闭。所以大多数黑软也都通过较复杂的手段实现了自身在任务列表中的隐藏,使被发现的机会大大降低。
在Win9x中,一般每个应用程序都要通过一个API(应用程序接口)函数RegisterServiceProcess()向系统申请注册成为一个服务进程,并且也是通过这个函数注销其服务进程来结束这个服务进程的运行。如果一个进程注册为一个服务进程,通过Ctrl+Alt+Del就可以在任务列表里看见该进程的标题。而如果一个进程运行了但没有向系统申请注册成为服务进程那么就不会在任务列表里显示。黑软也正是利用这个原理使自身在运行时能在任务列表中实现隐藏。该函数存放于系统内核Kernel32.dll中,具体声明如下:
DWORD RegisterServiceProcess(DWORD dwProcessId,DWORD dwType);
其第一个参数指定为一个服务进程的进程标识,如果是0则注册当前的进程;第二个参数指出是注册还是注销当前的进程,其状态分别为:RSP_SIMPLE_SERVICE和RSP_UNREGISTER_SERVICE。黑软一般是在程序启动初始化时首先从Kernel32.dll动态连接库中将RegisterServiceProcess()函数加载到内存,然后再通过该函数将程序从任务列表中隐藏:
//从Kernel32.dll中加载RegisterServiceProcess()
HMODULE m_hKernel=::GetModuleHandle("Kernel32.DLL");
RSP m_rsp=(RSP)::GetProcAddress(m_hKernel,"RegisterServiceProcess");
m_rsp(::GetCurrentProcessId(),1);//此时为隐藏,当第二个参数为0时显示
另外,还有一部分黑软是通过ShowWindowAsync()函数启动一个新的线程来显示一个新窗口的。该函数的原形为:
BOOL ShowWindowAsync(HWND hWnd,int nCmdShow);
而且我也试了一下在任务列表中隐藏,但是运行就出错了,出错提示如下:
Unhandled exception in ***.exe: 0xc0000005 : Access Violation 。
好像是 RegisterSevicePorcess()的问题,而且好像是与第一个参数有关。能给点具体的提示吗? 我用的是 WinXP + VC++6.0
if(lpHidePro==NULL)MessageBox(NULL,"End","Failed",MB_OK);
HIDEPRO lpHide=NULL;//HIDEPRO 定义为typedef (WINAPI *HIDEPRO)(DWORD lpProID,DWORD flag);
lpHide=(HIDEPRO)GetProcAddress(lpHidePro,"RegisterServiceProcess");
lpHide(::GetCurrentProcessId(),0);
NOTIFYICONDATA pnid;//任务栏图标结构
// Generated message map functions
protected:
afx_msg void OnSysTray(WPARAM wParam,LPARAM lParam);
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnClose();
afx_msg void OnRestore();//在mainfrm.cpp中添加
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_CLOSE()
ON_COMMAND(ID_RESTORE, OnRestore)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SYSTRAY,OnSysTray)
END_MESSAGE_MAP()int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
............
pnid.cbSize = sizeof(NOTIFYICONDATA);
pnid.hIcon = LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));
pnid.hWnd = m_hWnd;
sprintf(pnid.szTip, "聊天服务程序\n");
pnid.uCallbackMessage = WM_SYSTRAY;
pnid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
pnid.uID = ID_SYSTRAY;
Shell_NotifyIcon(NIM_ADD, &pnid);
ShowWindow(SW_HIDE);
return 0;
}void CMainFrame::OnSysTray(WPARAM wParam,LPARAM lParam)
{
CPoint pt;
CServerApp* app = (CServerApp*)AfxGetApp();//这里你可能需要修改
switch (lParam) {
case WM_RBUTTONUP:
{ // Let's track a popup menu
GetCursorPos(&pt);
HMENU hmenu = LoadMenu(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDM_CONTEXTMAIN));
HMENU hpopup = GetSubMenu(hmenu, 0);//注意:是得到子菜单
switch (TrackPopupMenu(hpopup, // Popup menu to track
TPM_RETURNCMD | // Return menu code
TPM_RIGHTBUTTON, // Track right mouse button?
pt.x, pt.y, // screen coordinates
0, // reserved
m_hWnd, // owner
NULL)) // LPRECT user can click in
// without dismissing menu
{
case ID_APP_EXIT: //DestroyWindow(hwnd); break;
PostQuitMessage(0);
Shell_NotifyIcon(NIM_DELETE, &pnid);
break;
case ID_APP_ABOUT:
app->OnAppAbout();
break;
case ID_RESTORE:
break;
}
break;
}
case WM_LBUTTONDBLCLK:
ShowWindow(SW_SHOWDEFAULT);
break;
}
}LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) //当重新按下“最小化”按钮时,将窗口隐藏
{
LRESULT result = CFrameWnd::WindowProc(message, wParam, lParam);
if(wParam == SC_MINIMIZE){
ShowWindow(SW_HIDE);
}
return result;
}void CMainFrame::OnClose()
{
Shell_NotifyIcon(NIM_DELETE, &pnid);//销毁任务栏图标
CFrameWnd::OnClose();
}void CMainFrame::OnRestore()
{
}
BEGIN
POPUP "_Tray_"
BEGIN
MENUITEM "恢复视窗(&R)", ID_RESTORE
MENUITEM SEPARATOR
MENUITEM "关于...", ID_APP_ABOUT
MENUITEM "退出(&Exit)", ID_APP_EXIT
END
END