diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props
index c97a124..9aad919 100644
--- a/Source/Directory.Build.props
+++ b/Source/Directory.Build.props
@@ -16,8 +16,7 @@
true
-
- false
+ true
diff --git a/Source/Launchbar.sln b/Source/Launchbar.sln
index 2f5b708..569730e 100644
--- a/Source/Launchbar.sln
+++ b/Source/Launchbar.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
+# 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Launchbar", "Launchbar\Launchbar.csproj", "{55B26A32-A0F3-4E58-8B0F-D992D199069F}"
diff --git a/Source/Launchbar/App.xaml.cs b/Source/Launchbar/App.xaml.cs
index 82b5c49..03328ba 100644
--- a/Source/Launchbar/App.xaml.cs
+++ b/Source/Launchbar/App.xaml.cs
@@ -10,10 +10,7 @@
namespace Launchbar;
-///
-/// Interaction logic for App.xaml
-///
-public sealed partial class App : Application
+public sealed partial class App : Application, IDisposable
{
#if DEBUG
private const string MutexName = @"Global\LaunchbarSingleInstanceMutex(Debug)";
@@ -25,11 +22,7 @@ public sealed partial class App : Application
private static SplashScreen? splashScreen;
- // ReSharper disable NotAccessedField.Local
-#pragma warning disable IDE0052 // Remove unread private members - We need to keep the mutex alive during the lifetime of the app.
private readonly Mutex instanceMutex;
-#pragma warning restore IDE0052 // Remove unread private members
- // ReSharper restore NotAccessedField.Local
private WindowBar? barLeft;
@@ -109,7 +102,7 @@ public App()
setting.PropertyChanged += this.settingsPropertyChanged;
- this.contextMenu = this.FindResource("contextMenuTemplate") as ContextMenu ?? throw new InvalidOperationException("Missing 'contextMenuTemplate'.");
+ this.contextMenu = this.FindResource("ContextMenuTemplate") as ContextMenu ?? throw new InvalidOperationException("Missing 'contextMenuTemplate'.");
this.forceContextMenuLayout();
try
@@ -286,4 +279,16 @@ public static ContextMenu RequestContextMenu()
// Attach to this object.
return app.contextMenu;
}
+
+ protected override void OnExit(ExitEventArgs e)
+ {
+ base.OnExit(e);
+
+ this.instanceMutex.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.instanceMutex.Dispose();
+ }
}
\ No newline at end of file
diff --git a/Source/Launchbar/Launchbar.csproj b/Source/Launchbar/Launchbar.csproj
index e5ea1ba..c4df4bd 100644
--- a/Source/Launchbar/Launchbar.csproj
+++ b/Source/Launchbar/Launchbar.csproj
@@ -6,13 +6,13 @@
net8.0-windows
true
WinExe
+ true
Resources\Next.ico
- 5.2.0-preview.1
+ 5.2.0-preview.2
Launchbar
RunIT to the next level.
Copyright © Mertsch $([System.DateTime]::UtcNow.Year)
- true
true
@@ -20,10 +20,10 @@
-
-
+
+
-
+
diff --git a/Source/Launchbar/Menu.cs b/Source/Launchbar/Menu.cs
index 6435620..c0a65df 100644
--- a/Source/Launchbar/Menu.cs
+++ b/Source/Launchbar/Menu.cs
@@ -11,14 +11,14 @@ public sealed class Menu : NotifyBase
{
#region Fields
- private MenuEntryCollection entries;
+ private MenuEntryCollection? entries;
#endregion
///
/// Gets or sets a list of menu entries.
///
- public MenuEntryCollection Entries
+ public MenuEntryCollection? Entries
{
get { return this.entries; }
set
diff --git a/Source/Launchbar/MenuEntryAdvanced.cs b/Source/Launchbar/MenuEntryAdvanced.cs
index 1135827..8c1f2ff 100644
--- a/Source/Launchbar/MenuEntryAdvanced.cs
+++ b/Source/Launchbar/MenuEntryAdvanced.cs
@@ -1,7 +1,6 @@
-using System.Text;
+using Launchbar.Win32;
using System.Windows.Media;
using System.Xml.Serialization;
-using Launchbar.Win32;
namespace Launchbar;
@@ -150,7 +149,7 @@ public void UpdateIcon()
{
if (p.IsValidFile) // Valid file?
{
- newIcon = WinHelper.ExtractAssociatedIcon(p.PathAbsolute);
+ newIcon = WinHelper.ExtractAssociatedIcon(p.PathAbsolute!); // IsValidFile tests the path
newType = IconType.Custom;
}
else if (p.IsValidPath) // Valid dictionary?
@@ -181,18 +180,13 @@ public void ChooseIcon()
{
if (this is Program { IsValidFile: true } p) // When the program path is valid, use that path as default.
{
- path = p.Path;
+ path = p.Path!; // IsValidFile tests the path
}
}
- // We need to have a string that is long enough to handle a more complex path than
- // the default one (more characters in length).
- StringBuilder sb = new StringBuilder(path, 4096); // 4095 + null-char should be enough
-
- // Methods returns one when pressing OK in the dialog.
- if (SafeNativeMethods.PickIconDlg(nint.Zero, sb, (uint)sb.Capacity, ref index) == 1)
+ if (WinHelper.PickIconDialog(ref path, ref index))
{
- this.IconPath = sb.ToString(); //save the information
+ this.IconPath = path;
this.IconIndex = index;
}
this.UpdateIcon();
diff --git a/Source/Launchbar/Win32/SafeNativeMethods.cs b/Source/Launchbar/Win32/LibraryImport.cs
similarity index 95%
rename from Source/Launchbar/Win32/SafeNativeMethods.cs
rename to Source/Launchbar/Win32/LibraryImport.cs
index b751e5f..d65e55a 100644
--- a/Source/Launchbar/Win32/SafeNativeMethods.cs
+++ b/Source/Launchbar/Win32/LibraryImport.cs
@@ -1,6 +1,4 @@
using System.Runtime.InteropServices;
-using System.Security;
-using System.Text;
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global
@@ -9,8 +7,7 @@
namespace Launchbar.Win32;
-[SuppressUnmanagedCodeSecurity]
-internal static partial class SafeNativeMethods
+internal static partial class LibraryImport
{
internal const string User32 = @"user32.dll";
@@ -94,8 +91,8 @@ public static nint SetWindowLongPtr(nint hWnd, int nIndex, nint dwNewLong)
/// [in, out] A pointer to an integer that, on entry, specified the index of
/// the initial selection. On exit, the integer specifies the index of the icon that was selected.
/// Returns 1 if successful; otherwise, 0.
- [DllImport(Shell32, EntryPoint = "PickIconDlg", CharSet = CharSet.Unicode)]
- public static extern int PickIconDlg(nint hwnd, StringBuilder pszIconPath, uint cchIconPath, ref int piIconIndex);
+ [LibraryImport(Shell32, EntryPoint = "PickIconDlg", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial int PickIconDlg(nint hwnd, [In, Out] char[] pszIconPath, uint cchIconPath, ref int piIconIndex);
///
/// Destroys an icon and frees any memory the icon occupied.
diff --git a/Source/Launchbar/Win32/WinHelper.cs b/Source/Launchbar/Win32/WinHelper.cs
index a411516..e57a6dd 100644
--- a/Source/Launchbar/Win32/WinHelper.cs
+++ b/Source/Launchbar/Win32/WinHelper.cs
@@ -20,11 +20,11 @@ internal static class WinHelper
public static BitmapSource? ExtractAssociatedIcon(string path)
{
int i = 0;
- nint hIcon = SafeNativeMethods.ExtractAssociatedIcon(nint.Zero, path, ref i);
+ nint hIcon = LibraryImport.ExtractAssociatedIcon(nint.Zero, path, ref i);
if (hIcon != nint.Zero)
{
BitmapSource bms = Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, null);
- SafeNativeMethods.DestroyIcon(hIcon);
+ LibraryImport.DestroyIcon(hIcon);
return bms;
}
return null;
@@ -39,11 +39,11 @@ internal static class WinHelper
/// The extracted icon as .
public static BitmapSource? ExtractIcon(string path, int index)
{
- nint hIcon = SafeNativeMethods.ExtractIcon(nint.Zero, path, (uint)index);
+ nint hIcon = LibraryImport.ExtractIcon(nint.Zero, path, (uint)index);
if (hIcon != nint.Zero)
{
BitmapSource bms = Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, null);
- SafeNativeMethods.DestroyIcon(hIcon);
+ LibraryImport.DestroyIcon(hIcon);
return bms;
}
return null;
@@ -55,21 +55,21 @@ internal static class WinHelper
///
public static void SendMouseButtonDown(MouseButton button)
{
- SafeNativeMethods.MOUSEINPUT mi = new SafeNativeMethods.MOUSEINPUT
+ LibraryImport.MOUSEINPUT mi = new LibraryImport.MOUSEINPUT
{
dwFlags = button switch
{
- MouseButton.Left => SafeNativeMethods.MOUSEINPUTFLAGS.MOUSEEVENTF_LEFTDOWN,
- MouseButton.Right => SafeNativeMethods.MOUSEINPUTFLAGS.MOUSEEVENTF_RIGHTDOWN,
+ MouseButton.Left => LibraryImport.MOUSEINPUTFLAGS.MOUSEEVENTF_LEFTDOWN,
+ MouseButton.Right => LibraryImport.MOUSEINPUTFLAGS.MOUSEEVENTF_RIGHTDOWN,
_ => throw new NotSupportedException(),
},
};
- SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT mkhInput = new SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT { mi = mi };
+ LibraryImport.MOUSEKEYBDHARDWAREINPUT mkhInput = new LibraryImport.MOUSEKEYBDHARDWAREINPUT { mi = mi };
- SafeNativeMethods.INPUT input = new SafeNativeMethods.INPUT { type = SafeNativeMethods.INPUT_TYPE.MOUSE, mkhi = mkhInput };
+ LibraryImport.INPUT input = new LibraryImport.INPUT { type = LibraryImport.INPUT_TYPE.MOUSE, mkhi = mkhInput };
- SafeNativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
+ LibraryImport.SendInput(1, ref input, Marshal.SizeOf(input));
}
///
@@ -78,21 +78,21 @@ public static void SendMouseButtonDown(MouseButton button)
///
public static void SendMouseButtonUp(MouseButton button)
{
- SafeNativeMethods.MOUSEINPUT mi = new SafeNativeMethods.MOUSEINPUT
+ LibraryImport.MOUSEINPUT mi = new LibraryImport.MOUSEINPUT
{
dwFlags = button switch
{
- MouseButton.Left => SafeNativeMethods.MOUSEINPUTFLAGS.MOUSEEVENTF_LEFTUP,
- MouseButton.Right => SafeNativeMethods.MOUSEINPUTFLAGS.MOUSEEVENTF_RIGHTUP,
+ MouseButton.Left => LibraryImport.MOUSEINPUTFLAGS.MOUSEEVENTF_LEFTUP,
+ MouseButton.Right => LibraryImport.MOUSEINPUTFLAGS.MOUSEEVENTF_RIGHTUP,
_ => throw new NotSupportedException(),
},
};
- SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT mkhInput = new SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT { mi = mi };
+ LibraryImport.MOUSEKEYBDHARDWAREINPUT mkhInput = new LibraryImport.MOUSEKEYBDHARDWAREINPUT { mi = mi };
- SafeNativeMethods.INPUT input = new SafeNativeMethods.INPUT { type = SafeNativeMethods.INPUT_TYPE.MOUSE, mkhi = mkhInput };
+ LibraryImport.INPUT input = new LibraryImport.INPUT { type = LibraryImport.INPUT_TYPE.MOUSE, mkhi = mkhInput };
- SafeNativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
+ LibraryImport.SendInput(1, ref input, Marshal.SizeOf(input));
}
///
@@ -101,35 +101,50 @@ public static void SendMouseButtonUp(MouseButton button)
///
public static void SendMouseMoveRelative(int x, int y)
{
- SafeNativeMethods.MOUSEINPUT mi = new SafeNativeMethods.MOUSEINPUT
+ LibraryImport.MOUSEINPUT mi = new LibraryImport.MOUSEINPUT
{
- dwFlags = SafeNativeMethods.MOUSEINPUTFLAGS.MOUSEEVENTF_MOVE,
+ dwFlags = LibraryImport.MOUSEINPUTFLAGS.MOUSEEVENTF_MOVE,
dx = x,
dy = y,
};
- SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT mkhInput = new SafeNativeMethods.MOUSEKEYBDHARDWAREINPUT
+ LibraryImport.MOUSEKEYBDHARDWAREINPUT mkhInput = new LibraryImport.MOUSEKEYBDHARDWAREINPUT
{
mi = mi,
};
- SafeNativeMethods.INPUT input = new SafeNativeMethods.INPUT
+ LibraryImport.INPUT input = new LibraryImport.INPUT
{
- type = SafeNativeMethods.INPUT_TYPE.MOUSE,
+ type = LibraryImport.INPUT_TYPE.MOUSE,
mkhi = mkhInput,
};
- SafeNativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
+ LibraryImport.SendInput(1, ref input, Marshal.SizeOf(input));
}
public static void SetAsToolWindow(this Window window)
{
nint handle = new WindowInteropHelper(window).EnsureHandle();
- nint oldFlags = SafeNativeMethods.GetWindowLongPtr(handle, GWL.GWL_EXSTYLE);
+ nint oldFlags = LibraryImport.GetWindowLongPtr(handle, GWL.GWL_EXSTYLE);
if (oldFlags != nint.Zero)
{
nint newFlags = new nint(oldFlags.ToInt64() | ExtendedWindowStyles.WS_EX_TOOLWINDOW);
- SafeNativeMethods.SetWindowLongPtr(handle, GWL.GWL_EXSTYLE, newFlags);
+ LibraryImport.SetWindowLongPtr(handle, GWL.GWL_EXSTYLE, newFlags);
}
}
+
+ public static bool PickIconDialog(ref string path, ref int index)
+ {
+ // We need to have a string that is long enough to handle a more complex path than the default one (more characters in length).
+ char[] pathBuffer = new char[32 * 1024]; // https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
+ path.CopyTo(pathBuffer);
+
+ // Method returns one when pressing OK in the dialog.
+ if (LibraryImport.PickIconDlg(nint.Zero, pathBuffer, (uint)pathBuffer.Length, ref index) == 1)
+ {
+ path = new string(pathBuffer, 0, Array.IndexOf(pathBuffer, '\0')); // Extract string from null terminated string.
+ return true;
+ }
+ return false;
+ }
}
\ No newline at end of file
diff --git a/Source/Launchbar/WindowSettings.xaml.cs b/Source/Launchbar/WindowSettings.xaml.cs
index b36fcbd..107cf61 100644
--- a/Source/Launchbar/WindowSettings.xaml.cs
+++ b/Source/Launchbar/WindowSettings.xaml.cs
@@ -243,7 +243,7 @@ private void addMenuEntry(MenuEntry newEntry)
}
else
{
- Settings.Default.Menu.Entries.Add(newEntry);
+ Settings.Default.Menu.Entries?.Add(newEntry);
}
}