Win-Top
简介
当我们Windows中打开一个新窗口或切换任务时,原有的活动窗口就会被它所覆盖。不过,像Winamp等软件,却有一个能让自己的操作窗口总在最前的功能,非常实用。本软件的功能就是可以让任意Windows操作窗口总在最前面,不让其他的窗口挡住它!
代码
Win-Top主要遇到了下面几个问题:
1. 隐藏基于对话框的程序界面
2. 创建一个系统Tray图标
3. 安装系统钩子
4. HOOK系统菜单事件并添加一个自定义菜单项
隐藏基于对话框的程序界面
CDialog类的的默认方法使得隐藏基于MFC对话框程序界面变的麻烦。下面的步骤确保基于对话框的程序的界面隐藏。
在Visual Studio的资源编辑器中,将对话框的Visible 属性设为false
更改App类的InitInstance()方法为:
BOOL CWinTopApp::InitInstance(){
CWinApp::InitInstance();
m_pMainWnd = new CWinTopDlg();
return true;
}
当构造新的Dlg类时,手动创建对话框:
CWinTopDlg::CWinTopDlg(CWnd* pParent /*=NULL*/)
: CDialog(CWinTopDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
// Create the dialog manually
Create( IDD, pParent );
}
对话框还必须手动删除自己:
void CWinTopDlg::PostNcDestroy()
{
delete this;
}
某些情况下PostNcDestoy()不会自动调用,所以还需要手动调用:
void CWinTopDlg::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
ClearHook( m_hWnd );
PostNcDestroy();
}
系统Tray
CSystemTray类使得添加一个系统Tray变的非常容易。
m_Tray.Create( this, WM_ICON_NOTIFY, TEXT("总在最前"), m_hIcon, IDR_TRAYMENU );
所有的消息都传递给CSystemTray类处理
LRESULT CWinTopDlg::OnTrayNotification(WPARAM wParam, LPARAM lParam)
{
// Delegate all the work back to the default
// implementation in CSystemTray.
return m_Tray.OnTrayNotification(wParam, lParam);
}
系统钩子
所有的系统钩子都必须是一个DLL,这样系统中所有的进程都可以安装这个钩子。创建这个DLL的基本原来:
为使DLL中函数能够正确的导出使用下面的宏:
#ifdef HOOKDLL_EXPORTS
#define HOOKDLL_API __declspec(dllexport)
#else
#define HOOKDLL_API __declspec(dllimport)
#endif
在DLL中,为了使一些数据在进程间共享,可以使用dll的共享数据区:
// Global shared data
//
#pragma data_seg( ".SHARED" )
HWND sg_hWnd = NULL;
HINSTANCE sg_hInstance = NULL;
HHOOK sg_hHookShell = NULL;
HHOOK sg_hHookMenu = NULL;
UINT IDM_OT = 0;
#pragma data_seg()
#pragma comment( linker, "/section:.SHARED,rws" )
在Win32环境中,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。
因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。
#pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。
再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws")那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。
当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里.这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。
响应事件
#define pCW ((CWPSTRUCT*) lParam)
// Shell Proc
// Notice: - Append/Remove system menu with "总在最前"
// Modify: 15:28 2005-12-29
//
LRESULT CALLBACK ShellProc( int nCode, WPARAM wParam, LPARAM lParam )
{
// Happens when you select a the system menu item, and it dismisses
if( pCW->message == WM_MENUSELECT )
{
if( pCW->lParam == NULL && HIWORD( pCW->wParam ) == 0xffff )
::RemoveMenu( ::GetSystemMenu( pCW->hwnd, false ), IDM_OT, MF_BYCOMMAND );
}
// Happens when system menu is about to show
else if( pCW->message == WM_INITMENUPOPUP )
{
HMENU hMenu = (HMENU)pCW->wParam;
// If it's a menu and it's a system menu
if( ::IsMenu( hMenu ) && HIWORD( pCW->lParam ) == TRUE )
{
if( ::GetWindowLong( pCW->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST )
AppendMenu( ::GetSystemMenu( pCW->hwnd, false ), MF_CHECKED | MF_BYPOSITION | MF_STRING, IDM_OT, "总在最前" );
else
AppendMenu( ::GetSystemMenu( pCW->hwnd, false ), MF_BYPOSITION | MF_STRING, IDM_OT, "总在最前" );
}
}
return ::CallNextHookEx( sg_hHookShell, nCode, wParam, lParam );
}
#define pMsg ((MSG*)lParam)
// Menu proc
// Notice: - response for OnTop/UnTop message
// Modify:
//
LRESULT CALLBACK MenuProc( int nCode, WPARAM wParam, LPARAM lParam )
{
if( pMsg->message == WM_SYSCOMMAND && ( LOWORD( pMsg->wParam ) == IDM_OT ) )
{
if( ::GetWindowLong( pMsg->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST )
::SetWindowPos( pMsg->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED );
else
::SetWindowPos( pMsg->hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED );
}
return ::CallNextHookEx( sg_hHookMenu, nCode, wParam, lParam );
}
// Hook && unHook
//
HOOKDLL_API BOOL SetHook( HWND hWnd )
{
// The hook dll has installed
if( sg_hWnd != NULL )
return false;
// Shell hook to append the system menu
sg_hHookShell = ::SetWindowsHookEx( WH_CALLWNDPROC, (HOOKPROC) ShellProc, sg_hInstance, 0 );
// Menu hook to response to menu message
sg_hHookMenu = ::SetWindowsHookEx( WH_GETMESSAGE, (HOOKPROC) MenuProc, sg_hInstance, 0 );
if( (sg_hHookShell != NULL) && (sg_hHookMenu != NULL) )
{
sg_hWnd = hWnd;
return true;
}
// one or both dll was not installed OK
return false;
}
HOOKDLL_API BOOL ClearHook( HWND hWnd )
{
// our dll is not install here or hWnd is invalid
if( hWnd != sg_hWnd || hWnd == NULL )
return false;
if( !::UnhookWindowsHookEx( sg_hHookShell ) || !::UnhookWindowsHookEx( sg_hHookMenu ) )
return false;
// reset current HWND to null
sg_hWnd = NULL;
return true;
}
Bug
1. 有些窗口设置无效,例如( Visual Studio, MSDN Library )
2. 有时弹出系统菜单并不出现“总在最前”菜单项,再次弹出才会出现??
3. HOOK Dll项目设为Unicode字符集时,API SetWindowsHookEx()会失败??

767

被折叠的 条评论
为什么被折叠?



