added TaskbarLib (not compilable yet)

git-svn-id: svn://svn.code.sf.net/p/processhacker/code@1947 21ef857c-d57f-4fe0-8362-d861dc6d29cd
This commit is contained in:
wj32
2009-09-26 03:35:55 +00:00
parent 89e6fc6453
commit 1ff009849e
9 changed files with 2171 additions and 0 deletions
@@ -0,0 +1,197 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Runtime.CompilerServices;
namespace TaskbarLib.Interop
{
#region "Interface Classes"
[ComImportAttribute()]
[GuidAttribute("86C14003-4D6B-4EF3-A7B4-0506663B2E68")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CApplicationDestinations { }
[ComImportAttribute()]
[GuidAttribute("86BEC222-30F2-47E0-9F25-60D11CD75C28")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CApplicationDocumentLists { }
[ComImportAttribute()]
[GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CTaskbarList { }
[ComImportAttribute()]
[GuidAttribute("00021401-0000-0000-C000-000000000046")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CShellLink { }
[ComImportAttribute()]
[GuidAttribute("77F10CF0-3DB5-4966-B520-B7C54FD35ED6")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CDestinationList { }
[ComImportAttribute()]
[GuidAttribute("2D3468C1-36A7-43B6-AC24-D3F02FD9607A")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
internal class CEnumerableObjectCollection { }
#endregion
[ComImportAttribute()]
[GuidAttribute("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IObjectArray
{
void GetCount(out uint cObjects);
void GetAt(uint iIndex, ref Guid riid, [Out(), MarshalAs(UnmanagedType.Interface)] out object ppvObject);
}
[ComImportAttribute()]
[GuidAttribute(SafeNativeMethods.IID_IObjectCollection)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IObjectCollection
{
// IObjectArray
[PreserveSig]
void GetCount(out uint cObjects);
[PreserveSig]
void GetAt(uint iIndex, ref Guid riid, [Out(), MarshalAs(UnmanagedType.Interface)] out object ppvObject);
// IObjectCollection
void AddObject([MarshalAs(UnmanagedType.Interface)] object pvObject);
void AddFromArray([MarshalAs(UnmanagedType.Interface)] IObjectArray poaSource);
void RemoveObject(uint uiIndex);
void Clear();
}
[ComImportAttribute()]
[GuidAttribute("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPropertyStore
{
void GetCount(out UInt32 cProps);
void GetAt(UInt32 iProp, [MarshalAs(UnmanagedType.Struct)] out PropertyKey pkey);
void GetValue([In, MarshalAs(UnmanagedType.Struct)] ref PropertyKey pkey, [Out(), MarshalAs(UnmanagedType.Struct)] out PropVariant pv);
void SetValue([In, MarshalAs(UnmanagedType.Struct)] ref PropertyKey pkey, [In, MarshalAs(UnmanagedType.Struct)] ref PropVariant pv);
void Commit();
}
[ComImportAttribute()]
[GuidAttribute("6332DEBF-87B5-4670-90C0-5E57B408A49E")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ICustomDestinationList
{
void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
void BeginList(out uint cMaxSlots, ref Guid riid, [Out(), MarshalAs(UnmanagedType.Interface)] out object ppvObject);
void AppendCategory([MarshalAs(UnmanagedType.LPWStr)] string pszCategory, [MarshalAs(UnmanagedType.Interface)] IObjectArray poa);
void AppendKnownCategory([MarshalAs(UnmanagedType.I4)] KNOWNDESTCATEGORY category);
void AddUserTasks([MarshalAs(UnmanagedType.Interface)] IObjectArray poa);
void CommitList();
void GetRemovedDestinations(ref Guid riid, [Out(), MarshalAs(UnmanagedType.Interface)] out object ppvObject);
void DeleteList([MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
void AbortList();
}
[ComImportAttribute()]
[GuidAttribute("12337D35-94C6-48A0-BCE7-6A9C69D4D600")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IApplicationDestinations
{
void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
void RemoveDestination([MarshalAs(UnmanagedType.Interface)] object pvObject);
void RemoveAllDestinations();
}
[ComImportAttribute()]
[GuidAttribute("3C594F9F-9F30-47A1-979A-C9E83D3D0A06")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IApplicationDocumentLists
{
void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
void GetList([MarshalAs(UnmanagedType.I4)] APPDOCLISTTYPE listtype, uint cItemsDesired, ref Guid riid, [Out(), MarshalAs(UnmanagedType.Interface)] out object ppvObject);
}
[ComImportAttribute()]
[GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ITaskbarList3
{
// ITaskbarList
[PreserveSig]
void HrInit();
[PreserveSig]
void AddTab(IntPtr hwnd);
[PreserveSig]
void DeleteTab(IntPtr hwnd);
[PreserveSig]
void ActivateTab(IntPtr hwnd);
[PreserveSig]
void SetActiveAlt(IntPtr hwnd);
// ITaskbarList2
[PreserveSig]
void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);
// ITaskbarList3
void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
void SetProgressState(IntPtr hwnd, TBPFLAG tbpFlags);
void RegisterTab(IntPtr hwndTab, IntPtr hwndMDI);
void UnregisterTab(IntPtr hwndTab);
void SetTabOrder(IntPtr hwndTab, IntPtr hwndInsertBefore);
void SetTabActive(IntPtr hwndTab, IntPtr hwndMDI, TBATFLAG tbatFlags);
void ThumbBarAddButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray)] THUMBBUTTON[] pButtons);
void ThumbBarUpdateButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray)] THUMBBUTTON[] 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, /*[MarshalAs(UnmanagedType.LPStruct)]*/ ref RECT prcClip);
}
[ComImportAttribute()]
[GuidAttribute("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid bhid, [In] ref Guid riid, out IntPtr ppv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
}
[ComImportAttribute()]
[GuidAttribute("000214F9-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellLinkW
{
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, IntPtr pfd, uint fFlags);
void GetIDList(out IntPtr ppidl);
void SetIDList(IntPtr pidl);
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName);
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
void GetHotKey(out short wHotKey);
void SetHotKey(short wHotKey);
void GetShowCmd(out uint iShowCmd);
void SetShowCmd(uint iShowCmd);
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int iIcon);
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
void Resolve(IntPtr hwnd, uint fFlags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
}
@@ -0,0 +1,290 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
namespace TaskbarLib.Interop
{
[Flags]
internal enum KNOWNDESTCATEGORY
{
KDC_FREQUENT = 1,
KDC_RECENT
}
[Flags]
internal enum APPDOCLISTTYPE
{
ADLT_RECENT = 0,
ADLT_FREQUENT
}
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
[Flags]
internal enum TBPFLAG
{
TBPF_NOPROGRESS = 0,
TBPF_INDETERMINATE = 0x1,
TBPF_NORMAL = 0x2,
TBPF_ERROR = 0x4,
TBPF_PAUSED = 0x8
}
[Flags]
internal enum TBATFLAG
{
TBATF_USEMDITHUMBNAIL = 0x1,
TBATF_USEMDILIVEPREVIEW = 0x2
}
[Flags]
internal enum THBMASK
{
THB_BITMAP = 0x1,
THB_ICON = 0x2,
THB_TOOLTIP = 0x4,
THB_FLAGS = 0x8
}
[Flags]
internal enum THBFLAGS
{
THBF_ENABLED = 0,
THBF_DISABLED = 0x1,
THBF_DISMISSONCLICK = 0x2,
THBF_NOBACKGROUND = 0x4,
THBF_HIDDEN = 0x8
}
[Flags]
internal enum SIGDN : uint
{
SIGDN_NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL
SIGDN_PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING
SIGDN_PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING
SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR
SIGDN_FILESYSPATH = 0x80058000, // SHGDN_FORPARSING
SIGDN_URL = 0x80068000, // SHGDN_FORPARSING
SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR
SIGDN_PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER
}
[StructLayout(LayoutKind.Sequential)]
internal struct POINT
{
internal int X;
internal int Y;
internal POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct THUMBBUTTON
{
[MarshalAs(UnmanagedType.U4)]
public THBMASK dwMask;
public uint iId;
public uint iBitmap;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szTip;
[MarshalAs(UnmanagedType.U4)]
public THBFLAGS dwFlags;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct PropertyKey
{
public Guid fmtid;
public uint pid;
public PropertyKey(Guid fmtid, uint pid)
{
this.fmtid = fmtid;
this.pid = pid;
}
public static PropertyKey PKEY_Title = new PropertyKey(new Guid("F29F85E0-4FF9-1068-AB91-08002B27B3D9"), 2);
public static PropertyKey PKEY_AppUserModel_ID = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 5);
public static PropertyKey PKEY_AppUserModel_IsDestListSeparator = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 6);
public static PropertyKey PKEY_AppUserModel_RelaunchCommand = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 2);
public static PropertyKey PKEY_AppUserModel_RelaunchDisplayNameResource = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 4);
public static PropertyKey PKEY_AppUserModel_RelaunchIconResource = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 3);
}
[StructLayout(LayoutKind.Explicit)]
internal struct CALPWSTR
{
[FieldOffset(0)]
internal uint cElems;
[FieldOffset(4)]
internal IntPtr pElems;
}
[StructLayout(LayoutKind.Explicit)]
internal struct PropVariant
{
[FieldOffset(0)]
private ushort vt;
[FieldOffset(8)]
private IntPtr pointerValue;
[FieldOffset(8)]
private byte byteValue;
[FieldOffset(8)]
private long longValue;
[FieldOffset(8)]
private short boolValue;
[MarshalAs(UnmanagedType.Struct)]
[FieldOffset(8)]
private CALPWSTR calpwstr;
[DllImport("ole32.dll")]
private static extern int PropVariantClear(ref PropVariant pvar);
public VarEnum VarType
{
get { return (VarEnum)vt; }
}
public void SetValue(String val)
{
this.Clear();
this.vt = (ushort)VarEnum.VT_LPWSTR;
this.pointerValue = Marshal.StringToCoTaskMemUni(val);
}
public void SetValue(bool val)
{
this.Clear();
this.vt = (ushort)VarEnum.VT_BOOL;
this.boolValue = val ? (short)-1 : (short)0;
}
public string GetValue()
{
return Marshal.PtrToStringUni(this.pointerValue);
}
public void Clear()
{
PropVariantClear(ref this);
}
}
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
//Obviously, these GUIDs shouldn't be modified. The reason they
//are not readonly is that they are passed with 'ref' to various
//native methods.
public static Guid IID_IObjectArray = new Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9");
public const string IID_IObjectCollection = "5632B1A4-E38A-400A-928A-D4CD63230295";
public static Guid IID_IPropertyStore = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99");
public static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
public const int DWM_SIT_DISPLAYFRAME = 0x00000001;
public const int DWMWA_FORCE_ICONIC_REPRESENTATION = 7;
public const int DWMWA_HAS_ICONIC_BITMAP = 10;
public const int WM_COMMAND = 0x111;
public const int WM_SYSCOMMAND = 0x112;
public const int WM_DWMSENDICONICTHUMBNAIL = 0x0323;
public const int WM_DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326;
public const int WM_CLOSE = 0x0010;
public const int WM_ACTIVATE = 0x0006;
public const int WA_ACTIVE = 1;
public const int WA_CLICKACTIVE = 2;
public const int SC_CLOSE = 0xF060;
public const int MSGFLT_ADD = 1;
public const int MSGFLT_REMOVE = 2;
// Thumbbutton WM_COMMAND notification
public const uint THBN_CLICKED = 0x1800;
}
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
[DllImport("shell32.dll")]
public static extern int SHGetPropertyStoreForWindow(IntPtr hwnd, ref Guid iid /*IID_IPropertyStore*/, [Out(), MarshalAs(UnmanagedType.Interface)] out IPropertyStore propertyStore);
[DllImport("dwmapi.dll")]
public static extern int DwmSetIconicThumbnail(IntPtr hwnd, IntPtr hbitmap, uint flags);
[DllImport("dwmapi.dll")]
public static extern int DwmSetIconicLivePreviewBitmap(IntPtr hwnd, IntPtr hbitmap, ref POINT ptClient, uint flags);
[DllImport("dwmapi.dll")]
public static extern int DwmSetIconicLivePreviewBitmap(IntPtr hwnd, IntPtr hbitmap, IntPtr ptClient, uint flags);
[DllImport("dwmApi.dll")]
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttributeToSet, IntPtr pvAttributeValue, uint cbAttribute);
[DllImport("dwmApi.dll")]
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttributeToSet, ref int pvAttributeValue, uint cbAttribute);
[DllImport("dwmapi.dll")]
public static extern int DwmInvalidateIconicBitmaps(IntPtr hwnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeWindowMessageFilter(uint message, uint flags);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern uint RegisterWindowMessage(string lpString);
public static readonly uint WM_TaskbarButtonCreated = RegisterWindowMessage("TaskbarButtonCreated");
[DllImport("shell32.dll")]
public static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID);
[DllImport("shell32.dll")]
public static extern void GetCurrentProcessExplicitAppUserModelID([Out(), MarshalAs(UnmanagedType.LPWStr)] out string AppID);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hwnd, ref RECT rect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRect(IntPtr hwnd, ref RECT rect);
public static bool GetClientSize(IntPtr hwnd, out System.Drawing.Size size)
{
RECT rect = new RECT();
if (!GetClientRect(hwnd, ref rect))
{
size = new System.Drawing.Size(-1, -1);
return false;
}
size = new System.Drawing.Size(rect.right, rect.bottom);
return true;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hwnd, int cmd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int X, int Y, int cx, int cy, uint flags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ClientToScreen(IntPtr hwnd, ref POINT point);
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hwnd, StringBuilder str, int maxCount);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt (IntPtr hDestDC, int destX, int destY, int width, int height, IntPtr hSrcDC, int srcX, int srcY, uint operation);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool StretchBlt(IntPtr hDestDC, int destX, int destY, int destWidth, int destHeight, IntPtr hSrcDC, int srcX, int srcY, int srcWidth, int srcHeight, uint operation);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("Shell32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern uint SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, /* The following parameter is not used - binding context. */ IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
}
}
@@ -0,0 +1,411 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TaskbarLib.Interop;
namespace TaskbarLib.DesktopIntegration
{
/// <summary>
/// A collection of categorized jump list destinations.
/// </summary>
internal sealed class JumpListDestinations
{
//TODO: This is highly inefficient, but we want to maintain
//insertion order when adding the categories because the bottom
//categories are first to be truncated if screen estate is low.
private SortedDictionary<int, List<IJumpListDestination>> _categorizedDestinations =
new SortedDictionary<int, List<IJumpListDestination>>();
public void AddDestination(IJumpListDestination destination)
{
List<IJumpListDestination> destinations =
_categorizedDestinations.Values.FirstOrDefault(
list => list.First().Category == destination.Category);
if (destinations == null)
{
destinations = new List<IJumpListDestination>();
_categorizedDestinations.Add(
_categorizedDestinations.Keys.LastOrDefault() + 1, destinations);
}
destinations.Add(destination);
}
public void DeleteDestination(IJumpListDestination destination)
{
List<IJumpListDestination> destinations =
_categorizedDestinations.Values.First(
list => list.First().Category == destination.Category);
IJumpListDestination toDelete = destinations.Find(
d => d.Path == destination.Path && d.Category == destination.Category && d.Title == destination.Title);
if (toDelete != null)
destinations.Remove(toDelete);
}
internal void RefreshDestinations(ICustomDestinationList destinationList)
{
if (_categorizedDestinations.Count == 0)
return;
foreach (int key in _categorizedDestinations.Keys)
{
IObjectCollection categoryContents =
(IObjectCollection)new CEnumerableObjectCollection();
var destinations = _categorizedDestinations[key];
foreach (IJumpListDestination destination in destinations)
{
categoryContents.AddObject(destination.GetShellRepresentation());
}
destinationList.AppendCategory(
destinations.First().Category,
(IObjectArray)categoryContents);
}
}
public IEnumerable<string> Categories
{
get
{
return
(from d in _categorizedDestinations.Keys
select _categorizedDestinations[d].First().Category);
}
}
public IEnumerable<IJumpListDestination> GetDestinationsByCategory(
string category)
{
return
(from k in _categorizedDestinations.Keys
let d = _categorizedDestinations[k]
where d.First().Category == category
select d).Single();
}
public void Clear()
{
_categorizedDestinations.Clear();
}
}
/// <summary>
/// A collection of jump list tasks.
/// </summary>
internal sealed class JumpListTasks
{
private List<IJumpListTask> _tasks = new List<IJumpListTask>();
public void AddTask(IJumpListTask task)
{
_tasks.Add(task);
}
public void DeleteTask(IJumpListTask task)
{
IJumpListTask toDelete = _tasks.Find(t => t.Path == task.Path && t.Arguments == task.Arguments);
if (toDelete != null)
_tasks.Remove(toDelete);
}
internal void RefreshTasks(ICustomDestinationList destinationList)
{
if (_tasks.Count == 0)
return;
IObjectCollection taskCollection =
(IObjectCollection)new CEnumerableObjectCollection();
foreach (IJumpListTask task in _tasks)
{
taskCollection.AddObject(task.GetShellRepresentation());
}
destinationList.AddUserTasks((IObjectArray)taskCollection);
}
public IEnumerable<IJumpListTask> Tasks
{
get { return _tasks; }
}
public void Clear()
{
_tasks.Clear();
}
}
/// <summary>
/// Represents a shell object that can be inserted to an application's
/// jump list.
/// </summary>
public interface IJumpListShellObject
{
/// <summary>
/// Gets or sets the object's title.
/// </summary>
string Title { get; }
/// <summary>
/// Gets or sets the object's path.
/// </summary>
string Path { get; }
/// <summary>
/// Gets the shell representation of an object, such as
/// <b>IShellLink</b> or <b>IShellItem</b>.
/// </summary>
/// <returns></returns>
object GetShellRepresentation();
}
/// <summary>
/// Represents a jump list destination.
/// </summary>
public interface IJumpListDestination : IJumpListShellObject
{
/// <summary>
/// Gets or sets the destination's category.
/// </summary>
string Category { get; }
}
/// <summary>
/// Represents a jump list task.
/// </summary>
public interface IJumpListTask : IJumpListShellObject
{
/// <summary>
/// Gets or sets the task's command line arguments.
/// </summary>
string Arguments { get; }
}
/// <summary>
/// Flags controlling the appearance of a window.
/// </summary>
public enum WindowShowCommand : uint
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays the window (including restoring
/// it to its original size and position).
/// </summary>
Normal = 1,
/// <summary>
/// Minimizes the window.
/// </summary>
Minimized = 2,
/// <summary>
/// Maximizes the window.
/// </summary>
Maximized = 3,
/// <summary>
/// Similar to <see cref="Normal"/>, except that the window
/// is not activated.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size
/// and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the window and activates the next top-level window.
/// </summary>
Minimize = 6,
/// <summary>
/// Minimizes the window and does not activate it.
/// </summary>
ShowMinimizedNoActivate = 7,
/// <summary>
/// Similar to <see cref="Normal"/>, except that the window is not
/// activated.
/// </summary>
ShowNA = 8,
/// <summary>
/// Activates and displays the window, restoring it to its original
/// size and position.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the initial value specified when
/// the process was created.
/// </summary>
Default = 10,
/// <summary>
/// Minimizes a window, even if the thread owning the window is not
/// responding. Use this only to minimize windows from a different
/// thread.
/// </summary>
ForceMinimize = 11
}
/// <summary>
/// Represents a separator in the task area of the jump list.
/// There is no need to set any properties on this class.
/// </summary>
public sealed class Separator : IJumpListTask
{
string IJumpListTask.Arguments
{
get { throw new NotImplementedException(); }
}
string IJumpListShellObject.Title
{
get { throw new NotImplementedException(); }
}
string IJumpListShellObject.Path
{
get { throw new NotImplementedException(); }
}
object IJumpListShellObject.GetShellRepresentation()
{
ShellLink shellLink = new ShellLink
{
IsSeparator = true
};
return shellLink.GetShellRepresentation();
}
}
/// <summary>
/// Represents a shell link (<b>IShellLink</b>) object.
/// </summary>
public sealed class ShellLink : IJumpListTask, IJumpListDestination
{
/// <summary>
/// Gets or sets the object's title.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Gets or sets the object's category.
/// </summary>
public string Category { get; set; }
/// <summary>
/// Gets or sets the object's path.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets the location of the object's icon.
/// </summary>
public string IconLocation { get; set; }
/// <summary>
/// Gets or sets the index of the object's icon in the specified
/// icon's location (<see cref="IconLocation"/>).
/// </summary>
public int IconIndex { get; set; }
/// <summary>
/// Gets or sets the object's arguments (passed to the command
/// line).
/// </summary>
public string Arguments { get; set; }
/// <summary>
/// Gets or sets the object's working directory.
/// </summary>
public string WorkingDirectory { get; set; }
/// <summary>
/// Gets or sets the show command of the launched application.
/// </summary>
public WindowShowCommand ShowCommand { get; set; }
/// <summary>
/// Gets or sets a flag indicating that the shell link
/// is a menu separator. If this flag is set, all other
/// properties are ignored.
/// </summary>
internal bool IsSeparator { get; set; }
/// <summary>
/// Gets the shell <b>IShellLink</b> representation
/// of the object.
/// </summary>
/// <returns>An <b>IShellLink</b> up-cast to <b>object</b>.</returns>
public object GetShellRepresentation()
{
IShellLinkW shellLink = (IShellLinkW)new CShellLink();
IPropertyStore propertyStore = (IPropertyStore)shellLink;
PropVariant propVariant = new PropVariant();
if (IsSeparator)
{
propVariant.SetValue(true);
propertyStore.SetValue(ref PropertyKey.PKEY_AppUserModel_IsDestListSeparator, ref propVariant);
propVariant.Clear();
}
else
{
shellLink.SetPath(Path);
if (!String.IsNullOrEmpty(IconLocation))
shellLink.SetIconLocation(IconLocation, IconIndex);
if (!String.IsNullOrEmpty(Arguments))
shellLink.SetArguments(Arguments);
if (!String.IsNullOrEmpty(WorkingDirectory))
shellLink.SetWorkingDirectory(WorkingDirectory);
shellLink.SetShowCmd((uint)ShowCommand);
propVariant.SetValue(Title);
propertyStore.SetValue(ref PropertyKey.PKEY_Title, ref propVariant);
propVariant.Clear();
}
propertyStore.Commit();
return shellLink;
}
}
/// <summary>
/// Represents a <b>IShellItem</b> object.
/// </summary>
public sealed class ShellItem : IJumpListDestination
{
string IJumpListShellObject.Title { get { return null; } }
/// <summary>
/// Gets or sets the object's category.
/// </summary>
public string Category { get; set; }
/// <summary>
/// Gets or sets the object's path.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets the shell <b>IShellItem</b> representation
/// of the object.
/// </summary>
/// <returns>An <b>IShellItem</b> up-cast to <b>object</b>.</returns>
public object GetShellRepresentation()
{
return null;// GetShellItemFromPath(Path);
//Note: this will only work if there is a file association
//for the extension we're trying to register to our program.
}
internal static IShellItem GetShellItemFromPath(string path)
{
if (String.IsNullOrEmpty(path))
throw new ArgumentNullException(
"path", "Shell item cannot be generated from null or empty path.");
IShellItem resultItem = default(IShellItem);
//Guid shellItemGuid = new Guid(IIDGuid.IShellItem);
//uint result = SHCreateItemFromParsingName(
// path,
// IntPtr.Zero,
// ref shellItemGuid,
// out resultItem);
//// Throw if an error occurred.
//System.Runtime.InteropServices.Marshal.ThrowExceptionForHR((int)result);
return resultItem;
}
}
}
@@ -0,0 +1,528 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Win32;
using TaskbarLib.Interop;
using System.Runtime.CompilerServices;
using TaskbarLib.DesktopIntegration;
namespace TaskbarLib.DesktopIntegration
{
/// <summary>
/// Provides services to manage taskbar jump lists, including
/// custom destinations and custom tasks.
/// </summary>
/// <remarks>
/// This class mostly borrows the Windows Shell's concepts where
/// jump lists are concerned including:
/// <b>Application destinations</b> - Destinations added to the application's
/// recent and frequent categories by the shell or by the application.
/// <b>Custom destinations</b> - Destinations added to the application's
/// jump list in other categories by the application.
/// <b>Tasks</b> - Tasks added to the application's jump list.
/// <b>The methods of this class are not thread-safe.</b>
/// </remarks>
public sealed class JumpListManager : IDisposable
{
#region Members
string _appId;
JumpListDestinations _destinations;
JumpListTasks _tasks;
ICustomDestinationList _customDestinationList;
uint _maxSlotsInList;
ApplicationDestinationType _enabledAutoDestinationType = ApplicationDestinationType.Recent;
EventHandler _displaySettingsChangeHandler;
#endregion
/// <summary>
/// Initializes a new instance of the jump list manager
/// with the specified application id.
/// </summary>
/// <param name="appId">The application id.</param>
public JumpListManager(string appId)
{
_appId = appId;
_destinations = new JumpListDestinations();
_tasks = new JumpListTasks();
_customDestinationList = (ICustomDestinationList)new CDestinationList();
if (String.IsNullOrEmpty(_appId))
{
_appId = Windows7Taskbar.GetCurrentProcessAppId();
}
if (!String.IsNullOrEmpty(_appId))
{
_customDestinationList.SetAppID(_appId);
}
_displaySettingsChangeHandler = delegate
{
RefreshMaxSlots();
};
SystemEvents.DisplaySettingsChanged +=
_displaySettingsChangeHandler;
}
/// <summary>
/// Initializes a new instance of the jump list manager
/// with the specified window handle.
/// </summary>
/// <param name="hwnd">The window handle.</param>
public JumpListManager(IntPtr hwnd)
: this(Windows7Taskbar.GetWindowAppId(hwnd))
{
}
/// <summary>
/// Adds a task to the application's jump list.
/// </summary>
/// <param name="task">An object implementing <see cref="IJumpListTask"/>,
/// such as <see cref="ShellLink"/>.</param>
public void AddUserTask(IJumpListTask task)
{
_tasks.AddTask(task);
}
/// <summary>
/// Retrieves the tasks currently present in the application's
/// jump list. If the tasks are modified through the use of this
/// property, the <see cref="Refresh"/> method must be called to
/// repopulate the application's jump list.
/// </summary>
public IEnumerable<IJumpListTask> Tasks
{
get { return _tasks.Tasks; }
}
/// <summary>
/// Deletes the specified task from the application's jump list.
/// </summary>
/// <param name="task">The task to delete.</param>
public void DeleteTask(IJumpListTask task)
{
_tasks.DeleteTask(task);
}
/// <summary>
/// Deletes all the tasks from the application's jump list.
/// </summary>
public void ClearTasks()
{
_tasks.Clear();
}
/// <summary>
/// Adds a custom destination to the application's jump list.
/// </summary>
/// <param name="destination">An object implementing
/// <see cref="IJumpListDestination"/> such as <see cref="ShellLink"/>
/// or <see cref="ShellItem"/>.</param>
public void AddCustomDestination(IJumpListDestination destination)
{
_destinations.AddDestination(destination);
}
/// <summary>
/// Deletes the specified custom destination from the application's
/// jump list.
/// </summary>
/// <param name="destination">The destination to delete.</param>
public void DeleteCustomDestination(IJumpListDestination destination)
{
_destinations.DeleteDestination(destination);
}
/// <summary>
/// The currently enabled automatic application destination type.
/// The supported values are the values of the
/// <see cref="ApplicationDestinationType"/> enumeration.
/// Only of the values can be set at any given time.
/// </summary>
public ApplicationDestinationType EnabledAutoDestinationType
{
get
{
return _enabledAutoDestinationType;
}
set
{
if (_enabledAutoDestinationType == value)
return;
_enabledAutoDestinationType = value;
}
}
/// <summary>
/// Removes all destinations from the application's jump list.
/// </summary>
public void ClearAllDestinations()
{
ClearApplicationDestinations();
ClearCustomDestinations();
}
/// <summary>
/// Removes all application destinations (such as frequent and recent)
/// from the application's jump list.
/// </summary>
public void ClearApplicationDestinations()
{
IApplicationDestinations destinations = (IApplicationDestinations)
new CApplicationDestinations();
if (!String.IsNullOrEmpty(_appId))
{
destinations.SetAppID(_appId);
}
try
{
//This does not remove pinned items.
destinations.RemoveAllDestinations();
}
catch (System.IO.FileNotFoundException)
{//There are no destinations. That's cool.
}
}
/// <summary>
/// Retrieves all application destinations belonging to the specified
/// application destination type.
/// </summary>
/// <param name="type">The application destination type.</param>
/// <returns>A copy of the application destinations belonging to
/// the specified type; modifying the returned objects has no effect
/// on the application's destination list.</returns>
public IEnumerable<IJumpListDestination> GetApplicationDestinations(ApplicationDestinationType type)
{
if (type == ApplicationDestinationType.NONE)
throw new ArgumentException("type can't be NONE");
IApplicationDocumentLists destinations = (IApplicationDocumentLists)new CApplicationDocumentLists();
Guid iidObjectArray = typeof(IObjectArray).GUID;
object obj;
destinations.GetList((APPDOCLISTTYPE)type, 100, ref iidObjectArray, out obj);
List<IJumpListDestination> returnValue = new List<IJumpListDestination>();
Guid iidShellItem = typeof(IShellItem).GUID;
Guid iidShellLink = typeof(IShellLinkW).GUID;
IObjectArray array = (IObjectArray)obj;
uint count;
array.GetCount(out count);
for (uint i = 0; i < count; ++i)
{
try
{
array.GetAt(i, ref iidShellItem, out obj);
}
catch (Exception)//Wrong type
{
}
if (obj == null)
{
array.GetAt(i, ref iidShellLink, out obj);
//This shouldn't fail since if it's not IShellItem
//then it must be IShellLink.
IShellLinkW link = (IShellLinkW)obj;
ShellLink wrapper = new ShellLink();
StringBuilder sb = new StringBuilder(256);
link.GetPath(sb, sb.Capacity, IntPtr.Zero, 2);
wrapper.Path = sb.ToString();
link.GetArguments(sb, sb.Capacity);
wrapper.Arguments = sb.ToString();
int iconId;
link.GetIconLocation(sb, sb.Capacity, out iconId);
wrapper.IconIndex = iconId;
wrapper.IconLocation = sb.ToString();
uint showCmd;
link.GetShowCmd(out showCmd);
wrapper.ShowCommand = (WindowShowCommand)showCmd;
link.GetWorkingDirectory(sb, sb.Capacity);
wrapper.WorkingDirectory = sb.ToString();
returnValue.Add(wrapper);
}
else //It's an IShellItem.
{
IShellItem item = (IShellItem)obj;
ShellItem wrapper = new ShellItem();
string path;
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path);
wrapper.Path = path;
//Title and Category are irrelevant here, because it's
//an IShellItem. The user might want to see them, but he's
//free to go to the IShellItem and look at its property store.
returnValue.Add(wrapper);
}
}
return returnValue;
}
/// <summary>
/// Deletes the specified application destination from the application's
/// jump list.
/// </summary>
/// <param name="destination">The application destination.</param>
public void DeleteApplicationDestination(IJumpListDestination destination)
{
IApplicationDestinations destinations = (IApplicationDestinations)
new CApplicationDestinations();
if (!String.IsNullOrEmpty(_appId))
{
destinations.SetAppID(_appId);
}
destinations.RemoveDestination(destination.GetShellRepresentation());
}
/// <summary>
/// Deletes all custom destinations from the application's jump list.
/// </summary>
public void ClearCustomDestinations()
{
try
{
_customDestinationList.DeleteList(_appId);
}
catch (FileNotFoundException)
{//Means the list is empty, that's cool.
}
_destinations.Clear();
}
/// <summary>
/// Repopulates the application's jump list.
/// Use this method after all current changes to
/// the application's jump list have been introduced,
/// and you want the list to be refreshed.
/// </summary>
/// <returns><b>true</b> if the list was refreshed; <b>false</b>
/// if the operation was cancelled. The operation might have
/// been cancelled if the <see cref="UserRemovedItems"/> event
/// handler instructed us to cancel the operation.</returns>
/// <remarks>
/// If the user removed items from the jump list between the
/// last refresh operation and this one, then the
/// <see cref="UserRemovedItems"/> event will be invoked.
/// If the event handler for this event instructed us to cancel
/// the operation, then the current transaction is aborted,
/// no items are added, and this method returns <b>false</b>.
/// Check the return value to determine whether the jump list
/// needs to be changed and the operation attempted again.
/// </remarks>
public bool Refresh()
{
if (!BeginList())
return false; //Operation was cancelled
_tasks.RefreshTasks(_customDestinationList);
_destinations.RefreshDestinations(_customDestinationList);
switch (EnabledAutoDestinationType)
{
case ApplicationDestinationType.Frequent:
_customDestinationList.AppendKnownCategory(KNOWNDESTCATEGORY.KDC_FREQUENT);
break;
case ApplicationDestinationType.Recent:
_customDestinationList.AppendKnownCategory(KNOWNDESTCATEGORY.KDC_RECENT);
break;
}
CommitList();
return true;
}
/// <summary>
/// Returns the maximum number of items to be placed
/// in the application's jump list. This number depends
/// on factors such as the display resolution or monitor
/// change - do not assume it is always constant.
/// </summary>
public uint MaximumSlotsInList
{
get
{
if (_maxSlotsInList == 0)
{
RefreshMaxSlots();
}
return _maxSlotsInList;
}
}
/// <summary>
/// Cleans the resources associated with this jump list.
/// </summary>
public void Dispose()
{
SystemEvents.DisplaySettingsChanged -=
_displaySettingsChangeHandler;
if (_customDestinationList != null)
Marshal.ReleaseComObject(_customDestinationList);
}
/// <summary>
/// Register to this event to receive notifications when custom
/// destinations are being removed from your jump list by the user.
/// If you do not register to this event, you will not be able
/// to refresh the list. Additionally, if you attempt to add
/// items to the list which have been previously removed by the user,
/// the next refresh will fail to add your category.
/// </summary>
public event EventHandler<UserRemovedItemsEventArgs> UserRemovedItems;
#region Implementation
private void RefreshMaxSlots()
{
object obj;
_customDestinationList.BeginList(out _maxSlotsInList, ref SafeNativeMethods.IID_IObjectArray, out obj);
_customDestinationList.AbortList();
}
private bool BeginList()
{
if (UserRemovedItems == null)
{
throw new InvalidOperationException("You must register for the JumpListManager.UserRemovedItems event before adding any items");
}
object obj;
_customDestinationList.BeginList(out _maxSlotsInList, ref SafeNativeMethods.IID_IObjectArray, out obj);
IObjectArray removedItems = (IObjectArray)obj;
uint count;
removedItems.GetCount(out count);
if (count == 0)
return true;
string[] removedItemsArr = new string[count];
for (uint i = 0; i < count; ++i)
{
object item;
removedItems.GetAt(i, ref SafeNativeMethods.IID_IUnknown, out item);
try
{
IShellLinkW shellLink = (IShellLinkW)item;
if (shellLink != null)
{
StringBuilder sb = new StringBuilder(256);
shellLink.GetPath(sb, sb.Capacity, IntPtr.Zero, 2);
removedItemsArr[i] = sb.ToString();
}
continue;
}
catch (InvalidCastException) //It's not a ShellLink
{ }
try
{
IShellItem shellItem = (IShellItem)item;
if (shellItem != null)
{
string path;
shellItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path);
removedItemsArr[i] = path;
}
}
catch (InvalidCastException)
{
//It's neither a shell link nor a shell item.
//This is impossible.
Debug.Assert(false,
"List of removed items contains something that is neither a shell item nor a shell link");
}
}
UserRemovedItemsEventArgs args = new UserRemovedItemsEventArgs(removedItemsArr);
UserRemovedItems(this, args);
if (args.CancelCurrentOperation)
{
_customDestinationList.AbortList();
}
return !args.CancelCurrentOperation;
}
private void CommitList()
{
_customDestinationList.CommitList();
}
#endregion
}
/// <summary>
/// The application destination type.
/// </summary>
public enum ApplicationDestinationType
{
/// <summary>
/// No application destination type is selected.
/// </summary>
NONE = -1,
/// <summary>
/// Destinations used recently.
/// </summary>
Recent = 0,
/// <summary>
/// Destinations used frequently.
/// </summary>
Frequent
}
/// <summary>
/// The event arguments for the event that occurs
/// when the user removes items from the application's
/// jump list.
/// </summary>
public class UserRemovedItemsEventArgs : EventArgs
{
readonly string[] _removedItems;
internal UserRemovedItemsEventArgs(string[] removedItems)
{
_removedItems = removedItems;
}
/// <summary>
/// The collection of removed items. Each item is the path.
/// </summary>
public string[] RemovedItems
{
get
{
return _removedItems;
}
}
/// <summary>
/// Set to <b>true</b> if the current operation
/// should be cancelled. Should be used by the application
/// if because of the items the user has removed
/// there is no real work to do with the jump list.
/// </summary>
public bool CancelCurrentOperation { get; set; }
}
}
@@ -0,0 +1,119 @@
using System;
using System.Drawing;
using TaskbarLib.Interop;
namespace TaskbarLib.DesktopIntegration
{
/// <summary>
/// Represents a taskbar thumbnail button in the thumbnail toolbar.
/// </summary>
public sealed class ThumbButton
{
private ThumbButtonManager _manager;
internal ThumbButton(ThumbButtonManager manager, uint id, Icon icon, string tooltip)
{
_manager = manager;
Id = id;
Icon = icon;
Tooltip = tooltip;
}
/// <summary>
/// The event that occurs when the taskbar thumbnail button
/// is clicked.
/// </summary>
public event EventHandler Clicked;
/// <summary>
/// Gets or sets thumbnail button's id.
/// </summary>
public uint Id { get; set; }
/// <summary>
/// Gets or sets the thumbnail button's icon.
/// </summary>
public Icon Icon { get; set; }
/// <summary>
/// Gets or sets the thumbnail button's tooltip.
/// </summary>
public string Tooltip { get; set; }
internal THBFLAGS Flags { get; set; }
internal THUMBBUTTON Win32ThumbButton
{
get
{
THUMBBUTTON win32ThumbButton = new THUMBBUTTON();
win32ThumbButton.iId = Id;
win32ThumbButton.szTip = Tooltip;
win32ThumbButton.hIcon = Icon.Handle;
win32ThumbButton.dwFlags = Flags;
win32ThumbButton.dwMask = THBMASK.THB_FLAGS;
if (Tooltip != null)
win32ThumbButton.dwMask |= THBMASK.THB_TOOLTIP;
if (Icon != null)
win32ThumbButton.dwMask |= THBMASK.THB_ICON;
return win32ThumbButton;
}
}
/// <summary>
/// Gets or sets the thumbnail button's visibility.
/// </summary>
public bool Visible
{
get
{
return (this.Flags & THBFLAGS.THBF_HIDDEN) == 0;
}
set
{
if (value)
{
this.Flags &= ~(THBFLAGS.THBF_HIDDEN);
}
else
{
this.Flags |= THBFLAGS.THBF_HIDDEN;
}
_manager.RefreshThumbButtons();
}
}
/// <summary>
/// Gets or sets the thumbnail button's enabled state.
/// </summary>
public bool Enabled
{
get
{
return (this.Flags & THBFLAGS.THBF_DISABLED) == 0;
}
set
{
if (value)
{
this.Flags &= ~(THBFLAGS.THBF_DISABLED);
}
else
{
this.Flags |= THBFLAGS.THBF_DISABLED;
}
_manager.RefreshThumbButtons();
}
}
internal void FireClick()
{
if (Clicked != null)
Clicked(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using TaskbarLib.Interop;
using System.Collections;
namespace TaskbarLib.DesktopIntegration
{
/// <summary>
/// Manages a set of taskbar thumbnail buttons in an application.
/// </summary>
public sealed class ThumbButtonManager
{
/// <summary>
/// Initializes a new manager with the specified window handle.
/// </summary>
/// <param name="hwnd">The window handle.</param>
public ThumbButtonManager(IntPtr hwnd)
{
_hwnd = hwnd;
}
/// <summary>
/// Creates a new taskbar thumbnail button.
/// </summary>
/// <param name="id">The button's id.</param>
/// <param name="icon">The button's icon.</param>
/// <param name="tooltip">The button's tooltip.</param>
/// <returns>An object of type <see cref="ThumbButton"/>
/// representing the newly created thumbnail button.</returns>
public ThumbButton CreateThumbButton(uint id, Icon icon, string tooltip)
{
return new ThumbButton(this, id, icon, tooltip);
}
/// <summary>
/// Dispatches a Windows message to the thumbnail button event
/// handlers if appropriate.
/// </summary>
/// <remarks>
/// This method is intended for infrastructure use only.
/// </remarks>
/// <param name="message">The message.</param>
public void DispatchMessage(ref Message message)
{
UInt64 wparam = (UInt64)message.WParam.ToInt64();
UInt32 wparam32 = (UInt32) (wparam & 0xffffffff); //Clear top 32 bits
if (message.Msg == SafeNativeMethods.WM_COMMAND && (wparam32 >> 16 == SafeNativeMethods.THBN_CLICKED))
{
uint id = wparam32 & 0xffff; //Bottom 16 bits
_thumbButtons[id].FireClick();
}
}
/// <summary>
/// Adds the specified taskbar thumbnail buttons to the application's
/// thumbnail toolbar.
/// </summary>
/// <remarks>
/// Thumbnail buttons can only be added once - after being added,
/// they cannot be removed or deleted. However, they can be shown,
/// hidden, enabled and disabled.
/// </remarks>
/// <param name="buttons">The buttons to add.</param>
public void AddThumbButtons(params ThumbButton[] buttons)
{
Array.ForEach(buttons, b => _thumbButtons.Add(b.Id, b));
RefreshThumbButtons();
}
/// <summary>
/// Gets a specific thumbnail button by its id.
/// </summary>
/// <param name="id">The thumbnail button's id.</param>
/// <returns>An object of type <see cref="ThumbButton"/>
/// with the specified id.</returns>
public ThumbButton this[uint id]
{
get
{
return _thumbButtons[id];
}
}
#region Implementation
private bool _buttonsLoaded;
internal void RefreshThumbButtons()
{
THUMBBUTTON[] win32Buttons = (from thumbButton in _thumbButtons.Values select thumbButton.Win32ThumbButton).ToArray();
if (_buttonsLoaded)
{
Windows7Taskbar.TaskbarList.ThumbBarUpdateButtons(_hwnd, (uint)win32Buttons.Length, win32Buttons);
}
else //First time
{
Windows7Taskbar.TaskbarList.ThumbBarAddButtons(_hwnd, (uint)win32Buttons.Length, win32Buttons);
_buttonsLoaded = true;
}
}
private Dictionary<uint, ThumbButton> _thumbButtons = new Dictionary<uint, ThumbButton>();
private IntPtr _hwnd;
#endregion
}
}
@@ -0,0 +1,316 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using TaskbarLib.Interop;
namespace TaskbarLib.DesktopIntegration
{
/// <summary>
/// The primary coordinator of the Windows 7 taskbar-related activities.
/// For additional functionality, see the JumpListManager
/// and ThumbButtonManager classes.
/// </summary>
public static class Windows7Taskbar
{
#region Infrastructure
/// <summary>
/// The WM_TaskbarButtonCreated message number.
/// </summary>
public static uint TaskbarButtonCreatedMessage
{
get { return UnsafeNativeMethods.WM_TaskbarButtonCreated; }
}
// Use thread static to work around COM threading issues.
[ThreadStatic]
private static ITaskbarList3 _taskbarList;
internal static ITaskbarList3 TaskbarList
{
get
{
if (_taskbarList == null)
{
_taskbarList = (ITaskbarList3)new CTaskbarList();
_taskbarList.HrInit();
}
return _taskbarList;
}
}
private static IPropertyStore InternalGetWindowPropertyStore(IntPtr hwnd)
{
IPropertyStore propStore;
int rc = UnsafeNativeMethods.SHGetPropertyStoreForWindow(hwnd, ref SafeNativeMethods.IID_IPropertyStore, out propStore);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
return propStore;
}
private static void InternalEnableCustomWindowPreview(IntPtr hwnd, bool enable)
{
int t = enable ? 1 : 0;
int rc;
rc = UnsafeNativeMethods.DwmSetWindowAttribute(hwnd, SafeNativeMethods.DWMWA_HAS_ICONIC_BITMAP, ref t, 4);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
rc = UnsafeNativeMethods.DwmSetWindowAttribute(hwnd, SafeNativeMethods.DWMWA_FORCE_ICONIC_REPRESENTATION, ref t, 4);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
}
#endregion
#region Application Id
/// <summary>
/// Gets a window's application id by its window handle.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <returns>The application id of that window.</returns>
public static string GetWindowAppId(IntPtr hwnd)
{
IPropertyStore propStore = InternalGetWindowPropertyStore(hwnd);
PropVariant pv;
propStore.GetValue(ref PropertyKey.PKEY_AppUserModel_ID, out pv);
Marshal.ReleaseComObject(propStore);
return pv.GetValue();
}
/// <summary>
/// Sets the window's application id by its window handle.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="appId">The application id.</param>
public static void SetWindowAppId(IntPtr hwnd, string appId)
{
IPropertyStore propStore = InternalGetWindowPropertyStore(hwnd);
PropVariant pv = new PropVariant();
pv.SetValue(appId);
propStore.SetValue(ref PropertyKey.PKEY_AppUserModel_ID, ref pv);
Marshal.ReleaseComObject(propStore);
}
/// <summary>
/// Sets the current process' explicit application user model id.
/// </summary>
/// <param name="appId">The application id.</param>
public static void SetCurrentProcessAppId(string appId)
{
UnsafeNativeMethods.SetCurrentProcessExplicitAppUserModelID(appId);
}
/// <summary>
/// Gets the current process' explicit application user model id.
/// </summary>
/// <returns>The application id.</returns>
public static string GetCurrentProcessAppId()
{
string appId;
UnsafeNativeMethods.GetCurrentProcessExplicitAppUserModelID(out appId);
return appId;
}
#endregion
#region DWM Iconic Thumbnail and Peek Bitmap
/// <summary>
/// Indicates that the specified window requests the DWM
/// to demand live preview (thumbnail and peek) mode when necessary
/// instead of relying on default preview.
/// </summary>
/// <param name="hwnd">The window handle.</param>
public static void EnableCustomWindowPreview(IntPtr hwnd)
{
InternalEnableCustomWindowPreview(hwnd, true);
}
/// <summary>
/// Indicates that the specified window does not require the DWM
/// to demand live preview (thumbnail and peek) mode when necessary,
/// i.e. this window relies on default preview.
/// </summary>
/// <param name="hwnd">The window handle.</param>
public static void DisableCustomWindowPreview(IntPtr hwnd)
{
InternalEnableCustomWindowPreview(hwnd, false);
}
/// <summary>
/// Sets the specified iconic thumbnail for the specified window.
/// This is typically done in response to a DWM message.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="bitmap">The thumbnail bitmap.</param>
public static void SetIconicThumbnail(IntPtr hwnd, Bitmap bitmap)
{
int rc = UnsafeNativeMethods.DwmSetIconicThumbnail(hwnd, bitmap.GetHbitmap(), SafeNativeMethods.DWM_SIT_DISPLAYFRAME);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
}
/// <summary>
/// Sets the specified peek (live preview) bitmap for the specified
/// window. This is typically done in response to a DWM message.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="bitmap">The thumbnail bitmap.</param>
/// <param name="displayFrame">Whether to display a standard window
/// frame around the bitmap.</param>
public static void SetPeekBitmap(IntPtr hwnd, Bitmap bitmap, bool displayFrame)
{
int rc = UnsafeNativeMethods.DwmSetIconicLivePreviewBitmap(hwnd, bitmap.GetHbitmap(), IntPtr.Zero, displayFrame ? SafeNativeMethods.DWM_SIT_DISPLAYFRAME : (uint)0);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
}
/// <summary>
/// Sets the specified peek (live preview) bitmap for the specified
/// window. This is typically done in response to a DWM message.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="bitmap">The thumbnail bitmap.</param>
/// <param name="offset">The client area offset at which to display
/// the specified bitmap. The rest of the parent window will be
/// displayed as "remembered" by the DWM.</param>
/// <param name="displayFrame">Whether to display a standard window
/// frame around the bitmap.</param>
public static void SetPeekBitmap(IntPtr hwnd, Bitmap bitmap, Point offset, bool displayFrame)
{
var nativePoint = new POINT(offset.X, offset.Y);
int rc = UnsafeNativeMethods.DwmSetIconicLivePreviewBitmap(hwnd, bitmap.GetHbitmap(), ref nativePoint, displayFrame ? SafeNativeMethods.DWM_SIT_DISPLAYFRAME : (uint)0);
if (rc != 0)
throw Marshal.GetExceptionForHR(rc);
}
#endregion
#region Taskbar Overlay Icon
/// <summary>
/// Draws the specified overlay icon on the specified window's
/// taskbar button.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="icon">The overlay icon.</param>
/// <param name="description">The overlay icon description.</param>
public static void SetTaskbarOverlayIcon(IntPtr hwnd, Icon icon, string description)
{
TaskbarList.SetOverlayIcon(
hwnd,
icon == null ? IntPtr.Zero : icon.Handle,
description);
}
#endregion
#region Taskbar Progress Bar
/// <summary>
/// Represents the thumbnail progress bar state.
/// </summary>
public enum ThumbnailProgressState
{
/// <summary>
/// No progress is displayed.
/// </summary>
NoProgress = 0,
/// <summary>
/// The progress is indeterminate (marquee).
/// </summary>
Indeterminate = 0x1,
/// <summary>
/// Normal progress is displayed.
/// </summary>
Normal = 0x2,
/// <summary>
/// An error occurred (red).
/// </summary>
Error = 0x4,
/// <summary>
/// The operation is paused (yellow).
/// </summary>
Paused = 0x8
}
/// <summary>
/// Sets the progress state of the specified window's
/// taskbar button.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="state">The progress state.</param>
public static void SetProgressState(IntPtr hwnd, ThumbnailProgressState state)
{
TaskbarList.SetProgressState(hwnd, (TBPFLAG)state);
}
/// <summary>
/// Sets the progress value of the specified window's
/// taskbar button.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="current">The current value.</param>
/// <param name="maximum">The maximum value.</param>
public static void SetProgressValue(IntPtr hwnd, ulong current, ulong maximum)
{
TaskbarList.SetProgressValue(hwnd, current, maximum);
}
#endregion
#region Taskbar Thumbnails
/// <summary>
/// Specifies that only a portion of the window's client area
/// should be used in the window's thumbnail.
/// </summary>
/// <param name="hwnd">The window.</param>
/// <param name="clipRect">The rectangle that specifies the clipped region.</param>
public static void SetThumbnailClip(IntPtr hwnd, Rectangle clipRect)
{
RECT rect = new RECT(clipRect.Left, clipRect.Top, clipRect.Right, clipRect.Bottom);
TaskbarList.SetThumbnailClip(hwnd, ref rect);
}
/// <summary>
/// Sets the specified window's thumbnail tooltip.
/// </summary>
/// <param name="hwnd">The window.</param>
/// <param name="tooltip">The tooltip text.</param>
public static void SetThumbnailTooltip(IntPtr hwnd, string tooltip)
{
TaskbarList.SetThumbnailTooltip(hwnd, tooltip);
}
#endregion
#region Miscellaneous
/// <summary>
/// Specifies that the taskbar- and DWM-related windows messages should
/// pass through the Windows UIPI mechanism even if the process is
/// running elevated. Calling this method is not required unless the
/// process is running elevated.
/// </summary>
public static void AllowTaskbarWindowMessagesThroughUipi()
{
UnsafeNativeMethods.ChangeWindowMessageFilter(UnsafeNativeMethods.WM_TaskbarButtonCreated, SafeNativeMethods.MSGFLT_ADD);
UnsafeNativeMethods.ChangeWindowMessageFilter(SafeNativeMethods.WM_DWMSENDICONICTHUMBNAIL, SafeNativeMethods.MSGFLT_ADD);
UnsafeNativeMethods.ChangeWindowMessageFilter(SafeNativeMethods.WM_DWMSENDICONICLIVEPREVIEWBITMAP, SafeNativeMethods.MSGFLT_ADD);
UnsafeNativeMethods.ChangeWindowMessageFilter(SafeNativeMethods.WM_COMMAND, SafeNativeMethods.MSGFLT_ADD);
UnsafeNativeMethods.ChangeWindowMessageFilter(SafeNativeMethods.WM_SYSCOMMAND, SafeNativeMethods.MSGFLT_ADD);
UnsafeNativeMethods.ChangeWindowMessageFilter(SafeNativeMethods.WM_ACTIVATE, SafeNativeMethods.MSGFLT_ADD);
}
#endregion
}
}
@@ -0,0 +1,189 @@
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace TaskbarLib.DesktopIntegration.WindowsForms
{
/// <summary>
/// Contains extension methods for easier Windows Forms interop.
/// </summary>
public static class WindowsFormsExtensions
{
/// <summary>
/// Sets this form's application id.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="appId">The application id.</param>
public static void SetAppId(this Form form, string appId)
{
Windows7Taskbar.SetWindowAppId(form.Handle, appId);
}
/// <summary>
/// Gets this form's application id.
/// </summary>
/// <param name="form">The form.</param>
/// <returns>The form's application id.</returns>
public static string GetAppId(this Form form)
{
return Windows7Taskbar.GetWindowAppId(form.Handle);
}
/// <summary>
/// Enables custom window preview on this form, meaning that
/// the DWM will send messages when a live thumbnail preview
/// or a live peek will be necessary.
/// </summary>
/// <param name="form">The form.</param>
public static void EnableCustomWindowPreview(this Form form)
{
Windows7Taskbar.EnableCustomWindowPreview(form.Handle);
}
/// <summary>
/// Disables custom window preview on this form, meaning that
/// the DWM will not send messages when a live thumbnail preview
/// or a live peek will be necessary, but instead rely on its
/// default rendering.
/// </summary>
/// <param name="form">The form.</param>
public static void DisableCustomWindowPreview(this Form form)
{
Windows7Taskbar.DisableCustomWindowPreview(form.Handle);
}
/// <summary>
/// Sets this form's iconic thumbnail to the specified bitmap.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="bitmap">The bitmap.</param>
public static void SetIconicThumbnail(this Form form, Bitmap bitmap)
{
Windows7Taskbar.SetIconicThumbnail(form.Handle, bitmap);
}
/// <summary>
/// Sets this form's peek (live preview) bitmap.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="bitmap">The bitmap.</param>
/// <param name="displayFrame">Whether to display a standard window
/// frame around the bitmap.</param>
public static void SetPeekBitmap(this Form form, Bitmap bitmap, bool displayFrame)
{
Windows7Taskbar.SetPeekBitmap(form.Handle, bitmap, displayFrame);
}
/// <summary>
/// Draws the specified overlay icon over this form's taskbar button.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="icon">The overlay icon.</param>
/// <param name="description">The overlay icon's description.</param>
public static void SetTaskbarOverlayIcon(this Form form, Icon icon, string description)
{
Windows7Taskbar.SetTaskbarOverlayIcon(form.Handle, icon, description);
}
/// <summary>
/// Sets the progress bar in the containing form's taskbar button
/// to this progress bar's progress.
/// </summary>
/// <param name="progressBar">The progress bar.</param>
public static void SetTaskbarProgress(ProgressBar progressBar)
{
Form form = (Form)progressBar.TopLevelControl;
//Approximation:
ulong maximum = (ulong)(progressBar.Maximum - progressBar.Minimum);
ulong progress = (ulong)(progressBar.Value - progressBar.Minimum);
Windows7Taskbar.SetProgressState(form.Handle, Windows7Taskbar.ThumbnailProgressState.Normal);
Windows7Taskbar.SetProgressValue(form.Handle, progress, maximum);
}
/// <summary>
/// Sets the progress bar in the containing form's taskbar button
/// to this toolstrip progress bar's progress.
/// </summary>
/// <param name="progressBar">The progress bar.</param>
public static void SetTaskbarProgress(ToolStripProgressBar progressBar)
{
Form form = (Form)progressBar.Control.TopLevelControl;
//Approximation:
ulong maximum = (ulong)(progressBar.Maximum - progressBar.Minimum);
ulong progress = (ulong)(progressBar.Value - progressBar.Minimum);
Windows7Taskbar.SetProgressState(form.Handle, Windows7Taskbar.ThumbnailProgressState.Normal);
Windows7Taskbar.SetProgressValue(form.Handle, progress, maximum);
}
/// <summary>
/// Sets the progress bar of this form's taskbar button to the
/// specified percentage.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="percent">The progress percentage.</param>
public static void SetTaskbarProgress(this Form form, float percent)
{
Windows7Taskbar.SetProgressState(form.Handle, Windows7Taskbar.ThumbnailProgressState.Normal);
Windows7Taskbar.SetProgressValue(form.Handle, (ulong)percent, 100);
}
/// <summary>
/// Sets the progress bar of this form's taskbar button to the
/// specified state.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="state">The taskbar progress state.</param>
public static void SetTaskbarProgressState(this Form form, Windows7Taskbar.ThumbnailProgressState state)
{
Windows7Taskbar.SetProgressState(form.Handle, state);
}
/// <summary>
/// Creates a taskbar thumbnail button manager for this form.
/// </summary>
/// <param name="form">The form.</param>
/// <returns>An object of type <see cref="ThumbButtonManager"/>
/// that can be used to add and manage thumbnail toolbar buttons.</returns>
public static ThumbButtonManager CreateThumbButtonManager(this Form form)
{
return new ThumbButtonManager(form.Handle);
}
/// <summary>
/// Creates a jump list manager for this form.
/// </summary>
/// <param name="form">The form.</param>
/// <returns>An object of type <see cref="JumpListManager"/>
/// that can be used to manage the application's jump list.</returns>
public static JumpListManager CreateJumpListManager(this Form form)
{
return new JumpListManager(form.Handle);
}
/// <summary>
/// Specifies that only a portion of the form's client area
/// should be used in the form's thumbnail.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="clipRect">The rectangle that specifies the clipped region.</param>
public static void SetThumbnailClip(this Form form, Rectangle clipRect)
{
Windows7Taskbar.SetThumbnailClip(form.Handle, clipRect);
}
/// <summary>
/// Sets the specified form's thumbnail tooltip.
/// </summary>
/// <param name="form">The form.</param>
/// <param name="tooltip">The tooltip text.</param>
public static void SetThumbnailTooltip(this Form form, string tooltip)
{
Windows7Taskbar.SetThumbnailTooltip(form.Handle, tooltip);
}
}
}
+8
View File
@@ -416,6 +416,14 @@
<Compile Include="Components\TargetWindowButton.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Components\TaskbarLib\Interop\COMTypes.cs" />
<Compile Include="Components\TaskbarLib\Interop\Interop.cs" />
<Compile Include="Components\TaskbarLib\JumpLists\JumpListImpl.cs" />
<Compile Include="Components\TaskbarLib\JumpLists\JumpListManager.cs" />
<Compile Include="Components\TaskbarLib\ThumbnailButtons\ThumbButton.cs" />
<Compile Include="Components\TaskbarLib\ThumbnailButtons\ThumbButtonManager.cs" />
<Compile Include="Components\TaskbarLib\Windows7Taskbar.cs" />
<Compile Include="Components\TaskbarLib\WindowsFormsExtensions.cs" />
<Compile Include="Components\TimerProperties.cs">
<SubType>UserControl</SubType>
</Compile>