-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added LockFinder project, to check processes holding file lock during truncate Added Status Line to tell user if something didn't work Renamed method and tooltip to indicate Truncate is not guaranteed
- Loading branch information
Showing
8 changed files
with
338 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProductVersion>9.0.21022</ProductVersion> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{6C463EA8-FE3F-4832-B22B-E1B199F2C308}</ProjectGuid> | ||
<OutputType>Library</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>FileLockFinder</RootNamespace> | ||
<AssemblyName>FileLockFinder</AssemblyName> | ||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
<FileUpgradeFlags> | ||
</FileUpgradeFlags> | ||
<UpgradeBackupLocation> | ||
</UpgradeBackupLocation> | ||
<OldToolsVersion>3.5</OldToolsVersion> | ||
<TargetFrameworkProfile /> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>..\..\bin\Debug\plugins\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<Prefer32Bit>false</Prefer32Bit> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>..\..\bin\Release\plugins\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<Prefer32Bit>false</Prefer32Bit> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<SignAssembly>true</SignAssembly> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<AssemblyOriginatorKeyFile>..\Solution Items\Key.snk</AssemblyOriginatorKeyFile> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Drawing" /> | ||
<Reference Include="System.Windows.Forms" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="..\Solution Items\AssemblyVersion.cs"> | ||
<Link>Properties\AssemblyVersion.cs</Link> | ||
</Compile> | ||
<Compile Include="LockFinder.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="..\Solution Items\Key.snk"> | ||
<Link>Key.snk</Link> | ||
</None> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||
Other similar extension points exist, see Microsoft.Common.targets. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
<PropertyGroup> | ||
<PostBuildEvent> | ||
</PostBuildEvent> | ||
</PropertyGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
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 FileLockFinder | ||
{ | ||
public class LockFinder | ||
{ | ||
|
||
/// <summary> | ||
/// Method <c>FindLockedProcessName</c> Retrieve the first process name | ||
/// that is locking the file at the specified path | ||
/// </summary> | ||
/// <param name="path">The path of a file with a write lock held by a | ||
/// process</param> | ||
/// <resturns>The name of the first process found with a lock</resturns> | ||
/// <exception cref="Exception"> | ||
/// Thrown when the file path is not locked | ||
/// </exception> | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// Method <c>CheckIfFileIsLocked</c> Check if the file specified has a | ||
/// write lock held by a process | ||
/// </summary> | ||
/// <param name="path">The path of a file being checked if a write lock | ||
/// held by a process</param> | ||
/// <returns>true when one or more processes with lock</returns> | ||
static public bool CheckIfFileIsLocked(string path) | ||
{ | ||
var list = FindLockProcesses(path); | ||
if(list.Count > 0) { return true; } | ||
return false; | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
/// <param name="path">Path being checked</param> | ||
/// <returns>List of processes holding file lock to path</returns> | ||
/// <exception cref="Exception"></exception> | ||
static public List<Process> FindLockProcesses(string path) | ||
{ | ||
uint handle; | ||
string key = Guid.NewGuid().ToString(); | ||
List<Process> processes = new List<Process>(); | ||
|
||
int res = RmStartSession(out handle, 0, key); | ||
if (res != 0) | ||
{ | ||
throw new Exception("Could not begin restart session. " + | ||
"Unable to determine file locker."); | ||
} | ||
|
||
try | ||
{ | ||
const int ERROR_MORE_DATA = 234; | ||
uint pnProcInfoNeeded = 0, pnProcInfo = 0, | ||
lpdwRebootReasons = RmRebootReasonNone; | ||
string[] resources = new string[] { path }; | ||
|
||
res = RmRegisterResources(handle, (uint)resources.Length, | ||
resources, 0, null, 0, null); | ||
if (res != 0) | ||
{ | ||
throw new Exception("Could not register resource."); | ||
} | ||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, | ||
ref lpdwRebootReasons); | ||
if (res == ERROR_MORE_DATA) | ||
{ | ||
RM_PROCESS_INFO[] processInfo = | ||
new RM_PROCESS_INFO[pnProcInfoNeeded]; | ||
pnProcInfo = pnProcInfoNeeded; | ||
// Get the list. | ||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, | ||
processInfo, ref lpdwRebootReasons); | ||
if (res == 0) | ||
{ | ||
processes = new List<Process>((int)pnProcInfo); | ||
for (int 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) | ||
{ | ||
Console.WriteLine(exception.Message); | ||
} | ||
finally | ||
{ | ||
RmEndSession(handle); | ||
} | ||
|
||
return processes; | ||
} | ||
private const int RmRebootReasonNone = 0; | ||
private const int CCH_RM_MAX_APP_NAME = 255; | ||
private const int CCH_RM_MAX_SVC_NAME = 63; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
struct RM_UNIQUE_PROCESS | ||
{ | ||
public int dwProcessId; | ||
public System.Runtime.InteropServices. | ||
ComTypes.FILETIME ProcessStartTime; | ||
} | ||
[DllImport("rstrtmgr.dll", | ||
CharSet = CharSet.Auto, SetLastError = true)] | ||
static extern int RmGetList(uint dwSessionHandle, | ||
out uint pnProcInfoNeeded, | ||
ref uint pnProcInfo, | ||
[In, Out] RM_PROCESS_INFO[] rgAffectedApps, | ||
ref uint lpdwRebootReasons); | ||
[StructLayout(LayoutKind.Sequential, | ||
CharSet = CharSet.Auto)] | ||
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; | ||
} | ||
|
||
enum RM_APP_TYPE | ||
{ | ||
RmUnknownApp = 0, | ||
RmMainWindow = 1, | ||
RmOtherWindow = 2, | ||
RmService = 3, | ||
RmExplorer = 4, | ||
RmConsole = 5, | ||
RmCritical = 1000 | ||
} | ||
|
||
[DllImport("rstrtmgr.dll", | ||
CharSet = CharSet.Auto, | ||
SetLastError = true)] | ||
static extern int RmRegisterResources( | ||
uint pSessionHandle, | ||
UInt32 nFiles, | ||
string[] rgsFilenames, | ||
UInt32 nApplications, | ||
[In] RM_UNIQUE_PROCESS[] rgApplications, | ||
UInt32 nServices, string[] rgsServiceNames); | ||
|
||
[DllImport("rstrtmgr.dll", | ||
CharSet = CharSet.Auto, | ||
SetLastError = true)] | ||
static extern int RmStartSession( | ||
out uint pSessionHandle, | ||
int dwSessionFlags, | ||
string strSessionKey); | ||
|
||
[DllImport("rstrtmgr.dll", | ||
CharSet = CharSet.Auto, | ||
SetLastError = true)] | ||
static extern int RmEndSession(uint pSessionHandle); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("FileLockFinder")] | ||
[assembly: AssemblyDescription("")] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("6c463ea8-fe3f-4832-b22b-e1b199f2c308")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.