Интеграция .NET-приложения с таскбаром Windows 7

3 мая 2011 г.

Intro

Прежде всего хочу отметить, что в .NET 4.0 есть поддержка некоторых вещей, о которых будет идти речь. Но проект, на котором пришлось использовать данные вещи, начинался более 2-ух лет назад на .NET 2.0 и позже был переведен на 3.5. В целях совместимости системы расширений и инсталятора решили на 4.0 не переходить, т. к. ничего от этого мы бы не получили (интеграция с Windows 7 уже давно разрабатывалась).

Опера кнопки

Отображение прогресса в таскбаре Windows 7

Гугл сразу объяснил, что нужно использовать системный интерфейс ITaskbarList3 и класс CTaskbarList. После штудирования MSDN и интернета была готова обертка для таскбара:

  public static class Taskbar
   {
     #region Types
     public enum ProgressState
     {
       NoProgress = 0,
       Indeterminate = 1, // Marquee
       Normal = 2, // Green
       Error = 4, // Red
       Paused = 8 // Yellow
     }

     [StructLayout( LayoutKind.Sequential )]
     private struct Rectangle
     {
       public int Left;
       public int Top;
       public int Right;
       public int Bottom;

       public Rectangle( int left, int top, int right, int bottom )
       {
         Left = left;
         Top = top;
         Right = right;
         Bottom = bottom;
       }
     }

     private enum ThumbTabFlags
     {
       UseMdiThumbnail = 0x1,
       UseMdiLivePreview = 0x2
     }

     private enum ThumbBtnMask
     {
       Bitmap = 0x1,
       Icon = 0x2,
       Tooltip = 0x4,
       Flags = 0x8
     }

     private enum ThumbBtnFlags
     {
       Enabled = 0,
       Disabled = 0x1,
       DismissOnClick = 0x2,
       NoBackground = 0x4,
       Hidden = 0x8
     }

     [StructLayout( LayoutKind.Sequential, CharSet = CharSet.Auto )]
     private struct ThumbBtn
     {
       [MarshalAs( UnmanagedType.U4 )]
       public ThumbBtnMask Mask;

       public uint Id;

       public uint Bitmap;

       public IntPtr Icon;

       [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 260 )]
       public string Tip;

       [MarshalAs( UnmanagedType.U4 )]
       public ThumbBtnFlags Flags;
     }

     [ComImportAttribute]
     [GuidAttribute( "ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf" )]
     [InterfaceTypeAttribute( ComInterfaceType.InterfaceIsIUnknown )]
     private interface ITaskbarList3
     {
       [PreserveSig] void HrInit();
       [PreserveSig] void AddTab( IntPtr hwnd );
       [PreserveSig] void DeleteTab( IntPtr hwnd );
       [PreserveSig] void ActivateTab( IntPtr hwnd );
       [PreserveSig] void SetActiveAlt( IntPtr hwnd );
       [PreserveSig] void MarkFullscreenWindow( IntPtr hwnd, [MarshalAs( UnmanagedType.Bool )] bool fFullscreen );
       void SetProgressValue( IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal );
       void SetProgressState( IntPtr hwnd, ProgressState tbpFlags );
       void RegisterTab( IntPtr hwndTab, IntPtr hwndMDI );
       void UnregisterTab( IntPtr hwndTab );
       void SetTabOrder( IntPtr hwndTab, IntPtr hwndInsertBefore );
       void SetTabActive( IntPtr hwndTab, IntPtr hwndMDI, ThumbTabFlags tbatFlags );
       void ThumbBarAddButtons( IntPtr hwnd, uint cButtons, [MarshalAs( UnmanagedType.LPArray )] ThumbBtn[] pButtons );
       void ThumbBarUpdateButtons( IntPtr hwnd, uint cButtons, [MarshalAs( UnmanagedType.LPArray )] ThumbBtn[] pButtons );
       void ThumbBarSetImageList( IntPtr hwnd, IntPtr himl );
       void SetOverlayIcon( IntPtr hwnd, IntPtr hIcon, [MarshalAs( UnmanagedType.LPWStr )] string pszDescription );
       void SetThumbnailTooltip( IntPtr hwnd, [MarshalAs( UnmanagedType.LPWStr )] string pszTip );
       void SetThumbnailClip( IntPtr hwnd, ref Rectangle prcClip );
     }

     [GuidAttribute( "56FDF344-FD6D-11d0-958A-006097C9A090" )]
     [ClassInterfaceAttribute( ClassInterfaceType.None )]
     [ComImportAttribute]
     private class CTaskbarList { }
     #endregion

     #region Fields
     private static ITaskbarList3 taskbarList;
     #endregion

     #region Properties
     private static ITaskbarList3 TaskbarList
     {
       get
       {
         if ( taskbarList == null )
         {
           taskbarList = (ITaskbarList3)new CTaskbarList();
           taskbarList.HrInit();
         }

         return taskbarList;
       }
     }
     #endregion
   }

Теперь через свойство TaskbarList мы имеем доступ к API таскбара.
Отдельно стоит напомнить, что при обращении к свойству стоит проверить версию ОС, которая должна быть не ниже 6.1. У нас эта проверка выполнялась в Platform API Manager (класс, который отвечал за дергание API или его замещении при выполнении на ОС низших версий).
Дальше были реализованы методы для работы с таскбаром:

   public static void SetProgressState( IntPtr handle, ProgressState state )
   {
     TaskbarList.SetProgressState( handle, state );
   }

   public static void SetProgressValue( IntPtr handle, ulong current, ulong maximum )
   {
     TaskbarList.SetProgressValue( handle, current, maximum );
   }

   public static void SetOverlayIcon( IntPtr handle, Icon icon, string description )
   {
     TaskbarList.SetOverlayIcon( handle, icon == null ? IntPtr.Zero : icon.Handle, description );
   }

   public static void SetTaskbarTooltip( IntPtr handle, string tooltip )
   {
     TaskbarList.SetThumbnailTooltip( handle, tooltip );
   }

