Skip to content

Commit 41d56a6

Browse files
authored
Merge pull request #334 from RandallFlagg/truncate-file-menu-option
added truncate file option to context menu - .net 8
2 parents fb54dcb + 1cbe8bf commit 41d56a6

12 files changed

Lines changed: 406 additions & 162 deletions

src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using LogExpert.Core.EventArguments;
1212
using LogExpert.Dialogs;
1313
using LogExpert.UI.Entities;
14+
using LogExpert.UI.Extensions;
1415

1516
namespace LogExpert.UI.Controls.LogWindow;
1617

@@ -581,6 +582,28 @@ public void FollowTailChanged (bool isChecked, bool byTrigger)
581582
SendGuiStateUpdate();
582583
}
583584

585+
public void TryToTruncate ()
586+
{
587+
try
588+
{
589+
if (LockFinder.CheckIfFileIsLocked(Title))
590+
{
591+
var name = LockFinder.FindLockedProcessName(Title);
592+
StatusLineText($"Truncate failed: file is locked by {name}");
593+
}
594+
else
595+
{
596+
File.WriteAllText(Title, "");
597+
}
598+
}
599+
catch (Exception ex)
600+
{
601+
_logger.Warn($"Unexpected issue truncating file: {ex.Message}");
602+
StatusLineText("Unexpected issue truncating file");
603+
throw;
604+
}
605+
}
606+
584607
public void GotoLine (int line)
585608
{
586609
if (line >= 0)

src/LogExpert.UI/Controls/LogWindow/TimeSpreadigControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal partial class TimeSpreadingControl : UserControl
1717

1818
private Bitmap _bitmap = new(1, 1);
1919
private int _displayHeight = 1;
20-
private readonly int _edgeOffset = (int)Win32.GetSystemMetricsForDpi(Win32.SM_CYVSCROLL);
20+
private readonly int _edgeOffset = (int)NativeMethods.GetSystemMetricsForDpi(NativeMethods.SM_CYVSCROLL);
2121
private int _lastMouseY;
2222
private readonly object _monitor = new();
2323
private int _rectHeight = 1;

src/LogExpert.UI/Dialogs/ChooseIconDlg.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using LogExpert.UI.Extensions;
1+
using LogExpert.UI.Extensions;
22
using System.Runtime.Versioning;
33

44
namespace LogExpert.UI.Dialogs;
@@ -38,7 +38,7 @@ private void FillIconList()
3838
{
3939
iconListView.Items.Clear();
4040

41-
Icon[,] icons = Win32.ExtractIcons(FileName);
41+
Icon[,] icons = NativeMethods.ExtractIcons(FileName);
4242

4343
if (icons == null)
4444
{

src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public LogTabWindow (string[] fileNames, int instanceNumber, bool showInstanceNu
170170
public void ChangeTheme (Control.ControlCollection container)
171171
{
172172
ColorMode.LoadColorMode(ConfigManager.Settings.Preferences.DarkMode);
173-
Win32.UseImmersiveDarkMode(Handle, ColorMode.DarkModeEnabled);
173+
NativeMethods.UseImmersiveDarkMode(Handle, ColorMode.DarkModeEnabled);
174174

175175
#region ApplyColorToAllControls
176176
foreach (Control component in container)

src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.designer.cs

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowEventHandlers.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,11 @@ private void OnFindInExplorerToolStripMenuItemClick (object sender, EventArgs e)
847847
explorer.Start();
848848
}
849849

850-
[SupportedOSPlatform("windows")]
850+
private void TruncateFileToolStripMenuItem_Click (object sender, EventArgs e)
851+
{
852+
CurrentLogWindow?.TryToTruncate();
853+
}
854+
851855
private void OnExportBookmarksToolStripMenuItemClick (object sender, EventArgs e)
852856
{
853857
CurrentLogWindow?.ExportBookmarkList();

src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPrivate.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ private Icon CreateLedIcon (int level, bool dirty, int tailState, int syncMode)
774774
// a managed copy of icon. then the unmanaged win32 handle is destroyed
775775
var iconHandle = bmp.GetHicon();
776776
var icon = Icon.FromHandle(iconHandle).Clone() as Icon;
777-
Win32.DestroyIcon(iconHandle);
777+
NativeMethods.DestroyIcon(iconHandle);
778778

779779
gfx.Dispose();
780780
bmp.Dispose();
@@ -1033,7 +1033,7 @@ private void SetTabIcons (Preferences preferences)
10331033
[SupportedOSPlatform("windows")]
10341034
private void SetToolIcon (ToolEntry entry, ToolStripItem item)
10351035
{
1036-
Icon icon = Win32.LoadIconFromExe(entry.IconFile, entry.IconIndex);
1036+
Icon icon = NativeMethods.LoadIconFromExe(entry.IconFile, entry.IconIndex);
10371037
if (icon != null)
10381038
{
10391039
item.Image = icon.ToBitmap();
@@ -1046,7 +1046,7 @@ private void SetToolIcon (ToolEntry entry, ToolStripItem item)
10461046
item.DisplayStyle = ToolStripItemDisplayStyle.Image;
10471047
}
10481048

1049-
Win32.DestroyIcon(icon.Handle);
1049+
NativeMethods.DestroyIcon(icon.Handle);
10501050
icon.Dispose();
10511051
}
10521052

src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindowPublic.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public void SelectTab (ILogWindow logWindow)
297297
[SupportedOSPlatform("windows")]
298298
public void SetForeground ()
299299
{
300-
Win32.SetForegroundWindow(Handle);
300+
NativeMethods.SetForegroundWindow(Handle);
301301
if (WindowState == FormWindowState.Minimized)
302302
{
303303
if (_wasMaximized)

src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,12 +558,12 @@ private void DisplayCurrentIcon ()
558558
{
559559
if (_selectedTool != null)
560560
{
561-
Icon icon = Win32.LoadIconFromExe(_selectedTool.IconFile, _selectedTool.IconIndex);
561+
Icon icon = NativeMethods.LoadIconFromExe(_selectedTool.IconFile, _selectedTool.IconIndex);
562562
if (icon != null)
563563
{
564564
Image image = icon.ToBitmap();
565565
buttonIcon.Image = image;
566-
Win32.DestroyIcon(icon.Handle);
566+
NativeMethods.DestroyIcon(icon.Handle);
567567
icon.Dispose();
568568
}
569569
else
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Runtime.InteropServices;
5+
6+
// Expanded with some helpers from: https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4/
7+
// Uses Windows Restart Manager.
8+
// A more involved and cross platform solution to this problem is here: https://github.com/cklutz/LockCheck
9+
10+
11+
namespace LogExpert.UI.Extensions;
12+
13+
internal class LockFinder
14+
{
15+
16+
/// <summary>
17+
/// Method <c>FindLockedProcessName</c> Retrieve the first process name
18+
/// that is locking the file at the specified path
19+
/// </summary>
20+
/// <param name="path">The path of a file with a write lock held by a
21+
/// process</param>
22+
/// <resturns>The name of the first process found with a lock</resturns>
23+
/// <exception cref="Exception">
24+
/// Thrown when the file path is not locked
25+
/// </exception>
26+
static public string FindLockedProcessName (string path)
27+
{
28+
var list = FindLockProcesses(path);
29+
if (list.Count == 0)
30+
{
31+
throw new Exception(
32+
"No processes are locking the path specified");
33+
}
34+
return list[0].ProcessName;
35+
}
36+
37+
/// <summary>
38+
/// Method <c>CheckIfFileIsLocked</c> Check if the file specified has a
39+
/// write lock held by a process
40+
/// </summary>
41+
/// <param name="path">The path of a file being checked if a write lock
42+
/// held by a process</param>
43+
/// <returns>true when one or more processes with lock</returns>
44+
static public bool CheckIfFileIsLocked (string path)
45+
{
46+
var list = FindLockProcesses(path);
47+
if (list.Count > 0)
48+
{ return true; }
49+
return false;
50+
}
51+
52+
/// <summary>
53+
/// Used to find processes holding a lock on the file. This would cause
54+
/// other usage, such as file truncation or write opretions to throw
55+
/// IOException if an exclusive lock is attempted.
56+
/// </summary>
57+
/// <param name="path">Path being checked</param>
58+
/// <returns>List of processes holding file lock to path</returns>
59+
/// <exception cref="Exception"></exception>
60+
static public List<Process> FindLockProcesses (string path)
61+
{
62+
var key = Guid.NewGuid().ToString();
63+
var processes = new List<Process>();
64+
65+
var res = NativeMethods.RmStartSession(out var handle, 0, key);
66+
if (res != 0)
67+
{
68+
throw new Exception("Could not begin restart session. " +
69+
"Unable to determine file locker.");
70+
}
71+
72+
try
73+
{
74+
uint pnProcInfo = 0;
75+
uint lpdwRebootReasons = NativeMethods.RmRebootReasonNone;
76+
string[] resources = [path];
77+
78+
res = NativeMethods.RmRegisterResources(handle, (uint)resources.Length,
79+
resources, 0, null, 0, null);
80+
if (res != 0)
81+
{
82+
throw new Exception("Could not register resource.");
83+
}
84+
res = NativeMethods.RmGetList(handle, out var pnProcInfoNeeded, ref pnProcInfo, null,
85+
ref lpdwRebootReasons);
86+
const int ERROR_MORE_DATA = 234;
87+
if (res == ERROR_MORE_DATA)
88+
{
89+
var processInfo =
90+
new NativeMethods.RM_PROCESS_INFO[pnProcInfoNeeded];
91+
pnProcInfo = pnProcInfoNeeded;
92+
// Get the list.
93+
res = NativeMethods.RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
94+
if (res == 0)
95+
{
96+
processes = new List<Process>((int)pnProcInfo);
97+
for (var i = 0; i < pnProcInfo; i++)
98+
{
99+
try
100+
{
101+
processes.Add(Process.GetProcessById(processInfo[i].
102+
Process.dwProcessId));
103+
}
104+
catch (ArgumentException) { }
105+
}
106+
}
107+
else
108+
{
109+
throw new Exception("Could not list processes locking resource");
110+
}
111+
}
112+
else if (res != 0)
113+
{
114+
throw new Exception("Could not list processes locking resource." +
115+
"Failed to get size of result.");
116+
}
117+
}
118+
catch (Exception exception)
119+
{
120+
Trace.WriteLine(exception.Message);
121+
}
122+
finally
123+
{
124+
Trace.WriteLine($"RmEndSession: {NativeMethods.RmEndSession(handle)}");
125+
}
126+
127+
return processes;
128+
}
129+
}

0 commit comments

Comments
 (0)