diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs
index 7b3ae108..9fce6a8c 100644
--- a/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs
+++ b/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs
@@ -11,6 +11,7 @@
using LogExpert.Core.EventArguments;
using LogExpert.Dialogs;
using LogExpert.UI.Entities;
+using LogExpert.UI.Extensions;
namespace LogExpert.UI.Controls.LogWindow;
@@ -581,6 +582,28 @@ public void FollowTailChanged (bool isChecked, bool byTrigger)
SendGuiStateUpdate();
}
+ public void TryToTruncate ()
+ {
+ try
+ {
+ if (LockFinder.CheckIfFileIsLocked(Title))
+ {
+ var name = LockFinder.FindLockedProcessName(Title);
+ StatusLineText($"Truncate failed: file is locked by {name}");
+ }
+ else
+ {
+ File.WriteAllText(Title, "");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn($"Unexpected issue truncating file: {ex.Message}");
+ StatusLineText("Unexpected issue truncating file");
+ throw;
+ }
+ }
+
public void GotoLine (int line)
{
if (line >= 0)
diff --git a/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs b/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
index c65bc9a9..7aea14ed 100644
--- a/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
+++ b/src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs
@@ -17,7 +17,7 @@ internal partial class TimeSpreadingControl : UserControl
private Bitmap _bitmap = new(1, 1);
private int _displayHeight = 1;
- private readonly int _edgeOffset = (int)Win32.GetSystemMetricsForDpi(Win32.SM_CYVSCROLL);
+ private readonly int _edgeOffset = (int)NativeMethods.GetSystemMetricsForDpi(NativeMethods.SM_CYVSCROLL);
private int _lastMouseY;
private readonly object _monitor = new();
private int _rectHeight = 1;
diff --git a/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs b/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
index ddce747d..bee7a594 100644
--- a/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
+++ b/src/LogExpert.UI/Dialogs/ChooseIconDlg.cs
@@ -1,4 +1,4 @@
-using LogExpert.UI.Extensions;
+using LogExpert.UI.Extensions;
using System.Runtime.Versioning;
namespace LogExpert.UI.Dialogs;
@@ -38,7 +38,7 @@ private void FillIconList()
{
iconListView.Items.Clear();
- Icon[,] icons = Win32.ExtractIcons(FileName);
+ Icon[,] icons = NativeMethods.ExtractIcons(FileName);
if (icons == null)
{
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
index 254707aa..43742412 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
@@ -170,7 +170,7 @@ public LogTabWindow (string[] fileNames, int instanceNumber, bool showInstanceNu
public void ChangeTheme (Control.ControlCollection container)
{
ColorMode.LoadColorMode(ConfigManager.Settings.Preferences.DarkMode);
- Win32.UseImmersiveDarkMode(Handle, ColorMode.DarkModeEnabled);
+ NativeMethods.UseImmersiveDarkMode(Handle, ColorMode.DarkModeEnabled);
#region ApplyColorToAllControls
foreach (Control component in container)
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs
index 4030f93d..3821a872 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs
@@ -155,6 +155,7 @@ private void InitializeComponent()
tabRenameToolStripMenuItem = new ToolStripMenuItem();
copyPathToClipboardToolStripMenuItem = new ToolStripMenuItem();
findInExplorerToolStripMenuItem = new ToolStripMenuItem();
+ truncateFileToolStripMenuItem = new ToolStripMenuItem();
dragControlDateTime = new DateTimeDragControl();
statusStrip.SuspendLayout();
mainMenuStrip.SuspendLayout();
@@ -1053,7 +1054,7 @@ private void InitializeComponent()
//
tabContextMenuStrip.ForeColor = System.Drawing.SystemColors.ControlText;
tabContextMenuStrip.ImageScalingSize = new System.Drawing.Size(24, 24);
- tabContextMenuStrip.Items.AddRange(new ToolStripItem[] { closeThisTabToolStripMenuItem, closeOtherTabsToolStripMenuItem, closeAllTabsToolStripMenuItem, tabColorToolStripMenuItem, tabRenameToolStripMenuItem, copyPathToClipboardToolStripMenuItem, findInExplorerToolStripMenuItem });
+ tabContextMenuStrip.Items.AddRange(new ToolStripItem[] { closeThisTabToolStripMenuItem, closeOtherTabsToolStripMenuItem, closeAllTabsToolStripMenuItem, tabColorToolStripMenuItem, tabRenameToolStripMenuItem, copyPathToClipboardToolStripMenuItem, findInExplorerToolStripMenuItem, truncateFileToolStripMenuItem });
tabContextMenuStrip.Name = "tabContextMenuStrip";
tabContextMenuStrip.Size = new System.Drawing.Size(197, 158);
//
@@ -1114,6 +1115,14 @@ private void InitializeComponent()
findInExplorerToolStripMenuItem.ToolTipText = "Opens an Explorer window and selects the log file";
findInExplorerToolStripMenuItem.Click += OnFindInExplorerToolStripMenuItemClick;
//
+ // truncateFileToolStripMenuItem
+ //
+ this.truncateFileToolStripMenuItem.Name = "truncateFileToolStripMenuItem";
+ this.truncateFileToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
+ this.truncateFileToolStripMenuItem.Text = "Truncate File";
+ this.truncateFileToolStripMenuItem.ToolTipText = "Try to truncate the file opened in tab";
+ this.truncateFileToolStripMenuItem.Click += new System.EventHandler(this.TruncateFileToolStripMenuItem_Click);
+ //
// dragControlDateTime
//
dragControlDateTime.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
@@ -1236,6 +1245,7 @@ private void InitializeComponent()
private ToolStripButton toolStripButtonBubbles;
private ToolStripMenuItem copyPathToClipboardToolStripMenuItem;
private ToolStripMenuItem findInExplorerToolStripMenuItem;
+ private ToolStripMenuItem truncateFileToolStripMenuItem;
private ToolStripMenuItem exportBookmarksToolStripMenuItem;
private ToolStripComboBox groupsComboBoxHighlightGroups;
private ToolStripMenuItem debugToolStripMenuItem;
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowEventHandlers.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowEventHandlers.cs
index 3cb32910..3b0dc376 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowEventHandlers.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowEventHandlers.cs
@@ -847,7 +847,11 @@ private void OnFindInExplorerToolStripMenuItemClick (object sender, EventArgs e)
explorer.Start();
}
- [SupportedOSPlatform("windows")]
+ private void TruncateFileToolStripMenuItem_Click (object sender, EventArgs e)
+ {
+ CurrentLogWindow?.TryToTruncate();
+ }
+
private void OnExportBookmarksToolStripMenuItemClick (object sender, EventArgs e)
{
CurrentLogWindow?.ExportBookmarkList();
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPrivate.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPrivate.cs
index ec93bd35..68fcff65 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPrivate.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPrivate.cs
@@ -774,7 +774,7 @@ private Icon CreateLedIcon (int level, bool dirty, int tailState, int syncMode)
// a managed copy of icon. then the unmanaged win32 handle is destroyed
var iconHandle = bmp.GetHicon();
var icon = Icon.FromHandle(iconHandle).Clone() as Icon;
- Win32.DestroyIcon(iconHandle);
+ NativeMethods.DestroyIcon(iconHandle);
gfx.Dispose();
bmp.Dispose();
@@ -1033,7 +1033,7 @@ private void SetTabIcons (Preferences preferences)
[SupportedOSPlatform("windows")]
private void SetToolIcon (ToolEntry entry, ToolStripItem item)
{
- Icon icon = Win32.LoadIconFromExe(entry.IconFile, entry.IconIndex);
+ Icon icon = NativeMethods.LoadIconFromExe(entry.IconFile, entry.IconIndex);
if (icon != null)
{
item.Image = icon.ToBitmap();
@@ -1046,7 +1046,7 @@ private void SetToolIcon (ToolEntry entry, ToolStripItem item)
item.DisplayStyle = ToolStripItemDisplayStyle.Image;
}
- Win32.DestroyIcon(icon.Handle);
+ NativeMethods.DestroyIcon(icon.Handle);
icon.Dispose();
}
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPublic.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPublic.cs
index 4058efe4..eb4745ac 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPublic.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPublic.cs
@@ -297,7 +297,7 @@ public void SelectTab (ILogWindow logWindow)
[SupportedOSPlatform("windows")]
public void SetForeground ()
{
- Win32.SetForegroundWindow(Handle);
+ NativeMethods.SetForegroundWindow(Handle);
if (WindowState == FormWindowState.Minimized)
{
if (_wasMaximized)
diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs
index d6b20a7d..bdf5df26 100644
--- a/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs
+++ b/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs
@@ -558,12 +558,12 @@ private void DisplayCurrentIcon ()
{
if (_selectedTool != null)
{
- Icon icon = Win32.LoadIconFromExe(_selectedTool.IconFile, _selectedTool.IconIndex);
+ Icon icon = NativeMethods.LoadIconFromExe(_selectedTool.IconFile, _selectedTool.IconIndex);
if (icon != null)
{
Image image = icon.ToBitmap();
buttonIcon.Image = image;
- Win32.DestroyIcon(icon.Handle);
+ NativeMethods.DestroyIcon(icon.Handle);
icon.Dispose();
}
else
diff --git a/src/LogExpert.UI/Extensions/LockFinder.cs b/src/LogExpert.UI/Extensions/LockFinder.cs
new file mode 100644
index 00000000..ad4c74c7
--- /dev/null
+++ b/src/LogExpert.UI/Extensions/LockFinder.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+// Expanded with some helpers from: https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4/
+// Uses Windows Restart Manager.
+// A more involved and cross platform solution to this problem is here: https://github.com/cklutz/LockCheck
+
+
+namespace LogExpert.UI.Extensions;
+
+internal class LockFinder
+{
+
+ ///
+ /// Method FindLockedProcessName Retrieve the first process name
+ /// that is locking the file at the specified path
+ ///
+ /// The path of a file with a write lock held by a
+ /// process
+ /// The name of the first process found with a lock
+ ///
+ /// Thrown when the file path is not locked
+ ///
+ static public string FindLockedProcessName (string path)
+ {
+ var list = FindLockProcesses(path);
+ if (list.Count == 0)
+ {
+ throw new Exception(
+ "No processes are locking the path specified");
+ }
+ return list[0].ProcessName;
+ }
+
+ ///
+ /// Method CheckIfFileIsLocked Check if the file specified has a
+ /// write lock held by a process
+ ///
+ /// The path of a file being checked if a write lock
+ /// held by a process
+ /// true when one or more processes with lock
+ static public bool CheckIfFileIsLocked (string path)
+ {
+ var list = FindLockProcesses(path);
+ if (list.Count > 0)
+ { return true; }
+ return false;
+ }
+
+ ///
+ /// Used to find processes holding a lock on the file. This would cause
+ /// other usage, such as file truncation or write opretions to throw
+ /// IOException if an exclusive lock is attempted.
+ ///
+ /// Path being checked
+ /// List of processes holding file lock to path
+ ///
+ static public List FindLockProcesses (string path)
+ {
+ var key = Guid.NewGuid().ToString();
+ var processes = new List();
+
+ var res = NativeMethods.RmStartSession(out var handle, 0, key);
+ if (res != 0)
+ {
+ throw new Exception("Could not begin restart session. " +
+ "Unable to determine file locker.");
+ }
+
+ try
+ {
+ uint pnProcInfo = 0;
+ uint lpdwRebootReasons = NativeMethods.RmRebootReasonNone;
+ string[] resources = [path];
+
+ res = NativeMethods.RmRegisterResources(handle, (uint)resources.Length,
+ resources, 0, null, 0, null);
+ if (res != 0)
+ {
+ throw new Exception("Could not register resource.");
+ }
+ res = NativeMethods.RmGetList(handle, out var pnProcInfoNeeded, ref pnProcInfo, null,
+ ref lpdwRebootReasons);
+ const int ERROR_MORE_DATA = 234;
+ if (res == ERROR_MORE_DATA)
+ {
+ var processInfo =
+ new NativeMethods.RM_PROCESS_INFO[pnProcInfoNeeded];
+ pnProcInfo = pnProcInfoNeeded;
+ // Get the list.
+ res = NativeMethods.RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
+ if (res == 0)
+ {
+ processes = new List((int)pnProcInfo);
+ for (var i = 0; i < pnProcInfo; i++)
+ {
+ try
+ {
+ processes.Add(Process.GetProcessById(processInfo[i].
+ Process.dwProcessId));
+ }
+ catch (ArgumentException) { }
+ }
+ }
+ else
+ {
+ throw new Exception("Could not list processes locking resource");
+ }
+ }
+ else if (res != 0)
+ {
+ throw new Exception("Could not list processes locking resource." +
+ "Failed to get size of result.");
+ }
+ }
+ catch (Exception exception)
+ {
+ Trace.WriteLine(exception.Message);
+ }
+ finally
+ {
+ Trace.WriteLine($"RmEndSession: {NativeMethods.RmEndSession(handle)}");
+ }
+
+ return processes;
+ }
+}
diff --git a/src/LogExpert.UI/Extensions/NativeMethods.cs b/src/LogExpert.UI/Extensions/NativeMethods.cs
new file mode 100644
index 00000000..41d97fa1
--- /dev/null
+++ b/src/LogExpert.UI/Extensions/NativeMethods.cs
@@ -0,0 +1,228 @@
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace LogExpert.UI.Extensions;
+
+[SupportedOSPlatform("windows")]
+internal static partial class NativeMethods
+{
+ #region Fields
+
+ public const long SM_CYVSCROLL = 20;
+ public const long SM_CXHSCROLL = 21;
+ public const long SM_CXVSCROLL = 2;
+ public const long SM_CYHSCROLL = 3;
+ private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
+ private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
+ public const int RmRebootReasonNone = 0;
+ private const int CCH_RM_MAX_APP_NAME = 255;
+ private const int CCH_RM_MAX_SVC_NAME = 63;
+
+ #endregion
+
+ #region Structs
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RM_UNIQUE_PROCESS
+ {
+ public int dwProcessId;
+ public System.Runtime.InteropServices.
+ ComTypes.FILETIME ProcessStartTime;
+ }
+
+ [StructLayout(LayoutKind.Sequential,
+ CharSet = CharSet.Auto)]
+ public struct RM_PROCESS_INFO
+ {
+ public RM_UNIQUE_PROCESS Process;
+ [MarshalAs(UnmanagedType.ByValTStr,
+ SizeConst = CCH_RM_MAX_APP_NAME + 1)]
+ public string strAppName;
+ [MarshalAs(UnmanagedType.ByValTStr,
+ SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
+ public string strServiceShortName;
+ public RM_APP_TYPE ApplicationType;
+ public uint AppStatus;
+ public uint TSSessionId;
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool bRestartable;
+ }
+ #endregion Structs
+
+ #region Enums
+ public enum RM_APP_TYPE
+ {
+ RmUnknownApp = 0,
+ RmMainWindow = 1,
+ RmOtherWindow = 2,
+ RmService = 3,
+ RmExplorer = 4,
+ RmConsole = 5,
+ RmCritical = 1000
+ }
+
+ #endregion Enums
+
+ #region Library Imports
+
+ #region user32.dll Imports
+ [LibraryImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static partial bool DestroyIcon (nint hIcon);
+
+ [LibraryImport("User32.dll")]
+ public static partial int SetForegroundWindow (nint hWnd);
+
+ [LibraryImport("user32.dll")]
+ public static partial long GetSystemMetricsForDpi (long index);
+
+ [LibraryImport("user32.dll")]
+ public static partial long GetSystemMetrics (long index);
+
+ [LibraryImport("user32.dll")]
+ public static partial short GetKeyState (int vKey);
+
+ #endregion user32.dll Imports
+
+ #region shell32.dll Imports
+ /*
+ UINT ExtractIconEx(
+ LPCTSTR lpszFile,
+ int nIconIndex,
+ HICON *phiconLarge,
+ HICON *phiconSmall,
+ UINT nIcons
+ );
+ * */
+ [LibraryImport("shell32.dll", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial uint ExtractIconEx (
+ string fileName,
+ int iconIndex,
+ out nint iconsLarge,
+ out nint iconsSmall,
+ uint numIcons
+ );
+
+ #endregion shell32.dll Imports
+
+ #region dwmapi.dll Imports
+
+ #region TitleBarDarkMode
+ [LibraryImport("dwmapi.dll")]
+ public static partial int DwmSetWindowAttribute (nint hwnd, int attr, ref int attrValue, int attrSize);
+ #endregion TitleBarDarkMode
+
+ #endregion shell32.dll Imports
+
+ #region rstrtmgr.dll Imports
+
+ [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern int RmGetList (
+ uint dwSessionHandle,
+ out uint pnProcInfoNeeded,
+ ref uint pnProcInfo,
+ [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
+ ref uint lpdwRebootReasons);
+
+ [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern int RmRegisterResources (
+ uint pSessionHandle,
+ uint nFiles,
+ string[] rgsFilenames,
+ uint nApplications,
+ [In] RM_UNIQUE_PROCESS[] rgApplications,
+ uint nServices,
+ string[] rgsServiceNames);
+
+ [LibraryImport("rstrtmgr.dll", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial int RmStartSession (
+ out uint pSessionHandle,
+ int dwSessionFlags,
+ string strSessionKey);
+
+ [LibraryImport("rstrtmgr.dll", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial int RmEndSession (uint pSessionHandle);
+
+ #endregion rstrtmgr.dll Imports
+
+ #endregion Library Imports
+
+ #region Helper methods
+
+ public static Icon LoadIconFromExe (string fileName, int index)
+ {
+ nint smallIcons = new();
+ nint largeIcons = new();
+ int num = (int)ExtractIconEx(fileName, index, out largeIcons, out smallIcons, 1);
+ if (num > 0 && smallIcons != nint.Zero)
+ {
+ var icon = (Icon)Icon.FromHandle(smallIcons).Clone();
+ DestroyIcon(smallIcons);
+ return icon;
+ }
+ if (num > 0 && largeIcons != nint.Zero)
+ {
+ var icon = (Icon)Icon.FromHandle(largeIcons).Clone();
+ DestroyIcon(largeIcons);
+ return icon;
+ }
+ return null;
+ }
+
+ public static Icon[,] ExtractIcons (string fileName)
+ {
+ var iconCount = ExtractIconEx(fileName, -1, out var largeIcon, out var smallIcon, 0);
+ if (iconCount <= 0)
+ {
+ return null;
+ }
+
+ var result = new Icon[2, iconCount];
+
+ for (var i = 0; i < iconCount; ++i)
+ {
+ var num = ExtractIconEx(fileName, i, out var largeIcons, out var smallIcons, 1);
+ if (smallIcons != nint.Zero)
+ {
+ result[0, i] = (Icon)Icon.FromHandle(smallIcons).Clone();
+ DestroyIcon(smallIcons);
+ }
+ else
+ {
+ result[0, i] = null;
+ }
+
+ if (num > 0 && largeIcons != nint.Zero)
+ {
+ result[1, i] = (Icon)Icon.FromHandle(largeIcons).Clone();
+ DestroyIcon(largeIcons);
+ }
+ else
+ {
+ result[1, i] = null;
+ }
+ }
+
+ return result;
+ }
+
+ public static bool UseImmersiveDarkMode (nint handle, bool enabled)
+ {
+ var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
+ if (IsWindows10OrGreater(18985))
+ {
+ attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
+ }
+
+ var useImmersiveDarkMode = enabled ? 1 : 0;
+ return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
+
+ }
+
+ private static bool IsWindows10OrGreater (int build = -1)
+ {
+ return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
+ }
+
+ #endregion Helper methods
+}
\ No newline at end of file
diff --git a/src/LogExpert.UI/Extensions/Win32.cs b/src/LogExpert.UI/Extensions/Win32.cs
deleted file mode 100644
index a039e711..00000000
--- a/src/LogExpert.UI/Extensions/Win32.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System.Drawing;
-using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
-
-namespace LogExpert.UI.Extensions;
-
-[SupportedOSPlatform("windows")]
-internal static partial class Win32 //NativeMethods
-{
- #region Fields
-
- public const long SM_CYVSCROLL = 20;
- public const long SM_CXHSCROLL = 21;
- public const long SM_CXVSCROLL = 2;
- public const long SM_CYHSCROLL = 3;
- private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
- private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
-
- #endregion
-
- #region Library Imports
- [LibraryImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static partial bool DestroyIcon(nint hIcon);
-
- [LibraryImport("User32.dll")]
- public static partial int SetForegroundWindow(nint hWnd);
-
- [LibraryImport("user32.dll")]
- public static partial long GetSystemMetricsForDpi(long index);
-
- [LibraryImport("user32.dll")]
- public static partial long GetSystemMetrics(long index);
-
- [LibraryImport("user32.dll")]
- public static partial short GetKeyState(int vKey);
-
- /*
- UINT ExtractIconEx(
- LPCTSTR lpszFile,
- int nIconIndex,
- HICON *phiconLarge,
- HICON *phiconSmall,
- UINT nIcons
- );
- * */
- [LibraryImport("shell32.dll", StringMarshalling = StringMarshalling.Utf16)]
- public static partial uint ExtractIconEx(
- string fileName,
- int iconIndex,
- ref nint iconsLarge,
- ref nint iconsSmall,
- uint numIcons
- );
-
- #region TitleBarDarkMode
- [LibraryImport("dwmapi.dll")]
- public static partial int DwmSetWindowAttribute(nint hwnd, int attr, ref int attrValue, int attrSize);
- #endregion
- #endregion
-
- #region Public methods
-
- public static Icon LoadIconFromExe(string fileName, int index)
- {
- //IntPtr[] smallIcons = new IntPtr[1];
- //IntPtr[] largeIcons = new IntPtr[1];
- nint smallIcons = new();
- nint largeIcons = new();
- var num = (int)ExtractIconEx(fileName, index, ref largeIcons, ref smallIcons, 1);
- if (num > 0 && smallIcons != nint.Zero)
- {
- var icon = (Icon)Icon.FromHandle(smallIcons).Clone();
- DestroyIcon(smallIcons);
- return icon;
- }
- if (num > 0 && largeIcons != nint.Zero)
- {
- var icon = (Icon)Icon.FromHandle(largeIcons).Clone();
- DestroyIcon(largeIcons);
- return icon;
- }
- return null;
- }
-
- public static Icon[,] ExtractIcons(string fileName)
- {
- var smallIcon = nint.Zero;
- var largeIcon = nint.Zero;
- var iconCount = (int)ExtractIconEx(fileName, -1, ref largeIcon, ref smallIcon, 0);
- if (iconCount <= 0)
- {
- return null;
- }
-
- nint smallIcons = new();
- nint largeIcons = new();
- var result = new Icon[2, iconCount];
-
- for (var i = 0; i < iconCount; ++i)
- {
- var num = (int)ExtractIconEx(fileName, i, ref largeIcons, ref smallIcons, 1);
- if (smallIcons != nint.Zero)
- {
- result[0, i] = (Icon)Icon.FromHandle(smallIcons).Clone();
- DestroyIcon(smallIcons);
- }
- else
- {
- result[0, i] = null;
- }
- if (num > 0 && largeIcons != nint.Zero)
- {
- result[1, i] = (Icon)Icon.FromHandle(largeIcons).Clone();
- DestroyIcon(largeIcons);
- }
- else
- {
- result[1, i] = null;
- }
- }
- return result;
- }
-
- #endregion
-
- #region Private Methods
-
- public static bool UseImmersiveDarkMode(nint handle, bool enabled)
- {
-
- var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
- if (IsWindows10OrGreater(18985))
- {
- attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
- }
-
- var useImmersiveDarkMode = enabled ? 1 : 0;
- return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
-
- }
-
- private static bool IsWindows10OrGreater(int build = -1)
- {
- return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
- }
-
- #endregion TitleBarDarkMode
-
-}
\ No newline at end of file