Чтобы установить нужное состояние и значение прогресса операции нужно вызвать методы SetProgressState и SetProgressValue, передав им хендл окна приложения и желаемые значения состояния индикатора и прогресса.

Состояние прогресса

Метод SetOverlayIcon позволяет отобразить небольшую иконку на кнопке приложения на таскбаре.
Метод SetTaskbarTooltip позволяет установить тултип который будет отображен при показе превью окна.

Отображение кнопок в превью окне на таскбаре Windows 7

Кнопке на превью окне можно присвоить ID, иконку и тултип. Реализуем методы для работы с кнопками в превью окне на таскбаре Windows 7:

 public static void SetTaskbarButtons( IntPtr handle, List buttons )
   {
     TaskbarList.ThumbBarAddButtons( handle, (uint)buttons.Count, buttons.ToArray() );
   }

   public static ThumbBtn CreateTaskbarButton( uint id, Bitmap icon, string tooltip )
   {
     return
       new ThumbBtn
       {
         Id = id,
         Icon = icon.GetHicon(),
         Tip = tooltip,
         Flags = ThumbBtnFlags.Enabled,
         Mask = Taskbar.ThumbBtnMask.Icon | Taskbar.ThumbBtnMask.Tooltip | Taskbar.ThumbBtnMask.Flags
       };
   }

А вот и код, который создает кнопки:

 List buttons = new List();

   buttons.Add( CreateTaskbarButton( 1901, Resources.Icons.Prev, Resources.Strings.Prev ) );
   buttons.Add( CreateTaskbarButton( 1902, Resources.Icons.Play, Resources.Strings.PlayPause ) );
   buttons.Add( CreateTaskbarButton( 1903, Resources.Icons.Next, Resources.Strings.Next ) );

   Taskbar.SetTaskbarButtons( handle, buttons );

Передаем хендл окна и массив кнопок.

***

Чтобы наше приложение могло реагировать на эти кнопки, нужно добавить обработчик:

 protected override void WndProc( ref Forms.Message message )
   {
     if ( message.Msg == 0x111 ) // WM_COMMAND
     {
       uint buttonID = (uint)message.WParam - 402653184;

       switch ( buttonID )
       {
         case 1901:
           WindowsMediaPlayer.Prev();
           return;

         case 1902:
           WindowsMediaPlayer.Play();
           return;

         case 1903:
           WindowsMediaPlayer.Next();
           return;
       }
     }
   }

При получении WM_COMMAND выдираем ID кнопки и определяем реакцию. Замечу: если обработчик ресурсоемкий, выполняйте обработку в другом потоке.

Получение закрепленных на таскбаре Windows 7 приложений

Вызвав метод GetTaskBarItems (см. ниже) мы получим список путей к файлам, которые закреплены на таскбаре Windows 7. Замечу, что метот вернет только те приложения, которые закреплены через прямые пути к файлу на диске.

 public static class QuickLaunch
   {
     public static readonly Regex PathExpression = new Regex( @"[a-zA-Z]{1}:\\([a-zA-Z0-9 \.\(\)]+\\)+[a-zA-Z0-9 \.\(\)]+.(\w)+" );

     #region Properties
     public static string PathToQuickLaunchShortcuts
     {
       get
       {
         return
           Path.Combine(
             Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData ),
             @"Microsoft\Internet Explorer\Quick Launch" );
       }
     }

     public static string PathToStartMenuShortcuts
     {
       get { return Path.Combine( PathToQuickLaunchShortcuts, @"User Pinned\StartMenu" ); }
     }

     public static string PathToTaskBarShortcuts
     {
       get { return Path.Combine( PathToQuickLaunchShortcuts, @"User Pinned\TaskBar" ); }
     }
     #endregion

     #region Methods
     public static string GetTargetPath( string lnkFile )
     {
       try
       {
         return PathExpression.Match( File.ReadAllText( lnkFile ) ).Value;
       }
       catch
       {
         return null;
       }
     }

     public static List<string> GetItems( string folder )
     {
         return
           (from file in Directory.GetFiles( folder, "*.lnk" )
            let path = GetTargetPath( file )
            where !string.IsNullOrEmpty( path )
            select path)
           .ToList();
     }

     public static List<string> GetQuickLaunchItems()
     {
       return GetItems( PathToQuickLaunchShortcuts );
     }

     public static List<string> GetStartMenuItems()
     {
       return GetItems( PathToStartMenuShortcuts );
     }

     public static List<string> GetTaskBarItems()
     {
       return GetItems( PathToTaskBarShortcuts );
     }

     public static List<string> GetAllKnownItems()
     {
       List<string> items = GetTaskBarItems();

       foreach ( var item in GetStartMenuItems() )
       {
         if ( !items.Any( i => string.Compare( i, item, true ) == 0 ) )
           items.Add( item );
       }

       foreach ( var item in GetQuickLaunchItems() )
       {
         if ( !items.Any( i => string.Compare( i, item, true ) == 0 ) )
           items.Add( item );
       }

       return items.ToList();
     }
     #endregion
   }

Как бонус, методы GetQuickLaunchItems и GetStartMenuItems вернут пути к файлам закрепленным на панели Quick Launch и в меню по кнопке Start соответственно.

Теги: рубрика Windows, Программирование
  • Похожие статьи
  • Предыдущие из рубрики