Skip to content

Commit 0ea63f6

Browse files
Randall FlaggRandall Flagg
authored andcommitted
Added truncate file option to context menu - .net8 version
Based on: #146
1 parent b91051e commit 0ea63f6

4 files changed

Lines changed: 167 additions & 2 deletions

File tree

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using LogExpert.Core.EventArguments;
1111
using LogExpert.Dialogs;
1212
using LogExpert.UI.Entities;
13+
using LogExpert.UI.Extensions;
14+
1315
using System.Text;
1416

1517
namespace LogExpert.UI.Controls.LogWindow
@@ -597,6 +599,26 @@ public void GotoLine(int line)
597599
}
598600
}
599601

602+
public void TryToTruncate()
603+
{
604+
try
605+
{
606+
if (LockFinder.CheckIfFileIsLocked(Title))
607+
{
608+
var name = LockFinder.FindLockedProcessName(Title);
609+
var status = string.Format("Truncate failed: file is locked by {0}", name);
610+
StatusLineText(status);
611+
return;
612+
}
613+
614+
File.WriteAllText(Title, "");
615+
}catch(Exception E)
616+
{
617+
StatusLineText("Unexpected issue truncating file");
618+
throw E;
619+
}
620+
}
621+
600622
public void StartSearch()
601623
{
602624
_guiStateArgs.MenuEnabled = false;

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
@@ -9,7 +9,6 @@
99
using LogExpert.Core.EventArguments;
1010
using LogExpert.Dialogs;
1111
using LogExpert.UI.Dialogs;
12-
using LogExpert.UI.Dialogs.LogTabWindow;
1312

1413
using WeifenLuo.WinFormsUI.Docking;
1514

@@ -807,6 +806,11 @@ private void OnFindInExplorerToolStripMenuItemClick (object sender, EventArgs e)
807806
explorer.Start();
808807
}
809808

809+
private void TruncateFileToolStripMenuItem_Click (object sender, EventArgs e)
810+
{
811+
CurrentLogWindow?.TryToTruncate();
812+
}
813+
810814
private void OnExportBookmarksToolStripMenuItemClick (object sender, EventArgs e)
811815
{
812816
CurrentLogWindow?.ExportBookmarkList();
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)