# HG changeset patch # User Brad Greco # Date 1466826174 -36000 # Node ID 209d9210c18f16375b5f2b4c7d2d76a8be0dfb15 It works. diff -r 000000000000 -r 209d9210c18f .hgignore --- /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 diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder.sln --- /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 diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/App.config --- /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 @@ + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Hotkey.cs --- /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 +{ + /// Provides a means to encode and decode hotkeys in the format expected by the Win32 API + class Hotkey + { + private List _modifierKeys = new List() { 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; + + /// Raw hotkey value to use in Win32 functions + public ushort RawHotkey { get; set; } + + /// Shift key state + public bool Shift + { + get { return (RawHotkey & HOTKEYF_SHIFT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_SHIFT; else RawHotkey &= (ushort)~HOTKEYF_SHIFT; } + } + + /// Control key state + public bool Control + { + get { return (RawHotkey & HOTKEYF_CONTROL) != 0; } + set { if (value) RawHotkey |= HOTKEYF_CONTROL; else RawHotkey &= (ushort)~HOTKEYF_CONTROL; } + } + + /// Alt key state + public bool Alt + { + get { return (RawHotkey & HOTKEYF_ALT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_ALT; else RawHotkey &= (ushort)~HOTKEYF_ALT; } + } + + /// Ext key state + /// Probably useless nowadays + public bool Ext + { + get { return (RawHotkey & HOTKEYF_EXT) != 0; } + set { if (value) RawHotkey |= HOTKEYF_EXT; else RawHotkey &= (ushort)~HOTKEYF_EXT; } + } + + /// The key code portion of the hotkey data + /// The key code is stored in the lower byte of the hotkey + public byte KeyCode + { + get { return (byte)(RawHotkey & 0x00FF); } + set { RawHotkey = (ushort)((RawHotkey & 0xFF00) | value); } + } + + /// Default constructor + public Hotkey() { } + + /// Constructor with hotkey initializer + /// Hotkey value for Win32 API calls + public Hotkey(ushort hotkey) + { + RawHotkey = hotkey; + } + + /// Converts the hotkey data into a friendly string + 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)); + } + + /// Checks if two Hotkey objects have the same key combination + public override bool Equals(object obj) + { + Hotkey hotkey = obj as Hotkey; + return hotkey != null && hotkey.RawHotkey == RawHotkey; + } + + public override int GetHashCode() + { + return RawHotkey.GetHashCode(); + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Images/keyboard.ico Binary file ShortcutKeyFinder/Images/keyboard.ico has changed diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/MainForm.Designer.cs --- /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 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + } +} + diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/MainForm.cs --- /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 +{ + /// Shortcut key manager main form + 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 _oldSelection; + private Hotkey _hotkey; + + /// List of shortcuts currently selected by the user + private List SelectedShortcuts + { + get + { + List shortcuts = new List(); + foreach (DataGridViewRow row in ShortcutGrid.SelectedRows) + if ((ShortcutGrid.DataSource as BindingSource).Count > row.Index) + shortcuts.Add((Shortcut)row.DataBoundItem); + return shortcuts; + } + } + + /// Data source for the shortcut grid + private BindingList Shortcuts { get { return _shortcutKeyFinder != null ? _shortcutKeyFinder.Shortcuts : null; } } + + /// Constructor and form initialization + 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 }); + } + + /// Refreshes data when form receives focus + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + if (!_suspendUpdates) + FindShortcutWorker.RunWorkerAsync(); + } + + /// Cancels the inline editor if the user leaves the program + protected override void OnDeactivate(EventArgs e) + { + ShortcutGrid.CancelEdit(); + ShortcutGrid.EndEdit(); + } + + /// Scans for shortcuts in the background + private void FindShortcutWorker_DoWork(object sender, DoWorkEventArgs e) + { + // Disable list change events while scan is in progress + _dataSource.RaiseListChangedEvents = false; + _shortcutKeyFinder.LoadShortcuts(IsAdministrator()); + } + + /// Updates the grid after the scan is complete + 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; + } + + /// Updates the UI when a shortcut's properties have changed + 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); + } + + /// Enables or disables the buttons depending on the current selection + private void UpdateButtons() + { + ClearButton.Enabled = ShortcutGrid.SelectedRows.Count > 0 && SelectedShortcuts.Any(s => s.Hotkey != null); + ClearAllButton.Enabled = Shortcuts.Any(s => s.Hotkey != null); + } + + /// Sets and saves a shortcut's hotkey + 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); + } + } + + /// Sets cell background color based on its shortcut's properties, and inserts the shortcut's icon next to its name + 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; + } + } + + /// Triggers direct hotkey editing if the hotkey column is double clicked, or opens the shortcut's Properties window for any other column + 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); + } + } + + /// Opens the Properties window for the selected row on Enter, or activates the inline shortcut editor on F2 + 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); + } + } + } + + /// Prepares the inline editor by clearing the text and registering event handlers + 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; + } + + /// Removes event handlers from inline editor when it loses focus + 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; + } + + /// Finishes inline hotkey editing on Enter + private void CellEditor_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + if (e.KeyCode == Keys.Enter) + ShortcutGrid.EndEdit(); + } + + /// Listens for key events when the inline editor is active and converts them to a hotkey + 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; + } + + /// Saves the new hotkey to the data source and the shortcut when inline editing is complete + private void ShortcutGrid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) + { + e.ParsingApplied = true; + e.Value = _hotkey; + SetShortcutHotkey((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem, _hotkey); + } + + /// Updates the list view to show all shorcuts or only shortcuts with hotkeys + private void ShowAllCheckBox_CheckedChanged(object sender, EventArgs e) + { + _shortcutKeyFinder.ShowAll = ShowAllCheckBox.Checked; + ShortcutGrid.ClearSelection(); + SavePreferences(); + } + + /// Removes the hotkeys from the selected shortcuts when the Clear button is clicked + private void ClearButton_Click(object sender, EventArgs e) + { + foreach (Shortcut shortcut in SelectedShortcuts.Where(s => !s.ReadOnly && s.Hotkey != null)) + SetShortcutHotkey(shortcut, null); + } + + /// Clears shortcuts from all hotkeys when the Clear All button is clicked + 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; + } + + /// Prepares for inline shortcut editing, or prevents it if the shortcut is read only + private void ShortcutGrid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) + { + _hotkey = null; + e.Cancel = ((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem).ReadOnly; + } + + /// Updates the button state when the grid selection changes + private void ShortcutGrid_SelectionChanged(object sender, EventArgs e) + { + if (!_suspendUpdateButtons) + UpdateButtons(); + } + + /// Updates the button state since the hotkey of the current item may have changed + private void ShortcutGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + if (!_suspendUpdateButtons) + UpdateButtons(); + } + + /// Checks whether the program is running with elevated permissions + /// http://stackoverflow.com/a/11660205 + public static bool IsAdministrator() + { + return (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator); + } + + /// Shows or hides the button to elevate to administrator based on whether the program is already running as an administrator + private void SetupElevateButton() + { + if (IsAdministrator()) + ElevateButton.Visible = false; + else + Win32Helpers.AddUacShield(ElevateButton); + } + + /// Restarts the program, requesting elevation + 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 + } + + /// Saves checkbox state so it can be restored when the program is relaunched later + 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 + } + + /// Restores the checkbox state to the same as when the program last exited + 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; + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/MainForm.resx --- /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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + 17, 17 + + + 178, 17 + + + + + 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 + + + \ No newline at end of file diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Program.cs --- /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 + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Properties/AssemblyInfo.cs --- /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")] diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Properties/Resources.Designer.cs --- /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 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace ShortcutKeyFinder.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Properties/Resources.resx --- /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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Properties/Settings.Designer.cs --- /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 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +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; + } + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Properties/Settings.settings --- /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 @@ + + + + + + + diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Shortcut.cs --- /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 +{ + /// Class to perform operations on shortcut (.lnk) files + class Shortcut : INotifyPropertyChanged + { + private bool _hasDuplicateHotkey; + private Hotkey _hotkey; + public event PropertyChangedEventHandler PropertyChanged; + + /// Name of the shortcut as displayed by Windows Explorer + /// Not necessarily the same as the shortcut's filename + public string Name { get; private set; } + + /// Full path of the shortcut file + public string Path { get; private set; } + + /// Full path of the shortcut's parent directory + public string Location + { + get { return System.IO.Path.GetDirectoryName(Path); } + } + + /// Display name of the shortcut's parent directory + /// Gives the user a general idea of where the shortcut is located rather than the full path + 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"); + } + + } + + /// Hotkey associated with the shortcut + public Hotkey Hotkey + { + get { return _hotkey; } + set + { + if (value != _hotkey) + { + _hotkey = value; + NotifyPropertyChanged("Hotkey"); + } + } + } + + /// Icon associated with the shortcut + public Image Icon { get; private set; } + + /// Indicates whether this shortcut's hotkey is the same as that of another shortcut + /// Always false unless set to true by some other class + public bool HasDuplicateHotkey + { + get { return _hasDuplicateHotkey; } + set + { + if (value != _hasDuplicateHotkey) + { + _hasDuplicateHotkey = value; + NotifyPropertyChanged("HasDuplicateHotkey"); + } + } + } + + /// Indicates whether the user has permissions to modify the shortcut + /// Always false unless set to true by some other class + public bool ReadOnly { get; set; } + + /// Constructor + /// Full path to a shortcut (.lnk) file + public Shortcut(string path) + { + Path = path; + LoadShortcutInfo(); + } + + /// Implements the interface + private void NotifyPropertyChanged(String info) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(info)); + } + } + + /// Constructor + 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(); + } + } + + /// Shows the Windows Explorer properties window for the shortcut + /// Optional handle to a parent window for use when displaying error messages + public void ShowExplorerPropertiesWindow(IntPtr parentWindow = default(IntPtr)) + { + Win32Helpers.ShowFilePropertiesWindow(Path, parentWindow); + } + + /// Writes the shortcut data to disk + public void Save() + { + if (!Win32Helpers.SetShortcutHotkey(Path, Hotkey != null ? Hotkey.RawHotkey : (ushort)0)) + throw new UnauthorizedAccessException(); + } + + /// Determine whether two shortcuts are the same file + public override bool Equals(object obj) + { + Shortcut shortcut = obj as Shortcut; + return shortcut != null && shortcut.Path == Path; + } + + public override int GetHashCode() + { + return Path.GetHashCode(); + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/ShortcutKeyFinder.cs --- /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 +{ + /// Class to find all shortcuts that have hotkeys assigned + /// 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. + class ShortcutKeyFinder + { + private bool _showAll; + private List _allShortcuts = new List(); // List to hold all the shortcuts we find, regardless of whether they have hotkeys + + /// List of all shortcuts found in the search locations + /// If is true, shortcuts without assigned hotkeys are included + public BindingList Shortcuts { get; private set; } + + /// Indicates whether to include shortcuts without assigned hotkeys in the list of + public bool ShowAll + { + get { return _showAll; } + set + { + _showAll = value; + UpdateShortcutList(); + } + } + + /// Constructor + public ShortcutKeyFinder() + { + Shortcuts = new BindingList(); + } + + /// Scans the search locations for shortcut files + /// Whether to consider shortcuts owned by all users as read-only + 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(); + } + + /// Recursively scans a directory shortcut files + /// Directory to scan + /// Whether to consider the found shortcuts as read-only + /// The parameter exists for speed, to avoid checking permissions on every file. Good enough for our purposes. + 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 + } + + /// Updates the public list of with the results of the most recent scan + 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; + } + + /// Triggers a search for duplicate shortcuts when one of the shortcuts changes + void Shortcuts_ListChanged(object sender, ListChangedEventArgs e) + { + FindDuplicateShortcuts(); + } + + /// Scans for shortcuts that have duplicate hotkeys + private void FindDuplicateShortcuts() + { + Shortcuts.GroupBy(shortcut => shortcut.Hotkey).ToList().ForEach(group => group.ToList().ForEach(shortcut => shortcut.HasDuplicateHotkey = group.Count() > 1 && group.Key != null)); + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/ShortcutKeyFinder.csproj --- /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 @@ + + + + + Debug + AnyCPU + {0BFA5ACD-DDA4-4723-AFE2-3E66EECFEAB1} + WinExe + Properties + ShortcutKeyFinder + ShortcutKeyFinder + v4.0 + 512 + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + false + + + true + + + keyboard.ico + + + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + + + + MainForm.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + + + + + + + \ No newline at end of file diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/ShortcutKeyFinder.csproj.user --- /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 @@ + + + + publish\ + + + + + + en-US + false + + \ No newline at end of file diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/Win32Helpers.cs --- /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 +{ + /// Various functions to call the Win32 API + /// Method signatures taken from http://www.pinvoke.net/ + 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; + } + + /// IShellLink.GetPath fFlags: Flags that specify the type of path information to retrieve + [Flags()] + enum SLGP_FLAGS + { + /// Retrieves the standard short (8.3 format) file name + SLGP_SHORTPATH = 0x1, + /// Retrieves the Universal Naming Convention (UNC) path name of the file + SLGP_UNCPRIORITY = 0x2, + /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded + SLGP_RAWPATH = 0x4 + } + + /// IShellLink.Resolve fFlags + [Flags()] + enum SLR_FLAGS + { + /// + /// 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. + /// + SLR_NO_UI = 0x1, + /// Obsolete and no longer used + SLR_ANY_MATCH = 0x2, + /// 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. + SLR_UPDATE = 0x4, + /// Do not update the link information + SLR_NOUPDATE = 0x8, + /// Do not execute the search heuristics + SLR_NOSEARCH = 0x10, + /// Do not use distributed link tracking + SLR_NOTRACK = 0x20, + /// 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. + SLR_NOLINKINFO = 0x40, + /// Call the Microsoft Windows Installer + SLR_INVOKE_MSI = 0x80 + } + + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + /// Retrieves the list of item identifiers for a Shell link object + void GetIDList(out IntPtr ppidl); + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + void SetIDList(IntPtr pidl); + /// Retrieves the description string for a Shell link object + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// Sets the description for a Shell link object. The description can be any application-defined string + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// Retrieves the name of the working directory for a Shell link object + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// Sets the name of the working directory for a Shell link object + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// Retrieves the command-line arguments associated with a Shell link object + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// Sets the command-line arguments for a Shell link object + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// Retrieves the hot key for a Shell link object + void GetHotkey(out ushort pwHotkey); + /// Sets a hot key for a Shell link object + void SetHotkey(ushort wHotkey); + /// Retrieves the show command for a Shell link object + void GetShowCmd(out int piShowCmd); + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + void SetShowCmd(int iShowCmd); + /// Retrieves the location (path and index) of the icon for a Shell link object + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// Sets the location (path and index) of the icon for a Shell link object + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// Sets the relative path to the Shell link object + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + /// Sets the path and file name of a Shell link object + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + /// Retrieves the hotkey assigned to a shortcut (.lnk) file + /// Path to a shortcut (.lnk) file + /// A binary value representing the shortcut's hotkey + 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; + } + + /// Assigns a hotkey to a shortcut (.lnk) file + /// Path to an exising shortcut (.lnk) file to modify + /// Binary value containing the hotkey to set + 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; + }; + + /// Retrieves the hotkey assigned to a shortcut (.lnk) file + /// Path of the file to retreive properties from + /// Set to true to retreive the 16x16 icon. By default, the 32x32 icon is retrieved. + /// Set to true to remove icon overlays as displayed in Windows Explorer (for example, the shortcut arrow) + 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; + } + + /// Displays the Properties window of a file + /// File to display the properties window for + /// Handle to the parent window for error messages to be displayed + 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); + + /// Adds a UAC shield icon to a button + /// Button to add the shield icon to + 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 + } + + /// Class to hold basic file information + class FileInfo + { + public string DisplayName; + public Icon Icon; + + public FileInfo(string DisplayName, Icon Icon) + { + this.DisplayName = DisplayName; + this.Icon = Icon; + } + } +} diff -r 000000000000 -r 209d9210c18f ShortcutKeyFinder/keyboard.ico Binary file ShortcutKeyFinder/keyboard.ico has changed