Skip to content

Commit 6aa02ac

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

4 files changed

Lines changed: 235 additions & 1 deletion

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<RootNamespace>FileLockFinder</RootNamespace>
5+
<AssemblyName>FileLockFinder</AssemblyName>
6+
<Title>FileLockFinder</Title>
7+
<OutputType>Library</OutputType>
8+
<SignAssembly>true</SignAssembly>
9+
<AssemblyOriginatorKeyFile>..\Solution Items\Key.snk</AssemblyOriginatorKeyFile>
10+
<OutputPath>$(SolutionDir)..\bin\$(Configuration)\plugins</OutputPath>
11+
<DefineConstants>$(DefineConstants)</DefineConstants>
12+
</PropertyGroup>
13+
14+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
15+
<Optimize>False</Optimize>
16+
</PropertyGroup>
17+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
18+
<Optimize>True</Optimize>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<None Include="..\Solution Items\Key.snk">
23+
<Link>Key.snk</Link>
24+
</None>
25+
</ItemGroup>
26+
</Project>

src/FileLockFinder/LockFinder.cs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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 FileLockFinder
12+
{
13+
public 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) { return true; }
48+
return false;
49+
}
50+
51+
/// <summary>
52+
/// Used to find processes holding a lock on the file. This would cause
53+
/// other usage, such as file truncation or write opretions to throw
54+
/// IOException if an exclusive lock is attempted.
55+
/// </summary>
56+
/// <param name="path">Path being checked</param>
57+
/// <returns>List of processes holding file lock to path</returns>
58+
/// <exception cref="Exception"></exception>
59+
static public List<Process> FindLockProcesses(string path)
60+
{
61+
var key = Guid.NewGuid().ToString();
62+
var processes = new List<Process>();
63+
64+
int res = RmStartSession(out uint handle, 0, key);
65+
if (res != 0)
66+
{
67+
throw new Exception("Could not begin restart session. " +
68+
"Unable to determine file locker.");
69+
}
70+
71+
try
72+
{
73+
uint pnProcInfo = 0;
74+
uint lpdwRebootReasons = RmRebootReasonNone;
75+
string[] resources = [path];
76+
77+
res = RmRegisterResources(handle, (uint)resources.Length,
78+
resources, 0, null, 0, null);
79+
if (res != 0)
80+
{
81+
throw new Exception("Could not register resource.");
82+
}
83+
res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, null,
84+
ref lpdwRebootReasons);
85+
const int ERROR_MORE_DATA = 234;
86+
if (res == ERROR_MORE_DATA)
87+
{
88+
RM_PROCESS_INFO[] processInfo =
89+
new RM_PROCESS_INFO[pnProcInfoNeeded];
90+
pnProcInfo = pnProcInfoNeeded;
91+
// Get the list.
92+
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
93+
if (res == 0)
94+
{
95+
processes = new List<Process>((int)pnProcInfo);
96+
for (int i = 0; i < pnProcInfo; i++)
97+
{
98+
try
99+
{
100+
processes.Add(Process.GetProcessById(processInfo[i].
101+
Process.dwProcessId));
102+
}
103+
catch (ArgumentException) { }
104+
}
105+
}
106+
else
107+
{
108+
throw new Exception("Could not list processes locking resource");
109+
}
110+
}
111+
else if (res != 0)
112+
{
113+
throw new Exception("Could not list processes locking resource." +
114+
"Failed to get size of result.");
115+
}
116+
}
117+
catch (Exception exception)
118+
{
119+
Trace.WriteLine(exception.Message);
120+
}
121+
finally
122+
{
123+
Trace.WriteLine($"RmEndSession: {RmEndSession(handle)}");
124+
}
125+
126+
return processes;
127+
}
128+
private const int RmRebootReasonNone = 0;
129+
private const int CCH_RM_MAX_APP_NAME = 255;
130+
private const int CCH_RM_MAX_SVC_NAME = 63;
131+
132+
[StructLayout(LayoutKind.Sequential)]
133+
struct RM_UNIQUE_PROCESS
134+
{
135+
public int dwProcessId;
136+
public System.Runtime.InteropServices.
137+
ComTypes.FILETIME ProcessStartTime;
138+
}
139+
[DllImport("rstrtmgr.dll",
140+
CharSet = CharSet.Auto, SetLastError = true)]
141+
static extern int RmGetList(uint dwSessionHandle,
142+
out uint pnProcInfoNeeded,
143+
ref uint pnProcInfo,
144+
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
145+
ref uint lpdwRebootReasons);
146+
[StructLayout(LayoutKind.Sequential,
147+
CharSet = CharSet.Auto)]
148+
struct RM_PROCESS_INFO
149+
{
150+
public RM_UNIQUE_PROCESS Process;
151+
[MarshalAs(UnmanagedType.ByValTStr,
152+
SizeConst = CCH_RM_MAX_APP_NAME + 1)]
153+
public string strAppName;
154+
[MarshalAs(UnmanagedType.ByValTStr,
155+
SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
156+
public string strServiceShortName;
157+
public RM_APP_TYPE ApplicationType;
158+
public uint AppStatus;
159+
public uint TSSessionId;
160+
[MarshalAs(UnmanagedType.Bool)]
161+
public bool bRestartable;
162+
}
163+
164+
enum RM_APP_TYPE
165+
{
166+
RmUnknownApp = 0,
167+
RmMainWindow = 1,
168+
RmOtherWindow = 2,
169+
RmService = 3,
170+
RmExplorer = 4,
171+
RmConsole = 5,
172+
RmCritical = 1000
173+
}
174+
175+
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto, SetLastError = true)]
176+
static extern int RmRegisterResources(
177+
uint pSessionHandle,
178+
UInt32 nFiles,
179+
string[] rgsFilenames,
180+
UInt32 nApplications,
181+
[In] RM_UNIQUE_PROCESS[] rgApplications,
182+
UInt32 nServices, string[] rgsServiceNames);
183+
184+
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto, SetLastError = true)]
185+
static extern int RmStartSession(
186+
out uint pSessionHandle,
187+
int dwSessionFlags,
188+
string strSessionKey);
189+
190+
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto, SetLastError = true)]
191+
static extern int RmEndSession(uint pSessionHandle);
192+
}
193+
}

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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,11 @@ private void OnFindInExplorerToolStripMenuItemClick(object sender, EventArgs e)
804804
explorer.Start();
805805
}
806806

807+
private void truncateFileToolStripMenuItem_Click(object sender, EventArgs e)
808+
{
809+
CurrentLogWindow?.TryToTruncate();
810+
}
811+
807812
private void OnExportBookmarksToolStripMenuItemClick(object sender, EventArgs e)
808813
{
809814
CurrentLogWindow?.ExportBookmarkList();

0 commit comments

Comments
 (0)