This commit is contained in:
Markus Hofknecht 2020-04-30 13:22:47 +02:00
parent 53d8505a8e
commit 43755ae648
14 changed files with 1163 additions and 931 deletions

742
Business/Menus.cs Normal file
View file

@ -0,0 +1,742 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading;
using System.Windows.Forms;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.Handler;
using SystemTrayMenu.Helper;
using SystemTrayMenu.Utilities;
using Menu = SystemTrayMenu.UserInterface.Menu;
using Timer = System.Windows.Forms.Timer;
namespace SystemTrayMenu.Business
{
internal class Menus : IDisposable
{
internal event EventHandler<bool> LoadStarted;
internal event EventHandlerEmpty LoadStopped;
private enum OpenCloseState { Default, Opening, Closing };
private OpenCloseState openCloseState = OpenCloseState.Default;
private readonly Menu[] menus = new Menu[MenuDefines.MenusMax];
private readonly BackgroundWorker worker = new BackgroundWorker();
private readonly Screen screen = Screen.PrimaryScreen;
private readonly KeyboardInput keyboardInput;
private readonly Timer timerStillActiveCheck = new Timer();
private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose);
private DateTime deactivatedTime = DateTime.MinValue;
private IEnumerable<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed);
private List<Menu> AsList => AsEnumerable.ToList();
public Menus()
{
worker.WorkerSupportsCancellation = true;
worker.DoWork += Load;
void Load(object sender, DoWorkEventArgs e)
{
e.Result = GetData((BackgroundWorker)sender, Config.Path, 0);
}
worker.RunWorkerCompleted += LoadCompleted;
void LoadCompleted(object sender, RunWorkerCompletedEventArgs e)
{
keyboardInput.ResetSelectedByKey();
LoadStopped();
MenuData menuData = (MenuData)e.Result;
if (menuData.Validity == MenuDataValidity.Valid)
{
DisposeMenu(menus[0]);
menus[0] = Create(menuData, Path.GetFileName(Config.Path));
menus[0].AdjustLocationAndSize(screen);
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
}
}
keyboardInput = new KeyboardInput(menus);
keyboardInput.RegisterHotKey();
keyboardInput.HotKeyPressed += SwitchOpenClose;
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += CheckMenuOpenerStop;
keyboardInput.RowSelected += KeyboardInputRowSelected;
void KeyboardInputRowSelected(DataGridView dgv, int rowIndex)
{
FadeInIfNeeded();
CheckMenuOpenerStart(dgv, rowIndex);
}
waitLeave.LeaveTriggered += LeaveTriggered;
void LeaveTriggered() => FadeHalfOrOutIfNeeded();
}
internal void SwitchOpenClose()
{
if ((DateTime.Now - deactivatedTime).TotalMilliseconds < 200)
{
//By click on notifyicon the menu gets deactivated and closed
}
else if (string.IsNullOrEmpty(Config.Path))
{
//Case when Folder Dialog open
}
else if (openCloseState == OpenCloseState.Opening ||
menus[0].Visible && openCloseState == OpenCloseState.Default)
{
openCloseState = OpenCloseState.Closing;
MenusFadeOut();
StopWorker();
if (!AsEnumerable.Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
else
{
openCloseState = OpenCloseState.Opening;
LoadStarted(this, true);
StartWorker();
}
deactivatedTime = DateTime.MinValue;
}
public void Dispose()
{
worker.Dispose();
keyboardInput.Dispose();
timerStillActiveCheck.Dispose();
waitLeave.Dispose();
IconReader.Dispose();
DisposeMenu(menus[0]);
}
internal void DisposeMenu(Menu menuToDispose)
{
if (menuToDispose != null)
{
DataGridView dgv = menuToDispose.GetDataGridView();
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
rowData.Dispose();
DisposeMenu(rowData.SubMenu);
}
dgv.ClearSelection();
menuToDispose.Dispose();
}
}
internal static MenuData GetData(BackgroundWorker worker, string path, int level)
{
MenuData menuData = new MenuData
{
RowDatas = new List<RowData>(),
Validity = MenuDataValidity.Invalid,
Level = level
};
if (!worker.CancellationPending)
{
string[] directories = Array.Empty<string>();
try
{
directories = Directory.GetDirectories(path);
Array.Sort(directories, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string directory in directories)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(directory, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(directory, false);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
string resolvedLnkPath = string.Empty;
rowData.ReadIcon(true, ref resolvedLnkPath);
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
string[] files = Array.Empty<string>();
try
{
files = Directory.GetFiles(path);
Array.Sort(files, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string file in files)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(file, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(file, false);
string resolvedLnkPath = string.Empty;
if (rowData.ReadIcon(false, ref resolvedLnkPath))
{
rowData = ReadRowData(resolvedLnkPath, true, rowData);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
}
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
if (menuData.Validity == MenuDataValidity.Invalid)
{
menuData.Validity = MenuDataValidity.Valid;
}
}
return menuData;
}
internal void MainPreload()
{
menus[0] = Create(GetData(worker, Config.Path, 0),
Path.GetFileName(Config.Path));
menus[0].AdjustLocationAndSize(screen);
DisposeMenu(menus[0]);
}
internal void StartWorker()
{
if (worker.IsBusy)
{
LoadStopped();
}
else
{
worker.RunWorkerAsync();
}
}
internal void StopWorker()
{
if (worker.IsBusy)
{
worker.CancelAsync();
}
}
private static RowData ReadRowData(string fileName,
bool isResolvedLnk, RowData rowData = null)
{
if (rowData == null)
{
rowData = new RowData();
}
rowData.IsResolvedLnk = isResolvedLnk;
try
{
rowData.FileInfo = new FileInfo(fileName);
rowData.TargetFilePath = rowData.FileInfo.FullName;
if (!isResolvedLnk)
{
rowData.SetText(rowData.FileInfo.Name);
rowData.TargetFilePathOrig = rowData.FileInfo.FullName;
}
}
catch (Exception ex)
{
if (ex is SecurityException ||
ex is ArgumentException ||
ex is UnauthorizedAccessException ||
ex is PathTooLongException ||
ex is NotSupportedException)
{
Log.Warn($"fileName:'{fileName}'", ex);
}
else
{
throw;
}
}
return rowData;
}
private Menu Create(MenuData menuData, string title = null)
{
Menu menu = new Menu();
if (title != null)
{
if (string.IsNullOrEmpty(title))
{
title = Path.GetPathRoot(Config.Path);
}
menu.SetTitle(title);
menu.UserClickedOpenFolder += OpenFolder;
void OpenFolder()
{
Log.ProcessStart("explorer.exe", Config.Path);
}
}
menu.Level = menuData.Level;
menu.MouseWheel += AdjustSubMenusLocationAndSize;
menu.MouseLeave += waitLeave.Start;
menu.MouseEnter += waitLeave.Stop;
DataGridView dgv = menu.GetDataGridView();
dgv.CellMouseEnter += Dgv_CellMouseEnter;
dgv.CellMouseLeave += Dgv_CellMouseLeave;
dgv.MouseDown += Dgv_MouseDown;
dgv.MouseDoubleClick += Dgv_MouseDoubleClick;
dgv.SelectionChanged += Dgv_SelectionChanged;
menu.KeyPress += keyboardInput.KeyPress;
menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed;
menu.Deactivate += Deactivate;
void Deactivate(object sender, EventArgs e)
{
FadeHalfOrOutIfNeeded();
if (!IsActive())
{
deactivatedTime = DateTime.Now;
}
}
menu.Activated += Activated;
void Activated(object sender, EventArgs e)
{
if (IsActive() &&
menus[0].IsUsable)
{
menus[0].SetTitleColorActive();
AsList.ForEach(m => m.ShowWithFade());
}
CheckIfWindowsStartStoleFocusNoDeactivateInRareCase();
void CheckIfWindowsStartStoleFocusNoDeactivateInRareCase()
{
timerStillActiveCheck.Interval = 1000;
timerStillActiveCheck.Tick += StillActiveTick;
void StillActiveTick(object senderTimer, EventArgs eTimer)
{
if (!waitLeave.IsRunning)
{
FadeHalfOrOutIfNeeded();
if (!IsActive())
{
timerStillActiveCheck.Stop();
}
}
}
timerStillActiveCheck.Start();
}
}
menu.VisibleChanged += MenuVisibleChanged;
AddItemsToMenu(menuData.RowDatas, menu);
void AddItemsToMenu(List<RowData> data, Menu m)
{
foreach (RowData rowData in data)
{
CreateMenuRow(rowData, m);
}
}
return menu;
}
private void MenuVisibleChanged(object sender, EventArgs e)
{
Menu menu = (Menu)sender;
if (menu.IsUsable)
{
AdjustSubMenusLocationAndSize();
}
if (!menu.Visible)
{
DisposeMenu(menu);
}
if (!AsEnumerable.Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
private void Dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (menus[0].IsUsable)
{
if (keyboardInput.InUse)
{
CheckMenuOpenerStop(keyboardInput.iMenuKey,
keyboardInput.iRowKey);
keyboardInput.ClearIsSelectedByKey();
keyboardInput.InUse = false;
}
DataGridView dgv = (DataGridView)sender;
keyboardInput.Select(dgv, e.RowIndex);
CheckMenuOpenerStart(dgv, e.RowIndex);
}
}
private void CheckMenuOpenerStart(DataGridView dgv, int rowIndex)
{
if (rowIndex > -1 &&
dgv.Rows.Count > rowIndex)
{
RowData trigger = (RowData)dgv.Rows[rowIndex].Tag;
trigger.IsSelected = true;
dgv.Rows[rowIndex].Selected = true;
Menu menuFromTrigger = (Menu)dgv.FindForm();
Menu menuTriggered = trigger.SubMenu;
int level = menuFromTrigger.Level + 1;
if (trigger.ContainsMenu &&
level < MenuDefines.MenusMax &&
menus[0].IsUsable &&
(menus[level] == null ||
menus[level] != menuTriggered))
{
trigger.StopLoadMenuAndStartWaitToOpenIt();
trigger.StartMenuOpener();
if (trigger.Reading.IsBusy)
{
trigger.RestartLoading = true;
}
else
{
LoadStarted(this, false);
trigger.Reading.RunWorkerAsync(level);
}
}
}
}
private void CheckMenuOpenerStop(int menuIndex, int rowIndex, DataGridView dgv = null)
{
Menu menu = menus[menuIndex];
if (menu != null &&
rowIndex > -1)
{
if (dgv == null)
{
dgv = menu.GetDataGridView();
}
if (dgv.Rows.Count > rowIndex)
{
RowData trigger = (RowData)dgv.Rows[rowIndex].Tag;
if (trigger.Reading.IsBusy)
{
if (!trigger.IsContextMenuOpen)
{
trigger.IsSelected = false;
dgv.Rows[rowIndex].Selected = false;
}
trigger.Reading.CancelAsync();
}
else if (trigger.ContainsMenu && !trigger.IsLoading)
{
trigger.IsSelected = true;
dgv.Rows[rowIndex].Selected = true;
}
else
{
if (!trigger.IsContextMenuOpen)
{
trigger.IsSelected = false;
dgv.Rows[rowIndex].Selected = false;
}
}
if (trigger.IsLoading)
{
trigger.StopLoadMenuAndStartWaitToOpenIt();
trigger.IsLoading = false;
}
}
}
}
private void Dgv_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
if (!keyboardInput.InUse)
{
DataGridView dgv = (DataGridView)sender;
Menu menu = (Menu)dgv.FindForm();
CheckMenuOpenerStop(menu.Level, e.RowIndex, dgv);
}
}
#warning [Feature] Double Click to start item not only one click #66
private void Dgv_MouseDown(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag;
trigger.MouseDown(dgv, e);
}
}
private void Dgv_MouseDoubleClick(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag;
trigger.DoubleClick();
}
}
private void Dgv_SelectionChanged(object sender, EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
if (!menus[0].IsUsable)
{
row.DefaultCellStyle.SelectionBackColor = Color.White;
}
else if (rowData.IsSelectedByKeyboard)
{
row.DefaultCellStyle.SelectionBackColor =
MenuDefines.ColorSelectedItem;
row.Selected = true;
}
else if (rowData.IsSelected)
{
row.DefaultCellStyle.SelectionBackColor =
MenuDefines.ColorOpenFolder;
row.Selected = true;
}
else
{
rowData.IsSelected = false;
row.Selected = false;
}
}
}
private void CreateMenuRow(RowData rowData, Menu menu)
{
rowData.SetData(rowData, menu.GetDataGridView());
rowData.OpenMenu += OpenSubMenu;
rowData.Reading.WorkerSupportsCancellation = true;
rowData.Reading.DoWork += ReadMenu_DoWork;
void ReadMenu_DoWork(object senderDoWork,
DoWorkEventArgs eDoWork)
{
int level = (int)eDoWork.Argument;
BackgroundWorker worker = (BackgroundWorker)senderDoWork;
eDoWork.Result = Business.Menus.GetData(worker, rowData.TargetFilePath, level);
}
rowData.Reading.RunWorkerCompleted += ReadMenu_RunWorkerCompleted;
void ReadMenu_RunWorkerCompleted(object senderCompleted,
RunWorkerCompletedEventArgs e)
{
MenuData menuData = (MenuData)e.Result;
if (rowData.RestartLoading)
{
rowData.RestartLoading = false;
rowData.Reading.RunWorkerAsync(menuData.Level);
}
else
{
LoadStopped();
if (menuData.Validity != MenuDataValidity.Invalid)
{
menu = Create(menuData);
if (menuData.RowDatas.Count > 0)
{
menu.SetTypeSub();
}
else if (menuData.Validity == MenuDataValidity.NoAccess)
{
menu.SetTypeNoAccess();
}
else
{
menu.SetTypeEmpty();
}
menu.Tag = rowData;
rowData.SubMenu = menu;
rowData.MenuLoaded();
}
}
}
}
private void OpenSubMenu(object sender, RowData trigger)
{
Menu menuTriggered = trigger.SubMenu;
Menu menuFromTrigger = menus[menuTriggered.Level - 1];
for (int level = menuTriggered.Level;
level < MenuDefines.MenusMax; level++)
{
if (menus[level] != null)
{
Menu menuToClose = menus[level];
RowData oldTrigger = (RowData)menuToClose.Tag;
DataGridView dgv = menuFromTrigger.GetDataGridView();
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
rowData.IsSelected = false;
}
trigger.IsSelected = true;
dgv.ClearSelection();
dgv.Rows[trigger.RowIndex].Selected = true;
menuToClose.HideWithFade();
menuToClose.VisibleChanged += MenuVisibleChanged;
menus[level] = null;
}
}
DisposeMenu(menus[menuTriggered.Level]);
menus[menuTriggered.Level] = menuTriggered;
AdjustSubMenusLocationAndSize();
menus[menuTriggered.Level].ShowWithFadeOrTransparent(IsActive());
}
private void FadeInIfNeeded()
{
if (menus[0].IsUsable)
{
bool active = IsActive();
AsList.ForEach(menu => menu.ShowWithFadeOrTransparent(active));
}
}
internal void FadeHalfOrOutIfNeeded()
{
if (menus[0].IsUsable)
{
if (!(IsActive()))
{
Point position = Control.MousePosition;
if (AsList.Any(m => m.IsMouseOn(position)))
{
if (!keyboardInput.InUse)
{
AsList.ForEach(menu => menu.ShowTransparent());
}
}
else
{
MenusFadeOut();
}
}
}
}
private bool IsActive()
{
return Form.ActiveForm is Menu;
}
private void MenusFadeOut()
{
openCloseState = OpenCloseState.Closing;
AsList.ForEach(menu =>
{
if (menu.Level > 0)
{
menus[menu.Level] = null;
}
menu.HideWithFade();
});
}
private void AdjustSubMenusLocationAndSize()
{
int heightMax = screen.Bounds.Height -
new WindowsTaskbar().Size.Height;
Menu menuPredecessor = menus[0];
int widthPredecessors = -1; // -1 padding
bool directionToRight = false;
foreach (Menu menu in AsEnumerable.Where(m => m.Level > 0))
{
int newWith = (menu.Width -
menu.Padding.Horizontal + menuPredecessor.Width);
if (directionToRight)
{
if (widthPredecessors - menu.Width <=
-menu.Padding.Horizontal)
{
directionToRight = false;
}
else
{
widthPredecessors -= newWith;
}
}
else if (screen.Bounds.Width <
widthPredecessors + menuPredecessor.Width + menu.Width)
{
directionToRight = true;
widthPredecessors -= newWith;
}
menu.AdjustLocationAndSize(heightMax, menuPredecessor);
widthPredecessors += menu.Width - menu.Padding.Left;
menuPredecessor = menu;
}
}
}
}

