设为首页 收藏本站
查看: 597|回复: 0

[转]Windows 7程序开发系列之一(任务栏篇)

[复制链接]

尚未签到

发表于 2015-5-12 05:46:59 | 显示全部楼层 |阅读模式
  Windows 7 引入了很多新特性,其中最直观的莫过于用户界面上的变化。很多人也因为不能适应这种变化而回到了XP。但是在我看来这些新的特性却是一种进步,使用了一段时间之后,也萌生了要做一点开发的冲动。于是把以前一个电源管理的小软件重写了一次(点此下载 ),利用了Windows 7 的任务栏特性和JumpList。
         关于Windows 7开发的中文资料比较少。微软官方的教程还比较丰富,但都是英文的,可能某些英文不太好的朋友学起来比较吃力。我把任务栏和JumpList这两个方面做一个简短的教程,希望对后来者有所帮助。
  

        任务栏方面的东西比JumpList稍微简单一点,就先从任务栏说起。Windows7的任务栏包含了几个新的特性:Progress Bar(进度条)、Overlay Icon(覆盖图标)、 Thumbnail(缩略图)、Thumbnail Toolbar(位于缩略图下方的工具栏)、Tooltip(鼠标指向时的提示信息)、Aero Peek Preview(当鼠标停放在缩略图上时显示窗口预览)。下面是一张foobar2000运行时的截图:
  http://hi.iyunv.com/attachment/201005/25/0_1274792089vZUR.gif
  

         上图中foobar2000使用Progress Bar显示当前歌曲的播放进度,并且在右下角有一个白色的小三角形(Overlay Icon)显示当前是播放还是暂停,使用Thumbnail显示唱片封面,Thumbnail Toolbar有三个按钮分别是上一曲、暂停、下一曲,上方的Tooltip提示当前播放曲目,当鼠标放在缩略图上时,Aero Peek功能会隐藏所有窗口,只显示当前窗口的预览图。上面的Progress Bar和Overlay Icon不太清晰,下面的比较清晰:
  http://hi.iyunv.com/attachment/201005/25/0_12747926477poK.gif
       这篇文章中,将会讲解这些功能的开发。
  

一、Windows7 SDK的安装
         MSDN上很容易找到SDK的下载地址,我就不贴了。SDK有1.44G,下载需要一点时间。安装过程也没什么可讲,就是安装完后在开始菜单中找到Microsoft Windows SDK v7.0->Visual Studio Registration->Windows SDK Configuration Tool,将v7.0设置为当前版本,这样VS中的Windows SDK将使用v7.0版。
二、创建ITaskbarList4接口对象
         与任务栏按钮相关的功能都在这个接口中,Progress Bar, Overlay Icon等。首先创建一个Win32项目,并创建一个简单的窗口,具体不再赘述,文章末尾会给出代码。在WinMain函数的开头,注册一个"TaskbarButtonCreated"的消息,
  //注册用户消息
WM_TASKBARBUTTONCREATED = :: RegisterWindowMessage (TEXT ("TaskbarButtonCreated" ));

  这样我们在WndProc中就可以收到我们注册的 WM_TASKBARBUTTONCREATED 消息了。在这个消息中,创建 ITaskbarList4 接口对象,并调用初始化方法。至于COM相关的内容,已经超出了本教程的范围,有兴趣的可以参考相关资料。
  //创建接口对象 ITaskbarList
if (msg == WM_TASKBARBUTTONCREATED )
{
    :: CoCreateInstance (CLSID_TaskbarList , NULL , CLSCTX_INPROC_SERVER , IID_PPV_ARGS (& g_pTaskbar ));
    g_pTaskbar -> HrInit ();
}

三、 设置进度条和覆盖图标(Progress Bar, Overlay Icon)
        SetProgressState 方法设置进度条的状态,实际反映出来就是进度条的颜色,它接受一个窗口句柄和一个状态标记为参数。其中 TBPF_NOPROGRESS 为取消进度条, TBPF_INDETERMINATE 是一个不断滚动的,无确定值的进度条。
        SetProgressValue 方法设置进度条的值,也就是当前进度。它的参数是窗口句柄、当前值和总值。如果用这个方法设置了进度条,之前设置的TBPF_INDETERMINATE 将无效。
        SetOverlayIcon 设置覆盖图标,他的参数是窗口句柄、图标句柄和描述。其中图标要求16X16大小。如果图标句柄为NULL,则取消覆盖图标。
        在g_pTaskbar对象初始化后,加入下面代码:
  g_pTaskbar -> SetProgressState (g_hWnd , TBPF_ERROR );
