65.9K
CodeProject 正在变化。 阅读更多。
Home

AppBar - 如何使用简单的MFC类和DLL实现滑动的桌面栏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (23投票s)

2005年5月3日

CPOL

4分钟阅读

viewsIcon

158051

downloadIcon

6467

一种标准的Windows AppBar的替代方案,对您的应用程序的改动极小。

引言

我曾在网上多次寻找实现AppBar(桌面应用程序工具栏)的简单方法,但都未成功。微软提供了一些WinAPI的Shell函数,如SHAppBarMessage在此处阅读相关信息)以及一个旧的示例应用程序(AppBar.exe)。我尝试了一下,但那并不是我想要的,因为Shell AppBars会重新组织桌面以适应工具栏,而我想要的是一个不会干扰其他应用程序窗口或桌面图标的可滑动栏。

因此,我开发了一个单一的MFC对象,CAppBarMngr,它允许几乎任何应用程序变成一个可滑动的AppBar,并且对应用程序的源代码改动极小。由于有必要响应应用程序窗口外的鼠标移动,我不得不实现一个全局鼠标钩子,这需要生成一个DLL,但您不需要处理DLL的内部细节,只需将其分发给您的软件即可。

总体设计

只有一个类:CAppBarMngr,它将负责从桌面屏幕的左侧或右侧边缘滑动应用程序的主框架。正如我之前提到的,需要一个全局钩子来恰当地响应鼠标光标的位置(即是否已到达侧边沿)。

传统上,全局钩子实现为一个DLL,通过接收窗口的HWND句柄向应用程序窗口发送消息(钩子DLL不能启用MFC),这需要对应用程序接收和管理钩子消息的代码进行一些修改。然而,为了避免这种情况,我尝试了一种不同的方法:发送消息到一个辅助线程,如下文所述。

CAppBarMngr 类

此类派生自CWinThread,因此能够通过实现PreTranslateMessage()事件,使用WinAPI的PostThreadMessage()函数接收消息。此外,此类还承担其他一些职责:作为钩子DLL的包装器,并处理窗口移动(滑动)。

CAppBarMngr只有一个public成员:Init()函数。它将用于将管理器与被管理的窗口关联起来。它接收三个参数,如源代码中所述。

//------------------------------------------------------
// Function:  Init   - Loads DLL functions and initilize mouse hook
// Arguments: _hWnd  - Handler of window to manage
//            _width - Desired width of managed window
//            _left  - True if window is left side docked, false if right sided
// Returns:   APPBARHOOK_DLLERROR - An error has occurred while loading DLL functions
//            APPBARHOOK_ALREADYHOOKED - Another instance has already hooked the mouse
//            APPBARHOOK_SUCCESS - All is OK
//---------------------------------------------------------------------

int CAppBarMngr::Init(HWND _hWnd, int _width, bool _left)

如何使用它

这就是在您的应用程序中实现AppBar所需要做的一切

  • AppBarMngr.cppAppBarMngr.h文件插入您的MFC项目中。
  • 在您的主文件(创建主窗口的地方)中插入#include "AppBarMngr.h"
  • 创建您的主窗口,通常在yourapp::InitInstance()中。
  • 使用MFC的::AfxBeginThread()函数创建一个线程,如下所示,并保存其指针。
  • 调用Init方法,指定窗口的HWND句柄、所需的宽度和边缘。
  • 验证Init是否成功返回。

以下是演示应用程序中的代码片段

BOOL CAppBarDemoApp::InitInstance()
{
 CMainFrame* pFrame = new CMainFrame;
 m_pMainWnd = pFrame;

 // creates a simple frame, without caption, icon or menu

 pFrame->Create(NULL, "AppBarDemo", WS_POPUP);
 
 // avoid taskbar button to appear, also removes 3D edge

 pFrame->ModifyStyleEx(WS_EX_APPWINDOW|WS_EX_CLIENTEDGE, WS_EX_TOOLWINDOW);
 // Don't show, AppBar Manager class will do

 pFrame->ShowWindow(SW_HIDE);
 pFrame->UpdateWindow();

 // Create AppBar manager thread

 CAppBarMngr *appbar = 
           (CAppBarMngr *)::AfxBeginThread(RUNTIME_CLASS(CAppBarMngr));

 // Init AppBar Manager, right sided

 int result = appbar->Init(pFrame->m_hWnd, 150, false);  // use true for left sided
 
 // Check if hooking has been successful

 if (result==APPBARHOOK_SUCCESS)
  return TRUE;
 else if (result==APPBARHOOK_DLLERROR)
  ::AfxMessageBox("Error loading AppBarHook.dll");
 // else should be APPBARHOOK_ALREADYHOOKED, close application


 return FALSE;
}

这就是您需要做的全部。另外,别忘了将钩子DLL(AppBarHook.dll)与您的可执行应用程序一起分发;它必须位于同一目录才能正常工作。

免费的单实例控制

由于运行同一AppBar程序的两个副本没有意义,Init()函数会通知钩子已经被使用(通过返回APPBARHOOK_ALREADYHOOKED),因此您可以使用它来避免运行第二个实例。请注意上面示例的最后几行,如果钩子已经被使用,它将返回FALSE来关闭应用程序。

钩子项目

我不得不开发一个项目来实现全局鼠标钩子DLL。它只有一个文件:AppBarHook.cpp。我不想在这里做一个钩子教程,因为CodeProject上有很多很棒的文章。因此,我只会给您一些细节。

通常的钩子技术会保存一个接收钩子消息的窗口句柄(HWND)。我使用的是线程ID,这就是为什么CAppBarMngr是一个线程对象。因此,消息是通过WinAPI的::SendThreadMessage()而不是::SendMessage()函数传递的。

钩子将检测对CAppBarMngr可能感兴趣的鼠标事件。我说“可能”,因为钩子不知道窗口的状态和位置,它只知道管理的边缘和窗口宽度。MFC类将完成其余的工作。

钩子DLL只导出一个函数:SetHook(),它创建全局鼠标钩子并保存所需的宽度和边缘。它由CAppBarMngr调用。

关于演示应用程序

我创建了一个简单的演示应用程序项目用于测试。它有一个简单的CFrameWnd派生对象,没有框架、标题栏、菜单或边框。我已将此与其他标准的MFC窗口进行了测试,没有出现任何问题。

演示源代码是一个Visual C++ 6.0项目,但您将能够打开它并自动将其转换为较新版本的Visual C++。

如果您在此应用程序中发现任何错误,请不要在意。它仅用于演示目的,也不是本文的重点。出于同样的原因,我在这里不会描述它的内部细节。

.NET 版本

我曾多次被要求提供此控件的.NET版本。我已开始着手开发。准备好后,我将在下面的论坛中留言。

历史

  • 2005年5月3日 - 初版
  • 2008年7月9日 - 添加了多显示器支持
© . All rights reserved.