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
}
* This source code was highlighted with Source Code Highlighter.
Теперь через свойство 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 );
}
* This source code was highlighted with Source Code Highlighter.
Чтобы установить нужное состояние и значение прогресса операции нужно вызвать методы 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
};
}
* This source code was highlighted with Source Code Highlighter.
А вот и код, который создает кнопки:
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 );
* This source code was highlighted with Source Code Highlighter.
Передаем хендл окна и массив кнопок.
Чтобы наше приложение могло реагировать на эти кнопки, нужно добавить обработчик:
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;
}
}
}
* This source code was highlighted with Source Code Highlighter.
При получении 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
}
* This source code was highlighted with Source Code Highlighter.
Как бонус, методы GetQuickLaunchItems и GetStartMenuItems вернут пути к файлам закрепленным на панели Quick Launch и в меню по кнопке Start соответственно.
To be continued
Этот краткий экскурс является лишь небольшой выдержкой из того, с чем мне пришлось поработать на одном десктопном проекте. Если читатели найдутся, то таких статей будет много и не только по Windows 7 API. Список технологий, используемых на этом проекте, в которых мне пришлось "порытся" или столкнутся с "костылями" довольно широк: это проблемы микса WinForms и WPF, подводные камни WinForms, Window 7 API, API прошлых версий Windows, RSS, FTP, ID3Tags и Vorbis Comment, и многое другое.