g_pTaskbar -> SetProgressValue (g_hWnd , 50 , 100 );
g_pTaskbar -> SetOverlayIcon (g_hWnd , g_hRed , TEXT ("Error" ));

  这段代码将进度条状态设置为ERROR(红色),进度为50%,并在右下角显示一个红色图标(图标已在WinMain函数中加载)。运行后效果如下:
  http://hi.iyunv.com/attachment/201005/25/0_1274798941ucd4.gif
四、缩略图工具栏和信息提示(Thumbnail Toolbar, Tooltip)
         Toolbar稍微麻烦一点,需要设置一个结构THUMBBUTTON 。其中参数dwMask 是一些标记的组合,这里我们设置为THB_ICON | THB_TOOLTIP ,即我们会使用icon和tooltip这两个字段(注意此处的tooltip是按钮的tooltip,不要与上面的tooltip混淆了)。iId 关系到事件的响应,可按顺序编号。hIcon 是该按钮使用的图标句柄。szTip 中设置提示信息。使用下面的代码增加3个按钮:
  THUMBBUTTONMASK dwMask = THB_ICON | THB_TOOLTIP ;
THUMBBUTTON thbButtons [ 3 ];
thbButtons
[ 0 ]. dwMask = dwMask ;
thbButtons [ 0 ]. iId = 0 ;
thbButtons [ 0 ]. hIcon = g_hRed ;
StringCbCopy (thbButtons [ 0 ]. szTip , sizeof (thbButtons [ 0 ]. szTip ), TEXT ("Red" ));
thbButtons
[ 1 ]. dwMask = dwMask ;
thbButtons [ 1 ]. iId = 1 ;
thbButtons [ 1 ]. hIcon = g_hGreen ;
StringCbCopy (thbButtons [ 1 ]. szTip , sizeof (thbButtons [ 1 ]. szTip ), TEXT ("Green" ));
thbButtons
[ 2 ]. dwMask = dwMask ;
thbButtons [ 2 ]. iId = 2 ;
thbButtons [ 2 ]. hIcon = g_hBlue ;
StringCbCopy (thbButtons [ 2 ]. szTip , sizeof (thbButtons [ 2 ]. szTip ), TEXT ("Blue" ));
g_pTaskbar
-> ThumbBarAddButtons (g_hWnd , ARRAYSIZE (thbButtons ), thbButtons );
         效果如下:
  http://hi.iyunv.com/attachment/201005/25/0_1274801009e3t6.gif
       按钮有了,该响应这些按钮的消息了。点击这些按钮后,windows将会发送一个WM_COMMAND 消息,其中wParam 的高位为THBN_CLICKED ,低位是按钮的id(由参数 iId 设置 )。
  case WM_COMMAND:
    if (HIWORD (wParam ) == THBN_CLICKED )
    {
        OnThumbnailButtonClicked (LOWORD (wParam ));
    }
    break ;

  
  void OnThumbnailButtonClicked ( int id )
{
    switch (id )
    {
    case 0 :
        SetForegroundWindow (g_hWnd );
        MessageBox (g_hWnd , TEXT ("Red button clicked!" ), NULL , MB_OK );
        break ;
    case 1 :
        SetForegroundWindow (g_hWnd );
        MessageBox (g_hWnd , TEXT ("Green button clicked!" ), NULL , MB_OK );
        break ;
    case 2 :
        SetForegroundWindow (g_hWnd );
        MessageBox (g_hWnd , TEXT ("Blue button clicked!" ), NULL , MB_OK );
        break ;
    }
}

        现在点击工具栏上的按钮将会有显示对应的消息框。
         Tooltip比较简单。SetThumbnailTooltip 传入窗口句柄和描述的文字就可以了。在 WM_TASKBARBUTTONCREATED 消息处理中加入下面代码:
  g_pTaskbar -> SetThumbnailTooltip (g_hWnd , TEXT ("Some information" ));
  信息提示显示在预览窗之上:
  http://hi.iyunv.com/attachment/201005/26/0_1274871118dj5S.gif

五、缩略图和Aero Peek预览(Thumbnail, Preview)
         到目前为止,这个程序的缩略图和Aero Peek预览仍然是由系统来控制。对于大多数程序来说,系统自动产生的缩略图和预览几经足够。但有时候我们想要自己控制,比如foobar2000需要在缩略图中显示唱片封面(见第一张图)。
         要实现手动控制,需要在窗口创建以后设置一下窗口的属性,告诉Windows该窗口的缩略图和预览图需要由程序提供。在窗口创建之后加入下面代码:
  //设置窗口属性,打开自定义缩略图和AeroPeek预览
