mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
908610277d
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@1991 21ef857c-d57f-4fe0-8362-d861dc6d29cd
458 lines
16 KiB
C#
458 lines
16 KiB
C#
/*
|
|
* Process Hacker -
|
|
* ProcessHacker Taskbar Extensions
|
|
*
|
|
* Copyright (C) 2009 dmex
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using TaskbarLib.Interop;
|
|
using System.Runtime.InteropServices;
|
|
using ProcessHacker.Native.Api;
|
|
|
|
namespace TaskbarLib
|
|
{
|
|
/// <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)
|
|
{
|
|
HResult addObjectResult = categoryContents.AddObject(destination.GetShellRepresentation());
|
|
addObjectResult.ThrowIf();
|
|
}
|
|
|
|
HResult appendCategoryResult = destinationList.AppendCategory(
|
|
destinations.First().Category, (IObjectArray)categoryContents);
|
|
appendCategoryResult.ThrowIf();
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
HResult addObjectResult = taskCollection.AddObject(task.GetShellRepresentation());
|
|
addObjectResult.ThrowIf();
|
|
}
|
|
HResult addUserTasksResult = destinationList.AddUserTasks((IObjectArray)taskCollection);
|
|
addUserTasksResult.ThrowIf();
|
|
}
|
|
|
|
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);
|
|
|
|
HResult setValueResult = propertyStore.SetValue(ref PropertyKey.PKEY_AppUserModel_IsDestListSeparator, ref propVariant);
|
|
setValueResult.ThrowIf();
|
|
|
|
propVariant.Clear();
|
|
}
|
|
else
|
|
{
|
|
HResult setPathResult = shellLink.SetPath(Path);
|
|
setPathResult.ThrowIf();
|
|
|
|
if (!String.IsNullOrEmpty(IconLocation))
|
|
{
|
|
HResult setIconLocationResult = shellLink.SetIconLocation(IconLocation, IconIndex);
|
|
setIconLocationResult.ThrowIf();
|
|
}
|
|
if (!String.IsNullOrEmpty(Arguments))
|
|
{
|
|
HResult setArgumentsResult = shellLink.SetArguments(Arguments);
|
|
setArgumentsResult.ThrowIf();
|
|
}
|
|
if (!String.IsNullOrEmpty(WorkingDirectory))
|
|
{
|
|
HResult setWorkingDirectoryResult = shellLink.SetWorkingDirectory(WorkingDirectory);
|
|
setWorkingDirectoryResult.ThrowIf();
|
|
}
|
|
|
|
HResult setShowCmdResult = shellLink.SetShowCmd((uint)ShowCommand);
|
|
setShowCmdResult.ThrowIf();
|
|
|
|
propVariant.SetValue(Title);
|
|
|
|
HResult setValueResult = propertyStore.SetValue(ref PropertyKey.PKEY_Title, ref propVariant);
|
|
setValueResult.ThrowIf();
|
|
|
|
propVariant.Clear();
|
|
}
|
|
|
|
HResult commitResult = propertyStore.Commit();
|
|
commitResult.ThrowIf();
|
|
|
|
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 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("43826D1E-E718-42EE-BC55-A1E261C37BFE");
|
|
uint result = UnsafeNativeMethods.SHCreateItemFromParsingName(
|
|
path,
|
|
IntPtr.Zero,
|
|
ref shellItemGuid,
|
|
out resultItem);
|
|
//// Throw if an error occurred.
|
|
Marshal.ThrowExceptionForHR((int)result);
|
|
return resultItem;
|
|
}
|
|
}
|
|
} |