View file

@ -1,111 +1,26 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security;
using System.Windows.Forms;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.Handler;
using SystemTrayMenu.Helper;
using SystemTrayMenu.Business;
using SystemTrayMenu.UserInterface;
using SystemTrayMenu.Utilities;
using Menu = SystemTrayMenu.UserInterface.Menu;
namespace SystemTrayMenu
{
internal class SystemTrayMenu : IDisposable
{
private enum OpenCloseState { Default, Opening, Closing };
private OpenCloseState openCloseState = OpenCloseState.Default;
private readonly MenuNotifyIcon menuNotifyIcon = null;
private readonly Menu[] menus = new Menu[MenuDefines.MenusMax];
private readonly BackgroundWorker worker = new BackgroundWorker();
private readonly Screen screen = Screen.PrimaryScreen;
private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose);
private readonly KeyboardInput keyboardInput;
private readonly Timer timerStillActiveCheck = new Timer();
private DateTime deactivatedTime = DateTime.Now;
private readonly MenuNotifyIcon menuNotifyIcon = new MenuNotifyIcon();
private Menus menus = new Menus();
public SystemTrayMenu()
{
AppRestart.BeforeRestarting += Dispose;
SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings;
waitLeave.LeaveTriggered += WaitLeave_LeaveTriggered;
void WaitLeave_LeaveTriggered()
{
FadeHalfOrOutIfNeeded();
}
keyboardInput = new KeyboardInput(menus);
keyboardInput.RegisterHotKey();
keyboardInput.HotKeyPressed += SwitchOpenClose;
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += CheckMenuOpenerStop;
keyboardInput.RowSelected += KeyboardInputRowSelected;
void KeyboardInputRowSelected(DataGridView dgv, int rowIndex)
{
keyboardInput.InUse = true;
FadeInIfNeeded();
CheckMenuOpenerStart(dgv, rowIndex);
}
menuNotifyIcon = new MenuNotifyIcon();
menus.LoadStarted += menuNotifyIcon.LoadingStart;
menus.LoadStopped += menuNotifyIcon.LoadingStop;
menuNotifyIcon.Exit += Application.Exit;
menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon;
menuNotifyIcon.HandleClick += SwitchOpenClose;
void SwitchOpenClose()
{
if (string.IsNullOrEmpty(Config.Path) ||
(DateTime.Now - deactivatedTime).TotalMilliseconds < 300)
{
//Case when Folder Dialog open or restarts to fast
}
else if (openCloseState == OpenCloseState.Opening ||
menus[0].Visible && openCloseState == OpenCloseState.Default)
{
openCloseState = OpenCloseState.Closing;
MenusFadeOut();
if (worker.IsBusy)
{
worker.CancelAsync();
}
if (!Menus().Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
else
{
openCloseState = OpenCloseState.Opening;
menuNotifyIcon.LoadingStart(true);
worker.RunWorkerAsync();
}
}
worker.WorkerSupportsCancellation = true;
worker.DoWork += Load;
void Load(object sender, DoWorkEventArgs e)
{
e.Result = ReadMenu((BackgroundWorker)sender, Config.Path, 0);
}
worker.RunWorkerCompleted += LoadCompleted;
void LoadCompleted(object sender, RunWorkerCompletedEventArgs e)
{
keyboardInput.ResetSelectedByKey();
menuNotifyIcon.LoadingStop();
MenuData menuData = (MenuData)e.Result;
if (menuData.Validity == MenuDataValidity.Valid)
{
DisposeMenu(menus[0]);
menus[0] = CreateMenu(menuData, Path.GetFileName(Config.Path));
menus[0].AdjustLocationAndSize(screen);
Menus().ToList().ForEach(m => { m.ShowWithFade(); });
}
}
menuNotifyIcon.Click += menus.SwitchOpenClose;
menuNotifyIcon.OpenLog += Log.OpenLogFile;
menuNotifyIcon.ChangeFolder += ChangeFolder;
void ChangeFolder()
@ -116,617 +31,13 @@ namespace SystemTrayMenu
}
}
menus[0] = CreateMenu(ReadMenu(worker, Config.Path, 0),
Path.GetFileName(Config.Path));
menus[0].AdjustLocationAndSize(screen);
DisposeMenu(menus[0]);
menus.MainPreload();
}
public void Dispose()
{
worker.Dispose();
keyboardInput.Dispose();
menus.Dispose();
menuNotifyIcon.Dispose();
waitLeave.Dispose();
timerStillActiveCheck.Dispose();
DisposeMenu(menus[0]);
IconReader.Dispose();
}
private void DisposeMenu(Menu menuToDispose)
{
if (menuToDispose != null)
{
DataGridView dgv = menuToDispose.GetDataGridView();
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
rowData.Dispose();
DisposeMenu(rowData.SubMenu);
}
dgv.ClearSelection();
menuToDispose.Dispose();
}
}
private static MenuData ReadMenu(BackgroundWorker worker, string path, int level)
{
MenuData menuData = new MenuData
{
RowDatas = new List<RowData>(),
Validity = MenuDataValidity.Invalid,
Level = level
};
if (!worker.CancellationPending)
{
string[] directories = Array.Empty<string>();
try
{
directories = Directory.GetDirectories(path);
Array.Sort(directories, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string directory in directories)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(directory, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(directory, false);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
string resolvedLnkPath = string.Empty;
rowData.ReadIcon(true, ref resolvedLnkPath);
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
string[] files = Array.Empty<string>();
try
{
files = Directory.GetFiles(path);
Array.Sort(files, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string file in files)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(file, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(file, false);
string resolvedLnkPath = string.Empty;
if (rowData.ReadIcon(false, ref resolvedLnkPath))
{
rowData = ReadRowData(resolvedLnkPath, true, rowData);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
}
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
if (menuData.Validity == MenuDataValidity.Invalid)
{
menuData.Validity = MenuDataValidity.Valid;
}
}
return menuData;
}
private static RowData ReadRowData(string fileName,
bool isResolvedLnk, RowData rowData = null)
{
if (rowData == null)
{
rowData = new RowData();
}
rowData.IsResolvedLnk = isResolvedLnk;
try
{
rowData.FileInfo = new FileInfo(fileName);
rowData.TargetFilePath = rowData.FileInfo.FullName;
if (!isResolvedLnk)
{
rowData.SetText(rowData.FileInfo.Name);
rowData.TargetFilePathOrig = rowData.FileInfo.FullName;
}
}
catch (Exception ex)
{
if (ex is SecurityException ||
ex is ArgumentException ||
ex is UnauthorizedAccessException ||
ex is PathTooLongException ||
ex is NotSupportedException)
{
Log.Warn($"fileName:'{fileName}'", ex);
}
else
{
throw;
}
}
return rowData;
}
private void CheckMenuOpenerStart(DataGridView dgv, int rowIndex)
{
if (rowIndex > -1 &&
dgv.Rows.Count > rowIndex)
{
RowData trigger = (RowData)dgv.Rows[rowIndex].Tag;
trigger.IsSelected = true;
dgv.Rows[rowIndex].Selected = true;
Menu menuFromTrigger = (Menu)dgv.FindForm();
Menu menuTriggered = trigger.SubMenu;
int level = menuFromTrigger.Level + 1;
if (trigger.ContainsMenu &&
level < MenuDefines.MenusMax &&
menus[0].IsUsable &&
(menus[level] == null ||
menus[level] != menuTriggered))
{
trigger.StopLoadMenuAndStartWaitToOpenIt();
trigger.StartMenuOpener();
if (trigger.Reading.IsBusy)
{
trigger.RestartLoading = true;
}
else
{
menuNotifyIcon.LoadingStart();
trigger.Reading.RunWorkerAsync(level);
}
}
}
}
private void CheckMenuOpenerStop(int menuIndex, int rowIndex, DataGridView dgv = null)
{
Menu menu = menus[menuIndex];
if (menu != null &&
rowIndex > -1)
{
if (dgv == null)
{
dgv = menu.GetDataGridView();
}
if (dgv.Rows.Count > rowIndex)
{
RowData trigger = (RowData)dgv.Rows[rowIndex].Tag;
if (trigger.Reading.IsBusy)
{
if (!trigger.IsContextMenuOpen)
{
trigger.IsSelected = false;
dgv.Rows[rowIndex].Selected = false;
}
trigger.Reading.CancelAsync();
}
else if (trigger.ContainsMenu && !trigger.IsLoading)
{
trigger.IsSelected = true;
dgv.Rows[rowIndex].Selected = true;
}
else
{
if (!trigger.IsContextMenuOpen)
{
trigger.IsSelected = false;
dgv.Rows[rowIndex].Selected = false;
}
}
if (trigger.IsLoading)
{
trigger.StopLoadMenuAndStartWaitToOpenIt();
trigger.IsLoading = false;
}
}
}
}
private Menu CreateMenu(MenuData menuData, string title = null)
{
Menu menu = new Menu();
if (title != null)
{
if (string.IsNullOrEmpty(title))
{
title = Path.GetPathRoot(Config.Path);
}
menu.SetTitle(title);
menu.UserClickedOpenFolder += OpenFolder;
void OpenFolder()
{
Log.ProcessStart("explorer.exe", Config.Path);
}
}
menu.Level = menuData.Level;
menu.MouseWheel += AdjustSubMenusLocationAndSize;
menu.MouseLeave += waitLeave.Start;
menu.MouseEnter += waitLeave.Stop;
DataGridView dgv = menu.GetDataGridView();
dgv.CellMouseEnter += Dgv_CellMouseEnter;
dgv.CellMouseLeave += Dgv_CellMouseLeave;
dgv.MouseDown += Dgv_MouseDown;
dgv.MouseDoubleClick += Dgv_MouseDoubleClick;
dgv.SelectionChanged += Dgv_SelectionChanged;
menu.KeyPress += keyboardInput.KeyPress;
menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed;
menu.Deactivate += Deactivate;
void Deactivate(object sender, EventArgs e)
{
if (!FadeHalfOrOutIfNeeded())
{
deactivatedTime = DateTime.Now;
}
}
menu.Activated += Activated;
void Activated(object sender, EventArgs e)
{
if (Form.ActiveForm is Menu &&
menus[0].IsUsable)
{
menus[0].SetTitleColorActive();
Menus().ToList().ForEach(m => m.ShowWithFade());
}
CheckIfWindowsStartStoleFocusNoDeactivateInRareCase();
void CheckIfWindowsStartStoleFocusNoDeactivateInRareCase()
{
timerStillActiveCheck.Interval = 1000;
timerStillActiveCheck.Tick += StillActiveTick;
void StillActiveTick(object senderTimer, EventArgs eTimer)
{
if (!waitLeave.IsRunning &&
!FadeHalfOrOutIfNeeded())
{
timerStillActiveCheck.Stop();
}
}
timerStillActiveCheck.Start();
}
}
menu.VisibleChanged += MenuVisibleChanged;
AddItemsToMenu(menuData.RowDatas, menu);
void AddItemsToMenu(List<RowData> data, Menu m)
{
foreach (RowData rowData in data)
{
CreateMenuRow(rowData, m);
}
}
return menu;
}
private void Dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (menus[0].IsUsable)
{
if (keyboardInput.InUse)
{
CheckMenuOpenerStop(keyboardInput.iMenuKey,
keyboardInput.iRowKey);
keyboardInput.ClearIsSelectedByKey();
keyboardInput.InUse = false;
}
DataGridView dgv = (DataGridView)sender;
keyboardInput.Select(dgv, e.RowIndex);
CheckMenuOpenerStart(dgv, e.RowIndex);
}
}
private void Dgv_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
if (!keyboardInput.InUse)
{
DataGridView dgv = (DataGridView)sender;
Menu menu = (Menu)dgv.FindForm();
CheckMenuOpenerStop(menu.Level, e.RowIndex, dgv);
}
}
#warning [Feature] Double Click to start item not only one click #66
private void Dgv_MouseDown(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag;
trigger.MouseDown(dgv, e);
}
}
private void Dgv_MouseDoubleClick(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag;
trigger.DoubleClick();
}
}
private void Dgv_SelectionChanged(object sender, EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
if (!menus[0].IsUsable)
{
row.DefaultCellStyle.SelectionBackColor = Color.White;
}
else if (rowData.IsSelectedByKeyboard)
{
row.DefaultCellStyle.SelectionBackColor =
MenuDefines.ColorSelectedItem;
row.Selected = true;
}
else if (rowData.IsSelected)
{
row.DefaultCellStyle.SelectionBackColor =
MenuDefines.ColorOpenFolder;
row.Selected = true;
}
else
{
rowData.IsSelected = false;
row.Selected = false;
}
}
}
private void CreateMenuRow(RowData rowData, Menu menu)
{
rowData.SetData(rowData, menu.GetDataGridView());
rowData.OpenMenu += OpenSubMenu;
rowData.Reading.WorkerSupportsCancellation = true;
rowData.Reading.DoWork += ReadMenu_DoWork;
void ReadMenu_DoWork(object senderDoWork,
DoWorkEventArgs eDoWork)
{
int level = (int)eDoWork.Argument;
BackgroundWorker worker = (BackgroundWorker)senderDoWork;
eDoWork.Result = ReadMenu(worker, rowData.TargetFilePath, level);
}
rowData.Reading.RunWorkerCompleted += ReadMenu_RunWorkerCompleted;
void ReadMenu_RunWorkerCompleted(object senderCompleted,
RunWorkerCompletedEventArgs e)
{
MenuData menuData = (MenuData)e.Result;
if (rowData.RestartLoading)
{
rowData.RestartLoading = false;
rowData.Reading.RunWorkerAsync(menuData.Level);
}
else
{
menuNotifyIcon.LoadingStop();
if (menuData.Validity != MenuDataValidity.Invalid)
{
menu = CreateMenu(menuData);
if (menuData.RowDatas.Count > 0)
{
menu.SetTypeSub();
}
else if (menuData.Validity == MenuDataValidity.NoAccess)
{
menu.SetTypeNoAccess();
}
else
{
menu.SetTypeEmpty();
}
menu.Tag = rowData;
rowData.SubMenu = menu;
rowData.MenuLoaded();
}
}
}
}
private void OpenSubMenu(object sender, RowData trigger)
{
Menu menuTriggered = trigger.SubMenu;
Menu menuFromTrigger = menus[menuTriggered.Level - 1];
for (int level = menuTriggered.Level;
level < MenuDefines.MenusMax; level++)
{
if (menus[level] != null)
{
Menu menuToClose = menus[level];
RowData oldTrigger = (RowData)menuToClose.Tag;
DataGridView dgv = menuFromTrigger.GetDataGridView();
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Tag;
rowData.IsSelected = false;
}
trigger.IsSelected = true;
dgv.ClearSelection();
dgv.Rows[trigger.RowIndex].Selected = true;
menuToClose.HideWithFade();
menuToClose.VisibleChanged += MenuVisibleChanged;
menus[level] = null;
}
}
DisposeMenu(menus[menuTriggered.Level]);
menus[menuTriggered.Level] = menuTriggered;
AdjustSubMenusLocationAndSize();
menus[menuTriggered.Level].ShowWithFadeOrTransparent(
Form.ActiveForm is Menu);
}
private void FadeInIfNeeded()
{
if (menus[0].IsUsable)
{
bool isActive = Form.ActiveForm is Menu;
Menus().ToList().ForEach(
menu => menu.ShowWithFadeOrTransparent(isActive));
}
}
private bool FadeHalfOrOutIfNeeded()
{
bool menuActive = false;
if (menus[0].IsUsable)
{
menuActive = Form.ActiveForm is Menu;
if (!menuActive)
{
Point position = Control.MousePosition;
if (Menus().Any(m => m.IsMouseOn(position)))
{
if (!keyboardInput.InUse)
{
Menus().ToList().ForEach(
menu => menu.ShowTransparent());
}
}
else
{
MenusFadeOut();
}
}
}
return menuActive;
}
private void MenusFadeOut()
{
openCloseState = OpenCloseState.Closing;
Menus().ToList().ForEach(menu =>
{
if (menu.Level > 0)
{
menus[menu.Level] = null;
}
menu.HideWithFade();
});
}
private void MenuVisibleChanged(object sender, EventArgs e)
{
Menu menu = (Menu)sender;
if (menu.IsUsable)
{
AdjustSubMenusLocationAndSize();
}
if (!menu.Visible)
{
DisposeMenu(menu);
}
if (!Menus().Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
private void AdjustSubMenusLocationAndSize()
{
int heightMax = screen.Bounds.Height -
new WindowsTaskbar().Size.Height;
Menu menuPredecessor = menus[0];
int widthPredecessors = -1; // -1 padding
bool directionToRight = false;
foreach (Menu menu in Menus().Where(m => m.Level > 0))
{
int newWith = (menu.Width -
menu.Padding.Horizontal + menuPredecessor.Width);
if (directionToRight)
{
if (widthPredecessors - menu.Width <=
-menu.Padding.Horizontal)
{
directionToRight = false;
}
else
{
widthPredecessors -= newWith;
}
}
else if (screen.Bounds.Width <
widthPredecessors + menuPredecessor.Width + menu.Width)
{
directionToRight = true;
widthPredecessors -= newWith;
}
menu.AdjustLocationAndSize(heightMax, menuPredecessor);
widthPredecessors += menu.Width - menu.Padding.Left;
menuPredecessor = menu;
}
}
private IEnumerable<Menu> Menus()
{
return menus.Where(m => m != null && !m.IsDisposed);
}
}
}