BOOL truth = TRUE ;
DwmSetWindowAttribute (g_hWnd , DWMWA_HAS_ICONIC_BITMAP , & truth , sizeof (truth ));
DwmSetWindowAttribute (g_hWnd , DWMWA_FORCE_ICONIC_REPRESENTATION , & truth , sizeof (truth ));

        通过这样的设置以后,在WndProc中就可以收到WM_DWMSENDICONICTHUMBNAILWM_DWMSENDICONICLIVEPREVIEWBITMAP 这两个消息,它们分别是Windows向程序请求Thumbnail和Aero Peek Preview时发送的消息。对于第一个消息,参数wParam的高位和低位分别表示所请求的缩略图的最大宽度和高度。在消息处理中,通过DwmSetIconicThumbnail将一个位图句柄(HBITMAP)发送给系统。这里系统要求的位图大小不能超过参数传入的最大宽度和高度,而且必须是32位的位图,但是一般的软件只能保存24位的位图,这样的位图不符合要求,是无法显示的。于是我利用GDI+写了两个辅助函数,用于将PNG格式的资源加载为HBITMAP,以及缩放大小。
         第一个函数是加载PNG格式的资源,参数分别是:模块句柄(如果是本模块可使用GetModuleHandle(NULL)取得句柄),资源id,资源类型。这段代码所做的就是将资源加载到内存中,并由这块内存创建一个Bitmap对象。
  //将PNG等格式的资源加载为 GdiPlus::Bitmap
Gdiplus :: Bitmap * LoadBitmapFromResource ( HMODULE hModule , UINT resID , LPCTSTR resType )
{
    //分配一块内存,把加载的资源拷贝到内存中
    //取得资源的句柄
    HRSRC hRsrc = FindResource (hModule , MAKEINTRESOURCE (resID ), resType );
    if (hRsrc == NULL )
    {
        return NULL ;
    }
    //资源的大小
    int size = SizeofResource (GetModuleHandle (NULL ), hRsrc );
    //加载资源
    HGLOBAL hGlobal = LoadResource (GetModuleHandle (NULL ), hRsrc );
    if (hGlobal == NULL )
    {
        return NULL ;
    }
    //分配相同大小的一块内存
    HGLOBAL hGlobal2 = GlobalAlloc (GMEM_MOVEABLE , size );
    if (hGlobal2 == NULL )
    {
        FreeResource (hGlobal );
        return NULL ;
    }
    //锁资源
    LPVOID lpVoid = LockResource (hGlobal );
    if (lpVoid == NULL )
    {
        FreeResource (hGlobal );
        GlobalFree (hGlobal2 );
        return NULL ;
    }
    //锁内存
    LPVOID lpVoid2 = GlobalLock (hGlobal2 );
    if (lpVoid2 == NULL )
    {
        UnlockResource (hGlobal );
        FreeResource (hGlobal );
        GlobalFree (hGlobal2 );
        return NULL ;
    }
    //将资源拷贝到内存
    memcpy (lpVoid2 , lpVoid , size );
    //解锁
    GlobalUnlock (hGlobal2 );
    UnlockResource (hGlobal );
    //创建流
    LPSTREAM lpStream ;
    HRESULT hr = CreateStreamOnHGlobal (hGlobal2 , TRUE , & lpStream );
    if (hr != S_OK )
    {
        FreeResource (hGlobal );
        GlobalFree (hGlobal2 );
        return NULL ;
    }
    //基于流创建GdiPlus::Bitmap
    Gdiplus :: Bitmap * bmp = Gdiplus :: Bitmap :: FromStream (lpStream );
    //释放资源和内存
    FreeResource (hGlobal );
    GlobalFree (hGlobal2 );
    return bmp ;
}

         第二个函数是缩放Bitmap对象,最后一个参数可以选择是否要保持长宽比

  //缩放 GdiPlua::Bitmap
Gdiplus :: Bitmap * ResizeBitmap ( Gdiplus :: Bitmap * bmpSrc ,
int destWidth , int destHeight , bool keepAspect = true )
{
    //取得源图片宽度和高度
    int srcWidth = bmpSrc -> GetWidth ();
    int srcHeight = bmpSrc -> GetHeight ();
    //计算横向和纵向的缩放比率
    float scaleH = (float )destWidth / srcWidth ;
    float scaleV = (float )destHeight / srcHeight ;
    //如果需要保持长宽比,则横向和纵向统一采用较小的缩放比率
    if (keepAspect )
    {
        if (scaleH > scaleV )
        {
            scaleH = scaleV ;
        }
        else
        {
            scaleV = scaleH ;
        }
    }
    //计算目标宽高
    destWidth   = (int )(srcWidth * scaleH );
    destHeight = (int )(srcHeight * scaleV );
    //创建目标Bitmap
    Gdiplus :: Bitmap * bmpDest = new Gdiplus :: Bitmap (destWidth , destHeight , PixelFormat32bppARGB );
    Gdiplus :: Graphics graphic (bmpDest );
    //设置插值算法
    graphic . SetInterpolationMode (Gdiplus :: InterpolationModeHighQualityBicubic );
    //将源图像绘制到目标图像上
    graphic . DrawImage (bmpSrc , Gdiplus :: Rect (0 , 0 , destWidth , destHeight ),

        0 , 0 , srcWidth , srcHeight , Gdiplus :: UnitPixel );
    graphic . Flush ();
    return bmpDest ;
}

        在调用这两个函数之前,还有一点工作要做。 那就是要初始化GDI+。在WinMain函数的开头,加入下面的初始化代码:
  //初始化GDI+
