Mercurial > shortcutkeyfinder
changeset 0:209d9210c18f default tip
It works.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 25 Jun 2016 13:42:54 +1000 |
parents | |
children | |
files | .hgignore ShortcutKeyFinder.sln ShortcutKeyFinder/App.config ShortcutKeyFinder/Hotkey.cs ShortcutKeyFinder/Images/keyboard.ico ShortcutKeyFinder/MainForm.Designer.cs ShortcutKeyFinder/MainForm.cs ShortcutKeyFinder/MainForm.resx ShortcutKeyFinder/Program.cs ShortcutKeyFinder/Properties/AssemblyInfo.cs ShortcutKeyFinder/Properties/Resources.Designer.cs ShortcutKeyFinder/Properties/Resources.resx ShortcutKeyFinder/Properties/Settings.Designer.cs ShortcutKeyFinder/Properties/Settings.settings ShortcutKeyFinder/Shortcut.cs ShortcutKeyFinder/ShortcutKeyFinder.cs ShortcutKeyFinder/ShortcutKeyFinder.csproj ShortcutKeyFinder/ShortcutKeyFinder.csproj.user ShortcutKeyFinder/Win32Helpers.cs ShortcutKeyFinder/keyboard.ico |
diffstat | 20 files changed, 1948 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,4 @@ +syntax: glob +bin +obj +*.suo \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder.sln Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutKeyFinder", "ShortcutKeyFinder\ShortcutKeyFinder.csproj", "{0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/App.config Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <startup> + + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /></startup> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Hotkey.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Windows.Forms; + +namespace ShortcutKeyFinder +{ + /// <summary>Provides a means to encode and decode hotkeys in the format expected by the Win32 API</summary> + class Hotkey + { + private List<Keys> _modifierKeys = new List<Keys>() { Keys.ShiftKey, Keys.ControlKey, Keys.Menu }; + // Windows API values + public ushort HOTKEYF_SHIFT = 0x01 << 8; + public ushort HOTKEYF_CONTROL = 0x02 << 8; + public ushort HOTKEYF_ALT = 0x04 << 8; + public ushort HOTKEYF_EXT = 0x08 << 8; + + /// <summary>Raw hotkey value to use in Win32 functions</summary> + public ushort RawHotkey { get; set; } + + /// <summary>Shift key state</summary> + public bool Shift + { + get { return (RawHotkey & HOTKEYF_SHIFT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_SHIFT; else RawHotkey &= (ushort)~HOTKEYF_SHIFT; } + } + + /// <summary>Control key state</summary> + public bool Control + { + get { return (RawHotkey & HOTKEYF_CONTROL) != 0; } + set { if (value) RawHotkey |= HOTKEYF_CONTROL; else RawHotkey &= (ushort)~HOTKEYF_CONTROL; } + } + + /// <summary>Alt key state</summary> + public bool Alt + { + get { return (RawHotkey & HOTKEYF_ALT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_ALT; else RawHotkey &= (ushort)~HOTKEYF_ALT; } + } + + /// <summary>Ext key state</summary> + /// <remarks>Probably useless nowadays</remarks> + public bool Ext + { + get { return (RawHotkey & HOTKEYF_EXT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_EXT; else RawHotkey &= (ushort)~HOTKEYF_EXT; } + } + + /// <summary>The key code portion of the hotkey data</summary> + /// <remarks>The key code is stored in the lower byte of the hotkey</remarks> + public byte KeyCode + { + get { return (byte)(RawHotkey & 0x00FF); } + set { RawHotkey = (ushort)((RawHotkey & 0xFF00) | value); } + } + + /// <summary>Default constructor</summary> + public Hotkey() { } + + /// <summary>Constructor with hotkey initializer</summary> + /// <param name="hotkey">Hotkey value for Win32 API calls</param> + public Hotkey(ushort hotkey) + { + RawHotkey = hotkey; + } + + /// <summary>Converts the hotkey data into a friendly string</summary> + public override string ToString() + { + return string.Format("{0}{1}{2}{3}{4}", + Control ? "Ctrl+" : "", + Alt ? "Alt+" : "", + Shift ? "Shift+" : "", + Ext ? "Ext+" : "", + _modifierKeys.Contains((Keys)KeyCode) ? "" : new KeysConverter().ConvertToString((int)KeyCode)); + } + + /// <summary>Checks if two Hotkey objects have the same key combination</summary> + public override bool Equals(object obj) + { + Hotkey hotkey = obj as Hotkey; + return hotkey != null && hotkey.RawHotkey == RawHotkey; + } + + public override int GetHashCode() + { + return RawHotkey.GetHashCode(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/MainForm.Designer.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,226 @@ +namespace ShortcutKeyFinder +{ + partial class MainForm + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.ShortcutGrid = new System.Windows.Forms.DataGridView(); + this.ShortcutColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.HotkeyColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.LocationColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.FindShortcutWorker = new System.ComponentModel.BackgroundWorker(); + this.panel1 = new System.Windows.Forms.Panel(); + this.ClearButton = new System.Windows.Forms.Button(); + this.ClearAllButton = new System.Windows.Forms.Button(); + this.ElevateButton = new System.Windows.Forms.Button(); + this.ShowAllCheckBox = new System.Windows.Forms.CheckBox(); + this.InitialProgressBar = new System.Windows.Forms.ProgressBar(); + this.MainFormToolTip = new System.Windows.Forms.ToolTip(this.components); + ((System.ComponentModel.ISupportInitialize)(this.ShortcutGrid)).BeginInit(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // ShortcutGrid + // + this.ShortcutGrid.AllowUserToAddRows = false; + this.ShortcutGrid.AllowUserToDeleteRows = false; + this.ShortcutGrid.AllowUserToResizeRows = false; + this.ShortcutGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ShortcutGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; + this.ShortcutGrid.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText; + this.ShortcutGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.ShortcutGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.ShortcutColumn, + this.HotkeyColumn, + this.LocationColumn}); + this.ShortcutGrid.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnF2; + this.ShortcutGrid.Location = new System.Drawing.Point(12, 54); + this.ShortcutGrid.Name = "ShortcutGrid"; + this.ShortcutGrid.RowHeadersVisible = false; + this.ShortcutGrid.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.ShortcutGrid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.ShortcutGrid.Size = new System.Drawing.Size(912, 619); + this.ShortcutGrid.StandardTab = true; + this.ShortcutGrid.TabIndex = 0; + this.ShortcutGrid.Visible = false; + this.ShortcutGrid.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.ShortcutGrid_CellBeginEdit); + this.ShortcutGrid.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.ShortcutGrid_CellDoubleClick); + this.ShortcutGrid.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.ShortcutGrid_CellEndEdit); + this.ShortcutGrid.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.ShortcutGrid_CellFormatting); + this.ShortcutGrid.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.ShortcutGrid_CellPainting); + this.ShortcutGrid.EditingControlShowing += new System.Windows.Forms.DataGridViewEditingControlShowingEventHandler(this.ShortcutGrid_EditingControlShowing); + this.ShortcutGrid.SelectionChanged += new System.EventHandler(this.ShortcutGrid_SelectionChanged); + this.ShortcutGrid.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ShortcutGrid_KeyDown); + // + // ShortcutColumn + // + this.ShortcutColumn.DataPropertyName = "Name"; + this.ShortcutColumn.HeaderText = "Program"; + this.ShortcutColumn.Name = "ShortcutColumn"; + this.ShortcutColumn.ReadOnly = true; + // + // HotkeyColumn + // + this.HotkeyColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.HotkeyColumn.DataPropertyName = "Hotkey"; + this.HotkeyColumn.HeaderText = "Shortcut key"; + this.HotkeyColumn.Name = "HotkeyColumn"; + this.HotkeyColumn.Width = 92; + // + // LocationColumn + // + this.LocationColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.LocationColumn.DataPropertyName = "DisplayLocation"; + this.LocationColumn.HeaderText = "Location"; + this.LocationColumn.Name = "LocationColumn"; + this.LocationColumn.ReadOnly = true; + this.LocationColumn.Width = 73; + // + // FindShortcutWorker + // + this.FindShortcutWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.FindShortcutWorker_DoWork); + this.FindShortcutWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.FindShortcutWorker_RunWorkerCompleted); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.Controls.Add(this.ClearButton); + this.panel1.Controls.Add(this.ClearAllButton); + this.panel1.Controls.Add(this.ElevateButton); + this.panel1.Controls.Add(this.ShowAllCheckBox); + this.panel1.Location = new System.Drawing.Point(12, 12); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(912, 36); + this.panel1.TabIndex = 1; + // + // ClearButton + // + this.ClearButton.Enabled = false; + this.ClearButton.Location = new System.Drawing.Point(180, 3); + this.ClearButton.Name = "ClearButton"; + this.ClearButton.Size = new System.Drawing.Size(140, 30); + this.ClearButton.TabIndex = 2; + this.ClearButton.Text = "&Clear shortcut key"; + this.ClearButton.UseVisualStyleBackColor = true; + this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click); + // + // ClearAllButton + // + this.ClearAllButton.Enabled = false; + this.ClearAllButton.Location = new System.Drawing.Point(340, 3); + this.ClearAllButton.Name = "ClearAllButton"; + this.ClearAllButton.Size = new System.Drawing.Size(140, 30); + this.ClearAllButton.TabIndex = 3; + this.ClearAllButton.Text = "C&lear all shortcut keys"; + this.ClearAllButton.UseVisualStyleBackColor = true; + this.ClearAllButton.Click += new System.EventHandler(this.ClearAllButton_Click); + // + // ElevateButton + // + this.ElevateButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.ElevateButton.Location = new System.Drawing.Point(665, 3); + this.ElevateButton.Name = "ElevateButton"; + this.ElevateButton.Size = new System.Drawing.Size(244, 30); + this.ElevateButton.TabIndex = 4; + this.ElevateButton.Text = "&Allow editing shortcuts shared by all users"; + this.MainFormToolTip.SetToolTip(this.ElevateButton, "Shared shortcuts (in the common Start Menu and Desktop)\r\nmay only be edited by Ad" + + "ministrators.\r\nClick this button to relaunch this program as an Administrator."); + this.ElevateButton.UseVisualStyleBackColor = true; + this.ElevateButton.Click += new System.EventHandler(this.ElevateButton_Click); + // + // ShowAllCheckBox + // + this.ShowAllCheckBox.AutoSize = true; + this.ShowAllCheckBox.Location = new System.Drawing.Point(3, 11); + this.ShowAllCheckBox.Name = "ShowAllCheckBox"; + this.ShowAllCheckBox.Size = new System.Drawing.Size(157, 17); + this.ShowAllCheckBox.TabIndex = 1; + this.ShowAllCheckBox.Text = "&Show all available programs"; + this.MainFormToolTip.SetToolTip(this.ShowAllCheckBox, "Unchecked - Show only shortcuts with assigned hotkeys\r\nChecked - Show all shortcu" + + "ts to which hotkeys may be assigned"); + this.ShowAllCheckBox.UseVisualStyleBackColor = true; + this.ShowAllCheckBox.CheckedChanged += new System.EventHandler(this.ShowAllCheckBox_CheckedChanged); + // + // InitialProgressBar + // + this.InitialProgressBar.Location = new System.Drawing.Point(312, 273); + this.InitialProgressBar.MarqueeAnimationSpeed = 10; + this.InitialProgressBar.Name = "InitialProgressBar"; + this.InitialProgressBar.Size = new System.Drawing.Size(312, 23); + this.InitialProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.InitialProgressBar.TabIndex = 5; + // + // MainFormToolTip + // + this.MainFormToolTip.AutoPopDelay = 30000; + this.MainFormToolTip.InitialDelay = 500; + this.MainFormToolTip.ReshowDelay = 100; + this.MainFormToolTip.ShowAlways = true; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(936, 685); + this.Controls.Add(this.InitialProgressBar); + this.Controls.Add(this.panel1); + this.Controls.Add(this.ShortcutGrid); + this.Cursor = System.Windows.Forms.Cursors.WaitCursor; + this.DoubleBuffered = true; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(790, 38); + this.Name = "MainForm"; + this.Text = "Shortcut Key Manager"; + ((System.ComponentModel.ISupportInitialize)(this.ShortcutGrid)).EndInit(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.DataGridView ShortcutGrid; + private System.ComponentModel.BackgroundWorker FindShortcutWorker; + private System.Windows.Forms.DataGridViewTextBoxColumn ShortcutColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn HotkeyColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn LocationColumn; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button ElevateButton; + private System.Windows.Forms.CheckBox ShowAllCheckBox; + private System.Windows.Forms.Button ClearAllButton; + private System.Windows.Forms.Button ClearButton; + private System.Windows.Forms.ProgressBar InitialProgressBar; + private System.Windows.Forms.ToolTip MainFormToolTip; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/MainForm.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,397 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Security.Principal; +using System.Windows.Forms; + +namespace ShortcutKeyFinder +{ + /// <summary>Shortcut key manager main form</summary> + public partial class MainForm : Form + { + private const int _iconSize = 16; + private const int _iconMargin = 4; + private const string _registryKey = "Software\\ShortcutKeyManager", _showAllPreference = "ShowAllShortcuts"; + private bool _suspendUpdates, _suspendUpdateButtons; + private int _firstDisplayedRowIndex; + private ShortcutKeyFinder _shortcutKeyFinder; + private BindingSource _dataSource; + private SolidBrush _textBrush; + private StringFormat _cellFormat; + private List<Shortcut> _oldSelection; + private Hotkey _hotkey; + + /// <summary>List of shortcuts currently selected by the user</summary> + private List<Shortcut> SelectedShortcuts + { + get + { + List<Shortcut> shortcuts = new List<Shortcut>(); + foreach (DataGridViewRow row in ShortcutGrid.SelectedRows) + if ((ShortcutGrid.DataSource as BindingSource).Count > row.Index) + shortcuts.Add((Shortcut)row.DataBoundItem); + return shortcuts; + } + } + + /// <summary>Data source for the shortcut grid</summary> + private BindingList<Shortcut> Shortcuts { get { return _shortcutKeyFinder != null ? _shortcutKeyFinder.Shortcuts : null; } } + + /// <summary>Constructor and form initialization</summary> + public MainForm() + { + InitializeComponent(); + // Set up grid data source + _shortcutKeyFinder = new ShortcutKeyFinder(); + _dataSource = new BindingSource() { DataSource = _shortcutKeyFinder.Shortcuts }; + // Cell format for custom painting of shortcut icons and names in the same cell + _cellFormat = new StringFormat(StringFormatFlags.NoWrap) { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter }; + + // Set up grid + ShortcutGrid.AutoGenerateColumns = false; + HotkeyColumn.ReadOnly = false; + ShortcutGrid.DataSource = _dataSource; + ShortcutGrid.CellParsing += ShortcutGrid_CellParsing; + _shortcutKeyFinder.Shortcuts.ListChanged += Shortcuts_ListChanged; + + // Set up UI + SetupElevateButton(); + LoadPreferences(); + + // Reduce grid flicker + typeof(DataGridView).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty, null, ShortcutGrid, new object[] { true }); + } + + /// <summary>Refreshes data when form receives focus</summary> + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + if (!_suspendUpdates) + FindShortcutWorker.RunWorkerAsync(); + } + + /// <summary>Cancels the inline editor if the user leaves the program</summary> + protected override void OnDeactivate(EventArgs e) + { + ShortcutGrid.CancelEdit(); + ShortcutGrid.EndEdit(); + } + + /// <summary>Scans for shortcuts in the background</summary> + private void FindShortcutWorker_DoWork(object sender, DoWorkEventArgs e) + { + // Disable list change events while scan is in progress + _dataSource.RaiseListChangedEvents = false; + _shortcutKeyFinder.LoadShortcuts(IsAdministrator()); + } + + /// <summary>Updates the grid after the scan is complete</summary> + private void FindShortcutWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + _suspendUpdateButtons = true; // Prevent button flicker + // Save UI state + _firstDisplayedRowIndex = ShortcutGrid.FirstDisplayedScrollingRowIndex; + _oldSelection = SelectedShortcuts.ToList(); + + // Refresh the grid + _dataSource.RaiseListChangedEvents = true; + _dataSource.ResetBindings(false); + ShortcutGrid.ClearSelection(); + + // Restore UI state + if (ShortcutGrid.Rows.Count > 0) + ShortcutGrid.FirstDisplayedScrollingRowIndex = Math.Max(0, Math.Min(_firstDisplayedRowIndex, ShortcutGrid.Rows.Count - 1)); + foreach (DataGridViewRow row in ShortcutGrid.Rows) + row.Selected = _oldSelection.Contains((Shortcut)row.DataBoundItem); + _suspendUpdateButtons = false; + UpdateButtons(); + + // Hide busy indicators that appear when the form is first launched + InitialProgressBar.Visible = false; + ShortcutGrid.Visible = true; + Cursor = Cursors.Default; + } + + /// <summary>Updates the UI when a shortcut's properties have changed</summary> + void Shortcuts_ListChanged(object sender, ListChangedEventArgs e) + { + // Update the "Clear shortcut key" button since the selection's shortcut may have been set or unset + if (!_suspendUpdateButtons) + { + if (this.InvokeRequired) + this.Invoke(new Action(() => UpdateButtons())); + else + UpdateButtons(); + } + // Force repainting of changed rows so the background color gets updated + if (e.ListChangedType == ListChangedType.ItemChanged && e.NewIndex < ShortcutGrid.Rows.Count) + ShortcutGrid.InvalidateRow(e.NewIndex); + } + + /// <summary>Enables or disables the buttons depending on the current selection</summary> + private void UpdateButtons() + { + ClearButton.Enabled = ShortcutGrid.SelectedRows.Count > 0 && SelectedShortcuts.Any(s => s.Hotkey != null); + ClearAllButton.Enabled = Shortcuts.Any(s => s.Hotkey != null); + } + + /// <summary>Sets and saves a shortcut's hotkey</summary> + private void SetShortcutHotkey(Shortcut shortcut, Hotkey hotkey) + { + try + { + shortcut.Hotkey = hotkey; + shortcut.Save(); + } + catch + { + MessageBox.Show("Error setting shortcut key", "Set Shortcut Key", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// <summary>Sets cell background color based on its shortcut's properties, and inserts the shortcut's icon next to its name</summary> + private void ShortcutGrid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) + { + // Header row + if (e.RowIndex < 0) + return; + + DataGridViewRow row = ShortcutGrid.Rows[e.RowIndex]; + Shortcut shortcut = (Shortcut)row.DataBoundItem; + + // Paint duplicate shortcut keys red + if (shortcut.HasDuplicateHotkey) + { + e.CellStyle.BackColor = Color.DarkRed; + e.CellStyle.ForeColor = Color.White; + e.CellStyle.SelectionBackColor = Color.OrangeRed; + e.CellStyle.SelectionForeColor = Color.White; + } + // Paint readonly shortcuts gray + else if (shortcut.ReadOnly) + { + e.CellStyle.BackColor = Color.LightGray; + e.CellStyle.ForeColor = Color.Black; + e.CellStyle.SelectionBackColor = Color.SteelBlue; + e.CellStyle.SelectionForeColor = Color.White; + } + + // Paint the shortcut's icon next to its name + if (e.ColumnIndex == ShortcutColumn.Index) + { + Color textColor = row.Selected ? e.CellStyle.SelectionForeColor : e.CellStyle.ForeColor; + if (_textBrush == null || _textBrush.Color != textColor) + _textBrush = new SolidBrush(textColor); + e.PaintBackground(e.ClipBounds, row.Selected); + Rectangle textRect = new Rectangle(e.CellBounds.X + _iconSize + _iconMargin * 2, e.CellBounds.Y, e.CellBounds.Width - _iconSize - _iconMargin * 2, e.CellBounds.Height); + e.Graphics.DrawImage(shortcut.Icon, _iconMargin, textRect.Y + ((textRect.Height - _iconSize) / 2)); + e.Graphics.DrawString(e.FormattedValue.ToString(), e.CellStyle.Font, _textBrush, textRect, _cellFormat); + e.Handled = true; + } + } + + /// <summary>Triggers direct hotkey editing if the hotkey column is double clicked, or opens the shortcut's Properties window for any other column</summary> + private void ShortcutGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + if (e.ColumnIndex == HotkeyColumn.Index) + { + ShortcutGrid.CurrentCell = ShortcutGrid.Rows[e.RowIndex].Cells[e.ColumnIndex]; + ShortcutGrid.BeginEdit(true); + } + else + { + ((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem).ShowExplorerPropertiesWindow(Handle); + } + } + + /// <summary>Opens the Properties window for the selected row on Enter, or activates the inline shortcut editor on F2</summary> + private void ShortcutGrid_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { + e.Handled = true; + if (SelectedShortcuts.Count == 1) + SelectedShortcuts[0].ShowExplorerPropertiesWindow(Handle); + } + else if (e.KeyCode == Keys.F2) + { + if (ShortcutGrid.SelectedRows.Count == 1) + { + ShortcutGrid.CurrentCell = ShortcutGrid.SelectedRows[0].Cells[HotkeyColumn.Index]; + ShortcutGrid.BeginEdit(true); + } + } + } + + /// <summary>Prepares the inline editor by clearing the text and registering event handlers</summary> + private void ShortcutGrid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) + { + (e.Control as TextBox).Text = ""; + (e.Control as TextBox).KeyDown += CellEditor_KeyDown; + (e.Control as TextBox).PreviewKeyDown += CellEditor_PreviewKeyDown; + (e.Control as TextBox).Leave += CellEditor_Leave; + } + + /// <summary>Removes event handlers from inline editor when it loses focus</summary> + void CellEditor_Leave(object sender, EventArgs e) + { + (sender as TextBox).KeyDown -= CellEditor_KeyDown; + (sender as TextBox).PreviewKeyDown -= CellEditor_PreviewKeyDown; + (sender as TextBox).Leave -= CellEditor_Leave; + } + + /// <summary>Finishes inline hotkey editing on Enter</summary> + private void CellEditor_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + if (e.KeyCode == Keys.Enter) + ShortcutGrid.EndEdit(); + } + + /// <summary>Listens for key events when the inline editor is active and converts them to a hotkey</summary> + void CellEditor_KeyDown(object sender, KeyEventArgs e) + { + System.Diagnostics.Debug.WriteLine("keydown"); + if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) + { + _hotkey = null; + (sender as TextBox).Clear(); + } + else if (e.KeyCode != Keys.Enter) + { + _hotkey = new Hotkey() + { + KeyCode = (byte)e.KeyCode, + Control = e.Control, + Alt = e.Alt, + Shift = e.Shift + }; + (sender as TextBox).Text = _hotkey.ToString(); + } + e.Handled = true; + e.SuppressKeyPress = true; + } + + /// <summary>Saves the new hotkey to the data source and the shortcut when inline editing is complete</summary> + private void ShortcutGrid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) + { + e.ParsingApplied = true; + e.Value = _hotkey; + SetShortcutHotkey((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem, _hotkey); + } + + /// <summary>Updates the list view to show all shorcuts or only shortcuts with hotkeys</summary> + private void ShowAllCheckBox_CheckedChanged(object sender, EventArgs e) + { + _shortcutKeyFinder.ShowAll = ShowAllCheckBox.Checked; + ShortcutGrid.ClearSelection(); + SavePreferences(); + } + + /// <summary>Removes the hotkeys from the selected shortcuts when the Clear button is clicked</summary> + private void ClearButton_Click(object sender, EventArgs e) + { + foreach (Shortcut shortcut in SelectedShortcuts.Where(s => !s.ReadOnly && s.Hotkey != null)) + SetShortcutHotkey(shortcut, null); + } + + /// <summary>Clears shortcuts from all hotkeys when the Clear All button is clicked</summary> + private void ClearAllButton_Click(object sender, EventArgs e) + { + _suspendUpdates = true; + if (MessageBox.Show("Are you sure you want to clear all assigned shortcut keys?", "Clear Shortcut Keys", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + foreach (Shortcut shortcut in Shortcuts.Where(s => !s.ReadOnly && s.Hotkey != null)) + SetShortcutHotkey(shortcut, null); + } + _suspendUpdates = false; + } + + /// <summary>Prepares for inline shortcut editing, or prevents it if the shortcut is read only</summary> + private void ShortcutGrid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) + { + _hotkey = null; + e.Cancel = ((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem).ReadOnly; + } + + /// <summary>Updates the button state when the grid selection changes</summary> + private void ShortcutGrid_SelectionChanged(object sender, EventArgs e) + { + if (!_suspendUpdateButtons) + UpdateButtons(); + } + + /// <summary>Updates the button state since the hotkey of the current item may have changed</summary> + private void ShortcutGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + if (!_suspendUpdateButtons) + UpdateButtons(); + } + + /// <summary>Checks whether the program is running with elevated permissions</summary> + /// <remarks>http://stackoverflow.com/a/11660205</remarks> + public static bool IsAdministrator() + { + return (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator); + } + + /// <summary>Shows or hides the button to elevate to administrator based on whether the program is already running as an administrator</summary> + private void SetupElevateButton() + { + if (IsAdministrator()) + ElevateButton.Visible = false; + else + Win32Helpers.AddUacShield(ElevateButton); + } + + /// <summary>Restarts the program, requesting elevation</summary> + private void ElevateButton_Click(object sender, EventArgs e) + { + ProcessStartInfo info = new ProcessStartInfo() { UseShellExecute = true, WorkingDirectory = Environment.CurrentDirectory, FileName = Application.ExecutablePath, Verb = "runas" }; + try + { + Process.Start(info); + Application.Exit(); + } + catch { } // Do nothing if the user canceled the consent dialog + } + + /// <summary>Saves checkbox state so it can be restored when the program is relaunched later</summary> + private void SavePreferences() + { + try + { + RegistryKey key = Registry.CurrentUser.CreateSubKey(_registryKey); + key.SetValue(_showAllPreference, ShowAllCheckBox.Checked, RegistryValueKind.DWord); + } + catch { } // Silently fail if we can't save preferences + } + + /// <summary>Restores the checkbox state to the same as when the program last exited</summary> + private void LoadPreferences() + { + try + { + RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryKey); + ShowAllCheckBox.Checked = Convert.ToBoolean((int)key.GetValue(_showAllPreference, 0)); + } + catch { } // Silently fail if we can't load preferences + } + + private void ShortcutGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + string tooltip = null; + Shortcut shortcut = (Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem; + + if (shortcut.HasDuplicateHotkey) + tooltip = "More than one shortcut has the same hotkey assigned"; + else if (shortcut.ReadOnly) + tooltip = "Administrative privileges are required to edit this shortcut." + Environment.NewLine + "To edit, click the \"Allow editing shortcuts shared by all users\" button."; + + ShortcutGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].ToolTipText = tooltip; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/MainForm.resx Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <metadata name="ShortcutColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </metadata> + <metadata name="HotkeyColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </metadata> + <metadata name="LocationColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </metadata> + <metadata name="FindShortcutWorker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>17, 17</value> + </metadata> + <metadata name="MainFormToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>178, 17</value> + </metadata> + <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> + <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value> + AAABAAIAICAAAAEAIACoEAAAJgAAABAQAAABACAAaAQAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAg + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADKy8vVys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/zMG//8jNzfDUjUL/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9WLPP/Kzc3/yM3N8NWG + M//pu4v/782p/9aCKf/vzan/782p/9eGMf/tyaL/782p/9aCKf/vzan/782p/+/Nqf/vzan/782p/+/N + qf/vzan/782p/+/Nqf/vzan/14Yx/+3Jov/vzan/7cmi/9eGMf/vzan/782p/+/Nqf/sxJr/1oIp/8rN + zf/Izc3w1YYz//Xizf//////1oIp////////////2Ik2//348v//////1oIp//////////////////// + ///////////////////////////////////YiTb//fjy///////9+PL/2Ik2//////////////////rw + 5v/Wgin/ys3N/8jNzfDVhjP/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Kzc3/yM3N8NWGM//tyaL/782p/+/Nqf/Wgin/782p/+/Nqf/XhjH/7cmi/+/N + qf/XhjH/7cmi/+3Jov/XhjH/782p/+/Nqf/XhjH/7cmi/+/Nqf/XhjH/7cmi/+/Nqf/XhjH/7cmi/+/N + qf/Wgin/782p/+/Nqf/tyaL/1oIp/8rNzf/Izc3w1YYz//348v///////////9aCKf///////////9iJ + Nv/9+PL//////9iJNv/9+PL//fjy/9iJNv///////////9iJNv/9+PL//////9iJNv/9+PL//////9iJ + Nv/9+PL//////9aCKf////////////348v/Wgin/ys3N/8jNzfDVhjP/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Kzc3/yM3N8NWGM//tyaL/782p/9aC + Kf/vzan/782p/9aCKf/vzan/782p/9eGMf/tyaL/782p/9aCKf/vzan/782p/9aCKf/vzan/782p/9aC + Kf/vzan/7cmi/9eGMf/vzan/782p/9aCKf/vzan/782p/+/Nqf/tyaL/1oIp/8rNzf/Izc3w1YYz//34 + 8v//////1oIp////////////1oIp////////////2Ik2//348v//////1oIp////////////1oIp//// + ////////1oIp///////9+PL/2Ik2////////////1oIp//////////////////348v/Wgin/ys3N/8jN + zfDViTj/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9WG + Mf/Kzc3/yM3N8MrNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/ys3N/8rNzf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////// + //////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD/////////////////////////////////////////////////////KAAAABAAAAAgAAAAAQAgAAAA + AAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMnMzLLKzc3/ys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8nMzLLKzc3/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Kzc3/ys3N/9aCKf/17eT/9e3k/9aC + Kf/17eT/9e3k//Xt5P/17eT/9e3k//Xt5P/Wgin/9e3k//Xt5P/Wgin/ys3N/8rNzf/Wgin/1oIp/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/8rNzf/Kzc3/1oIp//Xt + 5P/17eT/9e3k/9aCKf/17eT/1oIp//Xt5P/Wgin/9e3k/9aCKf/17eT/9e3k/9aCKf/Kzc3/ys3N/9aC + Kf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/ys3N/8rN + zf/Wgin/9e3k//Xt5P/Wgin/9e3k/9aCKf/17eT/1oIp//Xt5P/Wgin/9e3k/9aCKf/17eT/1oIp/8rN + zf/Kzc3/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aCKf/Wgin/1oIp/9aC + Kf/Kzc3/yczMssrNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rNzf/Kzc3/ys3N/8rN + zf/Kzc3/yczMsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA//8AAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD//wAA//8AAP//AAD//wAA +</value> + </data> +</root> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Program.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ShortcutKeyFinder +{ + static class Program + { + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Properties/AssemblyInfo.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Shortcut Key Manager")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Brad Greco")] +[assembly: AssemblyProduct("Shortcut Key Manager")] +[assembly: AssemblyCopyright("Copyright © Brad Greco 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("04eab118-b2ce-4d90-acd0-475fb7973793")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Properties/Resources.Designer.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17929 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace ShortcutKeyFinder.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ShortcutKeyFinder.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Properties/Resources.resx Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Properties/Settings.Designer.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17929 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace ShortcutKeyFinder.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Properties/Settings.settings Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8'?> +<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)"> + <Profiles> + <Profile Name="(Default)" /> + </Profiles> + <Settings /> +</SettingsFile>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Shortcut.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,135 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.IO; + +namespace ShortcutKeyFinder +{ + /// <summary>Class to perform operations on shortcut (.lnk) files</summary> + class Shortcut : INotifyPropertyChanged + { + private bool _hasDuplicateHotkey; + private Hotkey _hotkey; + public event PropertyChangedEventHandler PropertyChanged; + + /// <summary>Name of the shortcut as displayed by Windows Explorer</summary> + /// <remarks>Not necessarily the same as the shortcut's filename</remarks> + public string Name { get; private set; } + + /// <summary>Full path of the shortcut file</summary> + public string Path { get; private set; } + + /// <summary>Full path of the shortcut's parent directory</summary> + public string Location + { + get { return System.IO.Path.GetDirectoryName(Path); } + } + + /// <summary>Display name of the shortcut's parent directory</summary> + /// <remarks>Gives the user a general idea of where the shortcut is located rather than the full path</remarks> + public string DisplayLocation + { + get { + return Location + .Replace(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Start Menu") + .Replace(Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), "Start Menu") + .Replace(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Desktop") + .Replace(Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory), "Desktop"); + } + + } + + /// <summary>Hotkey associated with the shortcut</summary> + public Hotkey Hotkey + { + get { return _hotkey; } + set + { + if (value != _hotkey) + { + _hotkey = value; + NotifyPropertyChanged("Hotkey"); + } + } + } + + /// <summary>Icon associated with the shortcut</summary> + public Image Icon { get; private set; } + + /// <summary>Indicates whether this shortcut's hotkey is the same as that of another shortcut</summary> + /// <remarks>Always false unless set to true by some other class</remarks> + public bool HasDuplicateHotkey + { + get { return _hasDuplicateHotkey; } + set + { + if (value != _hasDuplicateHotkey) + { + _hasDuplicateHotkey = value; + NotifyPropertyChanged("HasDuplicateHotkey"); + } + } + } + + /// <summary>Indicates whether the user has permissions to modify the shortcut</summary> + /// <remarks>Always false unless set to true by some other class</remarks> + public bool ReadOnly { get; set; } + + /// <summary>Constructor</summary> + /// <param name="path">Full path to a shortcut (.lnk) file</param> + public Shortcut(string path) + { + Path = path; + LoadShortcutInfo(); + } + + /// <summary>Implements the <see cref="INotifyPropertyChanged"/> interface</summary> + private void NotifyPropertyChanged(String info) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(info)); + } + } + + /// <summary>Constructor</summary> + private void LoadShortcutInfo() + { + ushort hotkey = Win32Helpers.GetShortcutHotkey(Path); + if (hotkey != 0) + Hotkey = new Hotkey(hotkey); + FileInfo info = Win32Helpers.GetFileInfo(Path, true, true); + if (info != null) + { + Name = info.DisplayName; + Icon = info.Icon.ToBitmap(); + } + } + + /// <summary>Shows the Windows Explorer properties window for the shortcut</summary> + /// <param name="parentWindow">Optional handle to a parent window for use when displaying error messages</param> + public void ShowExplorerPropertiesWindow(IntPtr parentWindow = default(IntPtr)) + { + Win32Helpers.ShowFilePropertiesWindow(Path, parentWindow); + } + + /// <summary>Writes the shortcut data to disk</summary> + public void Save() + { + if (!Win32Helpers.SetShortcutHotkey(Path, Hotkey != null ? Hotkey.RawHotkey : (ushort)0)) + throw new UnauthorizedAccessException(); + } + + /// <summary>Determine whether two shortcuts are the same file</summary> + public override bool Equals(object obj) + { + Shortcut shortcut = obj as Shortcut; + return shortcut != null && shortcut.Path == Path; + } + + public override int GetHashCode() + { + return Path.GetHashCode(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/ShortcutKeyFinder.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; + +namespace ShortcutKeyFinder +{ + /// <summary>Class to find all shortcuts that have hotkeys assigned</summary> + /// <remarks>As far as I can tell, Windows only looks for shortcut hotkeys in the Start Menu and on the Desktop. So this class does as well.</remarks> + class ShortcutKeyFinder + { + private bool _showAll; + private List<Shortcut> _allShortcuts = new List<Shortcut>(); // List to hold all the shortcuts we find, regardless of whether they have hotkeys + + /// <summary>List of all shortcuts found in the search locations</summary> + /// <remarks>If <see cref="ShowAll"/> is true, shortcuts without assigned hotkeys are included</remarks> + public BindingList<Shortcut> Shortcuts { get; private set; } + + /// <summary>Indicates whether to include shortcuts without assigned hotkeys in the list of <see cref="Shortcuts"/></summary> + public bool ShowAll + { + get { return _showAll; } + set + { + _showAll = value; + UpdateShortcutList(); + } + } + + /// <summary>Constructor</summary> + public ShortcutKeyFinder() + { + Shortcuts = new BindingList<Shortcut>(); + } + + /// <summary>Scans the search locations for shortcut files</summary> + /// <param name="Admin">Whether to consider shortcuts owned by all users as read-only</param> + public void LoadShortcuts(bool Admin = false) + { + // Reset the master list and rescan the search directories + _allShortcuts.Clear(); + LoadShortcuts(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), false); + LoadShortcuts(Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), !Admin); + LoadShortcuts(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), false); + LoadShortcuts(Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory), !Admin); + // Update the public list with what we found + UpdateShortcutList(); + } + + /// <summary>Recursively scans a directory shortcut files</summary> + /// <param name="path">Directory to scan</param> + /// <param name="readOnly">Whether to consider the found shortcuts as read-only</param> + /// <remarks>The <paramref name="readOnly"/> parameter exists for speed, to avoid checking permissions on every file. Good enough for our purposes.</remarks> + private void LoadShortcuts(string path, bool readOnly) + { + try + { + foreach (string lnk in Directory.EnumerateFiles(path, "*.lnk", SearchOption.AllDirectories)) + _allShortcuts.Add(new Shortcut(lnk) { ReadOnly = readOnly }); + } + catch { } // Silently ignore access denied errors, likely nothing the user can do about it anyway + } + + /// <summary>Updates the public list of <see cref="Shortcuts"/> with the results of the most recent scan</summary> + private void UpdateShortcutList() + { + // Remove event handlers while we're manipulating the list + Shortcuts.ListChanged -= Shortcuts_ListChanged; + + // Reset the list and add all appropriate shortcuts based on the current value of ShowAll + Shortcuts.Clear(); + foreach (Shortcut shortcut in _allShortcuts) + if (shortcut.Hotkey != null || _showAll) + Shortcuts.Add(shortcut); + + // Go through the list and look for duplicates + FindDuplicateShortcuts(); + + // Re-add event handlers + Shortcuts.ListChanged += Shortcuts_ListChanged; + } + + /// <summary>Triggers a search for duplicate shortcuts when one of the shortcuts changes</summary> + void Shortcuts_ListChanged(object sender, ListChangedEventArgs e) + { + FindDuplicateShortcuts(); + } + + /// <summary>Scans for shortcuts that have duplicate hotkeys</summary> + private void FindDuplicateShortcuts() + { + Shortcuts.GroupBy(shortcut => shortcut.Hotkey).ToList().ForEach(group => group.ToList().ForEach(shortcut => shortcut.HasDuplicateHotkey = group.Count() > 1 && group.Key != null)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/ShortcutKeyFinder.csproj Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1}</ProjectGuid> + <OutputType>WinExe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>ShortcutKeyFinder</RootNamespace> + <AssemblyName>ShortcutKeyFinder</AssemblyName> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <TargetFrameworkProfile /> + <IsWebBootstrapper>false</IsWebBootstrapper> + <PublishUrl>publish\</PublishUrl> + <Install>true</Install> + <InstallFrom>Disk</InstallFrom> + <UpdateEnabled>false</UpdateEnabled> + <UpdateMode>Foreground</UpdateMode> + <UpdateInterval>7</UpdateInterval> + <UpdateIntervalUnits>Days</UpdateIntervalUnits> + <UpdatePeriodically>false</UpdatePeriodically> + <UpdateRequired>false</UpdateRequired> + <MapFileExtensions>true</MapFileExtensions> + <ApplicationRevision>0</ApplicationRevision> + <ApplicationVersion>1.0.0.%2a</ApplicationVersion> + <UseApplicationTrust>false</UseApplicationTrust> + <BootstrapperEnabled>true</BootstrapperEnabled> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <Prefer32Bit>false</Prefer32Bit> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <Prefer32Bit>false</Prefer32Bit> + </PropertyGroup> + <PropertyGroup> + <SignAssembly>false</SignAssembly> + </PropertyGroup> + <PropertyGroup> + <NoWin32Manifest>true</NoWin32Manifest> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>keyboard.ico</ApplicationIcon> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Net" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Deployment" /> + <Reference Include="System.Drawing" /> + <Reference Include="System.Windows.Forms" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Hotkey.cs" /> + <Compile Include="MainForm.cs"> + <SubType>Form</SubType> + </Compile> + <Compile Include="MainForm.Designer.cs"> + <DependentUpon>MainForm.cs</DependentUpon> + </Compile> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Shortcut.cs" /> + <Compile Include="ShortcutKeyFinder.cs" /> + <Compile Include="Win32Helpers.cs" /> + <EmbeddedResource Include="MainForm.resx"> + <DependentUpon>MainForm.cs</DependentUpon> + <SubType>Designer</SubType> + </EmbeddedResource> + <EmbeddedResource Include="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + <SubType>Designer</SubType> + </EmbeddedResource> + <Compile Include="Properties\Resources.Designer.cs"> + <AutoGen>True</AutoGen> + <DependentUpon>Resources.resx</DependentUpon> + <DesignTime>True</DesignTime> + </Compile> + <None Include="Properties\Settings.settings"> + <Generator>SettingsSingleFileGenerator</Generator> + <LastGenOutput>Settings.Designer.cs</LastGenOutput> + </None> + <Compile Include="Properties\Settings.Designer.cs"> + <AutoGen>True</AutoGen> + <DependentUpon>Settings.settings</DependentUpon> + <DesignTimeSharedInput>True</DesignTimeSharedInput> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + </ItemGroup> + <ItemGroup> + <BootstrapperPackage Include=".NETFramework,Version=v4.0"> + <Visible>False</Visible> + <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5 SP1</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Windows.Installer.4.5"> + <Visible>False</Visible> + <ProductName>Windows Installer 4.5</ProductName> + <Install>true</Install> + </BootstrapperPackage> + </ItemGroup> + <ItemGroup> + <Content Include="keyboard.ico" /> + </ItemGroup> + <ItemGroup> + <Folder Include="Images\" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/ShortcutKeyFinder.csproj.user Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <PublishUrlHistory>publish\</PublishUrlHistory> + <InstallUrlHistory /> + <SupportUrlHistory /> + <UpdateUrlHistory /> + <BootstrapperUrlHistory /> + <ErrorReportUrlHistory /> + <FallbackCulture>en-US</FallbackCulture> + <VerifyUploadedFiles>false</VerifyUploadedFiles> + </PropertyGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShortcutKeyFinder/Win32Helpers.cs Sat Jun 25 13:42:54 2016 +1000 @@ -0,0 +1,297 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace ShortcutKeyFinder +{ + /// <summary>Various functions to call the Win32 API</summary> + /// <remarks>Method signatures taken from http://www.pinvoke.net/ </remarks> + class Win32Helpers + { + public const int STGM_READ = 0x00000000; + public const int STGM_READWRITE = 0x00000002; + public const int ILD_NORMAL = 0; + public const int SW_SHOWNORMAL = 1; + public const uint SEE_MASK_INVOKEIDLIST = 0x0000000C; + public const uint BCM_SETSHIELD = 0x160C; + public const int SHGFI_SMALLICON = 0x000000001; + public const int SHGFI_ICON = 0x000000100; + public const int SHGFI_DISPLAYNAME = 0x000000200; + public const int SHGFI_SYSICONINDEX = 0x000004000; + + + #region GetShortcutHotkey, SetShortcutHotkey + + // The CharSet must match the CharSet of the corresponding PInvoke signature + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + /// <summary>IShellLink.GetPath fFlags: Flags that specify the type of path information to retrieve</summary> + [Flags()] + enum SLGP_FLAGS + { + /// <summary>Retrieves the standard short (8.3 format) file name</summary> + SLGP_SHORTPATH = 0x1, + /// <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary> + SLGP_UNCPRIORITY = 0x2, + /// <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary> + SLGP_RAWPATH = 0x4 + } + + /// <summary>IShellLink.Resolve fFlags</summary> + [Flags()] + enum SLR_FLAGS + { + /// <summary> + /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + /// the high-order word of fFlags can be set to a time-out value that specifies the + /// maximum amount of time to be spent resolving the link. The function returns if the + /// link cannot be resolved within the time-out duration. If the high-order word is set + /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + /// duration, in milliseconds. + /// </summary> + SLR_NO_UI = 0x1, + /// <summary>Obsolete and no longer used</summary> + SLR_ANY_MATCH = 0x2, + /// <summary>If the link object has changed, update its path and list of identifiers. + /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine + /// whether or not the link object has changed.</summary> + SLR_UPDATE = 0x4, + /// <summary>Do not update the link information</summary> + SLR_NOUPDATE = 0x8, + /// <summary>Do not execute the search heuristics</summary> + SLR_NOSEARCH = 0x10, + /// <summary>Do not use distributed link tracking</summary> + SLR_NOTRACK = 0x20, + /// <summary>Disable distributed link tracking. By default, distributed link tracking tracks + /// removable media across multiple devices based on the volume name. It also uses the + /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + /// has changed. Setting SLR_NOLINKINFO disables both types of tracking.</summary> + SLR_NOLINKINFO = 0x40, + /// <summary>Call the Microsoft Windows Installer</summary> + SLR_INVOKE_MSI = 0x80 + } + + /// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary> + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// <summary>Retrieves the path and file name of a Shell link object</summary> + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + /// <summary>Retrieves the list of item identifiers for a Shell link object</summary> + void GetIDList(out IntPtr ppidl); + /// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary> + void SetIDList(IntPtr pidl); + /// <summary>Retrieves the description string for a Shell link object</summary> + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary> + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// <summary>Retrieves the name of the working directory for a Shell link object</summary> + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// <summary>Sets the name of the working directory for a Shell link object</summary> + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// <summary>Retrieves the command-line arguments associated with a Shell link object</summary> + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// <summary>Sets the command-line arguments for a Shell link object</summary> + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// <summary>Retrieves the hot key for a Shell link object</summary> + void GetHotkey(out ushort pwHotkey); + /// <summary>Sets a hot key for a Shell link object</summary> + void SetHotkey(ushort wHotkey); + /// <summary>Retrieves the show command for a Shell link object</summary> + void GetShowCmd(out int piShowCmd); + /// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary> + void SetShowCmd(int iShowCmd); + /// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary> + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// <summary>Sets the location (path and index) of the icon for a Shell link object</summary> + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// <summary>Sets the relative path to the Shell link object</summary> + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary> + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + /// <summary>Sets the path and file name of a Shell link object</summary> + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + /// <summary>Retrieves the hotkey assigned to a shortcut (.lnk) file</summary> + /// <param name="path">Path to a shortcut (.lnk) file</param> + /// <returns>A binary value representing the shortcut's hotkey</returns> + public static ushort GetShortcutHotkey(string path) + { + Type obj = Type.GetTypeFromCLSID(new Guid("00021401-0000-0000-C000-000000000046"), true); + IShellLinkW link = Activator.CreateInstance(obj) as IShellLinkW; + + ((IPersistFile)link).Load(path, STGM_READ); + ushort hotkey; + link.GetHotkey(out hotkey); + return hotkey; + } + + /// <summary>Assigns a hotkey to a shortcut (.lnk) file</summary> + /// <param name="path">Path to an exising shortcut (.lnk) file to modify</param> + /// <param name="hotkey">Binary value containing the hotkey to set</param> + public static bool SetShortcutHotkey(string path, ushort hotkey) + { + Type obj = Type.GetTypeFromCLSID(new Guid("00021401-0000-0000-C000-000000000046"), true); + IShellLinkW link = Activator.CreateInstance(obj) as IShellLinkW; + + try + { + ((IPersistFile)link).Load(path, STGM_READWRITE); + link.SetHotkey(hotkey); + ((IPersistFile)link).Save(path, false); + return true; + } + catch + { + return false; + } + } + + #endregion + + #region GetFileInfo + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags); + + [DllImport("comctl32.dll", SetLastError = true)] + public static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, int flags); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct SHFILEINFO + { + public IntPtr hIcon; + public int iIcon; + public uint dwAttributes; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szDisplayName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + }; + + /// <summary>Retrieves the hotkey assigned to a shortcut (.lnk) file</summary> + /// <param name="path">Path of the file to retreive properties from</param> + /// <param name="smallIcon">Set to true to retreive the 16x16 icon. By default, the 32x32 icon is retrieved.</param> + /// <param name="noOverlays">Set to true to remove icon overlays as displayed in Windows Explorer (for example, the shortcut arrow)</param> + public static FileInfo GetFileInfo(string path, bool smallIcon = false, bool noOverlays = false) + { + SHFILEINFO info = new SHFILEINFO(); + uint flags = SHGFI_DISPLAYNAME; + if (smallIcon) + flags |= SHGFI_SMALLICON; + if (noOverlays) + flags |= SHGFI_SYSICONINDEX; + else + flags |= SHGFI_ICON; + IntPtr shgfi = SHGetFileInfo(path, 0, ref info, (uint)Marshal.SizeOf(info), flags); + + if (shgfi == IntPtr.Zero) + return null; + + if (noOverlays) + { + IntPtr icon = ImageList_GetIcon(shgfi, info.iIcon, ILD_NORMAL); + return new FileInfo(info.szDisplayName, icon != IntPtr.Zero ? Icon.FromHandle(icon) : null); + } + else + return new FileInfo(info.szDisplayName, Icon.FromHandle(info.hIcon)); + } + + #endregion + + #region ShowFilePropertiesWindow + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); + + [StructLayout(LayoutKind.Sequential)] + public struct SHELLEXECUTEINFO + { + public int cbSize; + public uint fMask; + public IntPtr hwnd; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpVerb; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpFile; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpParameters; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpDirectory; + public int nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIcon; + public IntPtr hProcess; + } + + /// <summary>Displays the Properties window of a file</summary> + /// <param name="path">File to display the properties window for</param> + /// <param name="parentWindow">Handle to the parent window for error messages to be displayed</param> + public static void ShowFilePropertiesWindow(string path, IntPtr parentWindow = default(IntPtr)) + { + SHELLEXECUTEINFO info = new SHELLEXECUTEINFO(); + info.cbSize = Marshal.SizeOf(info); + info.fMask = SEE_MASK_INVOKEIDLIST; + info.hwnd = parentWindow; + info.lpVerb = "properties"; + info.lpFile = path; + info.nShow = SW_SHOWNORMAL; + ShellExecuteEx(ref info); + } + + #endregion + + #region AddUacShield + + [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] + static extern int SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam); + + /// <summary>Adds a UAC shield icon to a button</summary> + /// <param name="button">Button to add the shield icon to</param> + public static void AddUacShield(System.Windows.Forms.Button button) + { + button.FlatStyle = System.Windows.Forms.FlatStyle.System; + SendMessage(button.Handle, BCM_SETSHIELD, 0, (IntPtr)1); + } + + #endregion + } + + /// <summary>Class to hold basic file information</summary> + class FileInfo + { + public string DisplayName; + public Icon Icon; + + public FileInfo(string DisplayName, Icon Icon) + { + this.DisplayName = DisplayName; + this.Icon = Icon; + } + } +}