View file

@ -160,7 +160,7 @@ namespace SystemTrayMenu.DataClasses
ref string resolvedLnkPath)
{
bool handled = false;
resolvedLnkPath = LnkHelper.ResolveShortcut(TargetFilePath);
resolvedLnkPath = LnkHelper.GetResolvedFileName(TargetFilePath);
if (LnkHelper.IsDirectory(resolvedLnkPath))
{
Icon = IconReader.GetFolderIcon(TargetFilePath,

View file

@ -15,7 +15,7 @@ namespace SystemTrayMenu.UserInterface
private const int Interval60FPS = 16; //60fps=>1s/60fps=~16.6ms
private const double StepIn = 0.20;
private const double StepOut = 0.05;
private const double StepOut = 0.10;
private const double Transparent = 0.80;
private const double TransparentMinus = 0.60; //Transparent - StepIn
private const double TransparentPlus = 0.85; //Transparent + StepOut

View file

@ -1,5 +1,6 @@
using System;
using System.Windows.Forms;
using SystemTrayMenu.Utilities;
namespace SystemTrayMenu.Helper
{
@ -65,20 +66,35 @@ namespace SystemTrayMenu.Helper
};
}
/// <summary>
/// Registers a hot key in the system.
/// </summary>
/// <param name="key">The key itself that is associated with the hot key.</param>
internal void RegisterHotKey(Keys key)
{
uint keyModifiersNone = 0;
RegisterHotKey(keyModifiersNone, key);
}
/// <summary>
/// Registers a hot key in the system.
/// </summary>
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
/// <param name="key">The key itself that is associated with the hot key.</param>
internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
{
RegisterHotKey((uint)modifier, key);
}
private void RegisterHotKey(uint modifier, Keys key)
{
_currentId = _currentId + 1;
if (!DllImports.NativeMethods.User32RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
if (!DllImports.NativeMethods.User32RegisterHotKey(
_window.Handle, _currentId, modifier, (uint)key))
{
#pragma warning disable CA1303 // Do not pass literals as localized parameters
throw new InvalidOperationException("Couldnt register the hot key.");
#pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK
throw new InvalidOperationException(
Language.Translate("Could not register the hot key."));
}
}

View file

@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
TAMAHO SystemTrayMenu - an improved Windows toolbar
Copyright (C) 2019 Markus Hofknecht
Copyright (C) 2020 Markus Hofknecht
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ You can contact me by mail Markus@Hofknecht.eu
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
TAMAHO SystemTrayMenu Copyright (C) 2019 Markus Hofknecht
TAMAHO SystemTrayMenu Copyright (C) 2020 Markus Hofknecht
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View file

@ -35,5 +35,5 @@ using System.Runtime.InteropServices;
// 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("0.9.2.22")]
[assembly: AssemblyFileVersion("0.9.2.22")]
[assembly: AssemblyVersion("0.9.2.23")]
[assembly: AssemblyFileVersion("0.9.2.23")]

View file

@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
@ -13,12 +13,12 @@ namespace SystemTrayMenu.Resources {
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
// 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", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
@ -33,7 +33,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// 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 {
@ -47,8 +47,8 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// 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 {
@ -61,7 +61,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die (e.g. F10) ähnelt.
/// Looks up a localized string similar to (e.g. F10).
/// </summary>
internal static string _e_g__F10_ {
get {
@ -70,7 +70,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die About ähnelt.
/// Looks up a localized string similar to About.
/// </summary>
internal static string About {
get {
@ -79,7 +79,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Activate autostart ähnelt.
/// Looks up a localized string similar to Activate autostart.
/// </summary>
internal static string Activate_autostart {
get {
@ -88,7 +88,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die ALT ähnelt.
/// Looks up a localized string similar to ALT.
/// </summary>
internal static string ALT {
get {
@ -97,7 +97,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Autostart ähnelt.
/// Looks up a localized string similar to Autostart.
/// </summary>
internal static string Autostart {
get {
@ -106,7 +106,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Details ähnelt.
/// Looks up a localized string similar to Details.
/// </summary>
internal static string buttonDetails {
get {
@ -115,7 +115,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die OK ähnelt.
/// Looks up a localized string similar to OK.
/// </summary>
internal static string buttonOk {
get {
@ -124,7 +124,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die System Info ähnelt.
/// Looks up a localized string similar to System Info.
/// </summary>
internal static string buttonSystemInfo {
get {
@ -133,7 +133,16 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die CTRL ähnelt.
/// Looks up a localized string similar to Couldnt register the hot key..
/// </summary>
internal static string Could_not_register_the_hot_key_ {
get {
return ResourceManager.GetString("Could not register the hot key.", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to CTRL.
/// </summary>
internal static string CTRL {
get {
@ -142,7 +151,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die English ähnelt.
/// Looks up a localized string similar to English.
/// </summary>
internal static string English {
get {
@ -151,7 +160,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Exit ähnelt.
/// Looks up a localized string similar to Exit.
/// </summary>
internal static string Exit {
get {
@ -160,7 +169,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Folder ähnelt.
/// Looks up a localized string similar to Folder.
/// </summary>
internal static string Folder {
get {
@ -169,7 +178,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Folder empty ähnelt.
/// Looks up a localized string similar to Folder empty.
/// </summary>
internal static string Folder_empty {
get {
@ -178,7 +187,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Folder inaccessible ähnelt.
/// Looks up a localized string similar to Folder inaccessible.
/// </summary>
internal static string Folder_inaccessible {
get {
@ -187,7 +196,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Deutsch ähnelt.
/// Looks up a localized string similar to Deutsch.
/// </summary>
internal static string German {
get {
@ -196,7 +205,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Move the NotifyIcon per DragDrop from the SystemTray into the Taskbar ähnelt.
/// Looks up a localized string similar to Move the NotifyIcon per DragDrop from the SystemTray into the Taskbar.
/// </summary>
internal static string HintDragDropText {
get {
@ -205,7 +214,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die SystemTrayMenu - Hint ähnelt.
/// Looks up a localized string similar to SystemTrayMenu - Hint.
/// </summary>
internal static string HintDragDropTitle {
get {
@ -214,7 +223,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Language ähnelt.
/// Looks up a localized string similar to Language.
/// </summary>
internal static string Language {
get {
@ -223,7 +232,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Log File ähnelt.
/// Looks up a localized string similar to Log File.
/// </summary>
internal static string Log_File {
get {
@ -232,7 +241,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Restart ähnelt.
/// Looks up a localized string similar to Restart.
/// </summary>
internal static string Restart {
get {
@ -241,7 +250,7 @@ namespace SystemTrayMenu.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Shortcut key ähnelt.
/// Looks up a localized string similar to Shortcut key.
/// </summary>
internal static string Shortcut_key {
get {

View file

@ -180,4 +180,7 @@
<data name="Shortcut key" xml:space="preserve">
<value>Tastenkombination</value>
</data>
<data name="Could not register the hot key." xml:space="preserve">
<value>Der Tastenkürzel konnte nicht registriert werden.</value>
</data>
</root>

View file

@ -180,4 +180,7 @@
<data name="Shortcut key" xml:space="preserve">
<value>Shortcut key</value>
</data>
<data name="Could not register the hot key." xml:space="preserve">
<value>Couldnt register the hot key.</value>
</data>
</root>

View file

@ -138,6 +138,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Business\Menus.cs" />
<Compile Include="Config\Config.cs" />
<Compile Include="NativeDllImport\DestroyMenu.cs" />
<Compile Include="NativeDllImport\CreatePopupMenu.cs" />
@ -177,6 +178,7 @@
<Compile Include="Utilities\FolderOptions.cs" />
<Compile Include="Helpers\KeyboardHook.cs" />
<Compile Include="Utilities\Log.cs" />
<Compile Include="Utilities\Shares.cs" />
<Compile Include="Utilities\SingleAppInstance.cs" />
<Compile Include="Utilities\Language.cs" />
<Compile Include="Utilities\Scaling.cs" />
@ -260,10 +262,7 @@
</ItemGroup>
<ItemGroup>
<None Include="Resources\White50Percentage.ico" />
<None Include="Resources\STM.ico" />
<None Include="Resources\STM_black.ico" />
<None Include="Resources\Selected.png" />
<None Include="Resources\hintDragDrop.png" />
<None Include="Resources\L010.ico" />
<None Include="Resources\L020.ico" />
<None Include="Resources\L030.ico" />

View file

@ -12,7 +12,7 @@ namespace SystemTrayMenu.UserInterface
{
internal class MenuNotifyIcon : IDisposable
{
public event EventHandlerEmpty HandleClick;
public event EventHandlerEmpty Click;
public event EventHandlerEmpty ChangeFolder;
public event EventHandlerEmpty OpenLog;
public event EventHandlerEmpty Restart;
@ -69,7 +69,6 @@ namespace SystemTrayMenu.UserInterface
{
VerifyClick(e);
}
notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick;
void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
@ -81,7 +80,7 @@ namespace SystemTrayMenu.UserInterface
{
if (e.Button == MouseButtons.Left)
{
HandleClick.Invoke();
Click?.Invoke();
}
}
@ -92,7 +91,7 @@ namespace SystemTrayMenu.UserInterface
load.Dispose();
}
public void LoadingStart(bool reset = false)
public void LoadingStart(object sender = null, bool reset = false)
{
if (reset)
{

View file

@ -2,6 +2,7 @@
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.DllImports;
@ -35,6 +36,7 @@ namespace SystemTrayMenu.UserInterface
private readonly Fading fading = new Fading();
private bool autoResizeRowsDone = false;
private bool isShowing = false;
internal Menu()
{
@ -50,18 +52,22 @@ namespace SystemTrayMenu.UserInterface
{
try
{
isShowing = true;
Visible = true;
isShowing = false;
}
catch (ObjectDisposedException)
{
Visible = false;
Log.Info($"Could not open menu, menu already closed," +
isShowing = false;
Log.Info($"Could not open menu, old menu was disposing," +
$" IsDisposed={IsDisposed}");
}
if (Visible)
{
Activate();
NativeMethods.User32ShowInactiveTopmost(this);
NativeMethods.ForceForegroundWindow(Handle);
SetTitleColorActive();
}
@ -201,7 +207,10 @@ namespace SystemTrayMenu.UserInterface
internal void HideWithFade()
{
fading.Fade(Fading.FadingState.Hide);
if(!isShowing)
{
fading.Fade(Fading.FadingState.Hide);
}
}
internal void AdjustLocationAndSize(Screen screen)

View file

@ -1,188 +1,329 @@
using System;
using Shell32;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace SystemTrayMenu.Utilities
{
internal class LnkHelper
{
[Flags()]
private enum SLGP_FLAGS
public static string GetResolvedFileName(string shortcutFilename)
{
/// <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
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public long ftCreationTime;
public long ftLastAccessTime;
public long 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;
}
[Flags()]
private 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")]
private 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 short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short 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);
}
[ComImport, Guid("0000010c-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersist
{
[PreserveSig]
void GetClassID(out Guid pClassID);
}
[ComImport, Guid("0000010b-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistFile : IPersist
{
new void GetClassID(out Guid pClassID);
[PreserveSig]
int IsDirty();
[PreserveSig]
void Load([In, MarshalAs(UnmanagedType.LPWStr)]
string pszFileName, uint dwMode);
[PreserveSig]
void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
[In, MarshalAs(UnmanagedType.Bool)] bool fRemember);
[PreserveSig]
void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
[PreserveSig]
void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
}
private const uint STGM_READ = 0;
private const int MAX_PATH = 260;
// CLSID_ShellLink from ShlGuid.h
[
ComImport(),
Guid("00021401-0000-0000-C000-000000000046")
]
public class ShellLink
{
}
public static string ResolveShortcut(string filename)
{
ShellLink link = new ShellLink();
#pragma warning disable CA2010 // Always consume the value returned by methods marked with PreserveSigAttribute
((IPersistFile)link).Load(filename, STGM_READ);
#pragma warning restore CA2010 // => Has no returned value => OK
StringBuilder sb = new StringBuilder(MAX_PATH);
WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0);
string resolvedPath = sb.ToString();
if (!IsDirectory(resolvedPath) &&
!File.Exists(resolvedPath))
string resolvedFilename = string.Empty;
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
//For some lnk e.g. WinRar SkypeForBuisness
//resolved path wrong to Program Files (x86)
resolvedPath = ReplaceFirst(resolvedPath,
@"\Program Files (x86)\",
@"\Program Files\");
if (!File.Exists(resolvedPath))
resolvedFilename = GetShortcutFileNamePath(shortcutFilename);
}
else
{
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
void StaThreadMethod(object obj)
{
resolvedPath = string.Empty;
resolvedFilename = GetShortcutFileNamePath(shortcutFilename);
}
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start(shortcutFilename);
staThread.Join();
}
return resolvedFilename;
}
private static string GetShortcutFileNamePath(object shortcutFilename)
{
string resolvedFilename = string.Empty;
string pathOnly = Path.GetDirectoryName((string)shortcutFilename);
string filenameOnly = Path.GetFileName((string)shortcutFilename);
Shell shell = new Shell();
Folder folder = shell.NameSpace(pathOnly);
FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
ShellLinkObject link = (ShellLinkObject)folderItem.GetLink;
if (string.IsNullOrEmpty(link.Path))
{
resolvedFilename = link.Target.Path;
}
else
{
resolvedFilename = link.Path;
}
}
return resolvedPath;
return resolvedFilename;
}
// [Flags()]
// private 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
// }
// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
// private struct WIN32_FIND_DATAW
// {
// public uint dwFileAttributes;
// public long ftCreationTime;
// public long ftLastAccessTime;
// public long 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;
// }
// [Flags()]
// private 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")]
// private 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 short pwHotkey);
// /// <summary>Sets a hot key for a Shell link object</summary>
// void SetHotkey(short 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);
// }
// [ComImport, Guid("0000010c-0000-0000-c000-000000000046"),
// InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
// public interface IPersist
// {
// [PreserveSig]
// void GetClassID(out Guid pClassID);
// }
// [ComImport, Guid("0000010b-0000-0000-C000-000000000046"),
// InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
// public interface IPersistFile : IPersist
// {
// new void GetClassID(out Guid pClassID);
// [PreserveSig]
// int IsDirty();
// [PreserveSig]
// void Load([In, MarshalAs(UnmanagedType.LPWStr)]
// string pszFileName, uint dwMode);
// [PreserveSig]
// void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
// [In, MarshalAs(UnmanagedType.Bool)] bool fRemember);
// [PreserveSig]
// void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
// [PreserveSig]
// void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
// }
// private const uint STGM_READ = 0;
// private const int MAX_PATH = 260;
// // CLSID_ShellLink from ShlGuid.h
// [
// ComImport(),
// Guid("00021401-0000-0000-C000-000000000046")
// ]
// public class ShellLink
// {
// }
// public static string ResolveShortcut(string filename)
// {
// ShellLink link = new ShellLink();
//#pragma warning disable CA2010 // Always consume the value returned by methods marked with PreserveSigAttribute
// ((IPersistFile)link).Load(filename, STGM_READ);
//#pragma warning restore CA2010 // => Has no returned value => OK
// StringBuilder sb = new StringBuilder(MAX_PATH);
// WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
// ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0);
// string resolvedPath = sb.ToString();
// if (!IsDirectory(resolvedPath) &&
// !File.Exists(resolvedPath))
// {
// //For some lnk e.g. WinRar SkypeForBuisness
// //resolved path wrong to Program Files (x86)
//#warning Using Shell32 with this method only allow in single thread application. You have to include [STAThread] in main entry point.
// resolvedPath = ReplaceFirst(resolvedPath,
// @"\Program Files (x86)\",
// @"\Program Files\");
// if (!File.Exists(resolvedPath))
// {
// resolvedPath = string.Empty;
// //Shell32.Folder folder = GetShell32NameSpaceFolder(filename as object);
// //string name1 = string.Empty;
// //string path1 = string.Empty;
// //string description1 = string.Empty;
// //string working_dir1 = string.Empty;
// //string args1 = string.Empty;
// ////string test2 = name1 + path1 + description1 + working_dir1 + args1 + test1;
// //object[] args = new object[] { filename };
// //if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
// //{
// // //string test1 =
// // GetShortcutInfo(filename);
// //}
// //else
// //{
// // Thread staThread = new Thread(new ParameterizedThreadStart(GetShortcutInfo));
// // staThread.SetApartmentState(ApartmentState.STA);
// // staThread.Start(args);
// // staThread.Join();
// //}
// }
// }
// return resolvedPath;
// }
// //public static Shell32.Folder GetShell32NameSpaceFolder(Object folder)
// //{
// // Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
// // Object shell = Activator.CreateInstance(shellAppType);
// // return (Shell32.Folder)shellAppType.InvokeMember("NameSpace",
// // System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder }, CultureInfo.InvariantCulture);
// //}
// //TESTING
// // Get information about this link.
// // Return an error message if there's a problem.
// //static void GetShortcutInfo(object parameters)
// //{
// // object[] args = (object[])parameters;
// // string full_name = (string)args[0];
// // string name = "";
// // string path = "";
// // string descr = "";
// // string working_dir = "";
// // //string args = "";
// // try
// // {
// // // Make a Shell object.
// // Shell32.Shell shell = new Shell32.Shell();
// // // Get the shortcut's folder and name.
// // string shortcut_path =
// // full_name.Substring(0, full_name.LastIndexOf("\\"));
// // string shortcut_name =
// // full_name.Substring(full_name.LastIndexOf("\\") + 1);
// // if (!shortcut_name.EndsWith(".lnk"))
// // shortcut_name += ".lnk";
// // // Get the shortcut's folder.
// // Shell32.Folder shortcut_folder =
// // shell.NameSpace(shortcut_path);
// // // Get the shortcut's file.
// // Shell32.FolderItem folder_item =
// // shortcut_folder.Items().Item(shortcut_name);
// // if (folder_item == null)
// // {
// // //return "Cannot find shortcut file '" + full_name + "'";
// // }
// // if (!folder_item.IsLink)
// // {
// // //return "File '" + full_name + "' isn't a shortcut.";
// // }
// // // Display the shortcut's information.
// // Shell32.ShellLinkObject lnk =
// // (Shell32.ShellLinkObject)folder_item.GetLink;
// // name = folder_item.Name;
// // descr = lnk.Description;
// // path = lnk.Path;
// // working_dir = lnk.WorkingDirectory;
// // //args = lnk.Arguments;
// // //return "";
// // }
// // catch (Exception ex)
// // {
// // //return ex.Message;
// // }
// //}
public static bool IsDirectory(string filePath)
{
bool isDirectory = false;
@ -198,15 +339,15 @@ namespace SystemTrayMenu.Utilities
return isDirectory;
}
public static string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search, StringComparison.InvariantCulture);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace +
text.Substring(pos + search.Length);
}
// public static string ReplaceFirst(string text, string search, string replace)
// {
// int pos = text.IndexOf(search, StringComparison.InvariantCulture);
// if (pos < 0)
// {
// return text;
// }
// return text.Substring(0, pos) + replace +
// text.Substring(pos + search.Length);
// }
}
}