ULONG_PTR token ;
Gdiplus :: GdiplusStartupInput input ;
Gdiplus :: GdiplusStartup (& token , & input , NULL );

  在WinMain函数结束的时候别忘了关闭GDI+:
  //关闭GDI+
Gdiplus :: GdiplusShutdown (token );

  准备工作做好以后,可以正式来处理缩略图了。消息处理函数中,加入下面的消息处理代码:
  case WM_DWMSENDICONICTHUMBNAIL:
    OnSendThumbnail (hWnd , HIWORD (lParam ), LOWORD (lParam ));
    break ;

         在这里我们得到了缩略图需要的宽和高,转到OnSendThumbnail 函数中处理。
  void OnSendThumbnail ( HWND hWnd , int width , int height )
{
    //将资源加载为Bitmap
    Gdiplus :: Bitmap * bmp = LoadBitmapFromResource (GetModuleHandle (NULL ), IDB_THUMB , TEXT ("PNG" ));
    //缩放Bitmap
    Gdiplus :: Bitmap * bmpResized = ResizeBitmap (bmp , width , height , false );
    //取得位图句柄
    HBITMAP hbmp ;
    bmpResized -> GetHBITMAP (UINT (Gdiplus :: Color :: AlphaMask ), & hbmp );
    //发送位图句柄
    DwmSetIconicThumbnail (hWnd , hbmp , 0 );
    //释放对象
    DeleteObject (hbmp );
    delete bmpResized ;
    delete bmp ;
}

        因为没有保持长宽比,所以得到下面的效果:
  http://hi.iyunv.com/attachment/201005/26/0_1274878187Pcm3.gif
        如果保持长宽比,将得到下面的效果:
  http://hi.iyunv.com/attachment/201005/26/0_1274878876R1pO.gif
        接下来处理Aero Peek预览图,对应的Windows消息是WM_DWMSENDICONICLIVEPREVIEWBITMAP , 但是在该消息的参数中,没有提供宽和高。而如果我们提供的预览图的宽和高超过了窗口的客户区大小,同样是不能显示的。所以我们要手动获取客户区的大小,然后将图像缩放到客户区的尺寸。加入如下消息处理:
  case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
    OnSendPreview (hWnd );
    break ;

  
  void OnSendPreview ( HWND hWnd )
{
    //取得窗口客户区域
    RECT rcClient ;
    GetClientRect (hWnd , & rcClient );
    //将资源加载为 Bitmap
    Gdiplus :: Bitmap * bmp = LoadBitmapFromResource (GetModuleHandle (NULL ), IDB_PREVIEW , TEXT ("PNG" ));
    Gdiplus :: Bitmap * bmpResized = ResizeBitmap (bmp , rcClient . right - rcClient . left ,
        rcClient . bottom - rcClient . top , false );
    //取得位图句柄
    HBITMAP hbmp ;
    bmpResized -> GetHBITMAP (UINT (Gdiplus :: Color :: AlphaMask ), & hbmp );
    //发送位图句柄
    DwmSetIconicLivePreviewBitmap (hWnd , hbmp , NULL , 0 );
    //释放对象
    DeleteObject (hbmp );
    delete bmpResized ;
    delete bmp ;
}

        得到下面的效果 ,这张变形金刚的图片是我们发送给Windows的预览图,实际程序的界面上并没有这张图 。
  http://hi.iyunv.com/attachment/201005/26/0_1274880272dhXs.gif
         有一点 要提醒一下,Windows会将缩略图缓存,并不会每次都向程序请求缩略图,所以如果要更新缩略图的话需要调用API函数 DwmInvalidateIconicBitmaps ,这样当Windows再次需要显示缩略图时,就会发送WM_DWMSENDICONICTHUMBNAIL 消息,向应用程序请求缩略图。
  

         至此,上面提到的所有功能都已实现。有几个头文件需要包含到程序中:
  #include  
#include  
#include  
#include  
#include  

         编译时需要链接的库有:dwmapi.lib gdiplus.lib
  
         下载本篇源代码

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-66003-1-1.html 上篇帖子: Windows 7 更改全半角切换快捷键(Shif+Space) 下篇帖子: Windows Phone 7 应用程序使用WCF Service
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表