Skip to content

Commit b2a6810

Browse files
author
ZanyBaka
committed
Added Directory support
1 parent 35ff61d commit b2a6810

File tree

6 files changed

+95
-41
lines changed

6 files changed

+95
-41
lines changed

build/v2.zip

128 KB
Binary file not shown.

src/Console.v2/Console.v2.csproj.user

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Console.v2/Program.cs

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.IO;
5+
using System.Linq;
46
using System.Reflection;
57
using System.Runtime.InteropServices;
68
using FileLockedBy.Enums;
@@ -14,72 +16,119 @@ namespace FileLockedBy
1416
{
1517
class Program
1618
{
19+
private static bool SuppressUserInput = false;
20+
1721
static void Main(string[] args)
1822
{
1923
Console.Title = "Unlocker";
2024
Process currentProcess = Process.GetCurrentProcess();
21-
string fullPath = currentProcess.MainModule.FileName;
22-
string fileName = Path.GetFileName(currentProcess.MainModule.FileName);
25+
string fullPath = currentProcess.MainModule.FileName;
26+
string fileName = Path.GetFileName(currentProcess.MainModule.FileName);
2327

24-
bool suppressUserInput = false;
2528
if (args.Length == 2)
2629
{
27-
suppressUserInput = args[1].ToLowerInvariant() == "-s";
30+
SuppressUserInput = args[1].ToLowerInvariant() == "-s";
2831
}
2932
else if (args.Length != 1)
3033
{
31-
Console.WriteLine($"Usage: {fileName} <file_to_unlock> [-s] - unlocks the file");
34+
Console.WriteLine($"Usage: {fileName} <file_to_unlock> [-s] - unlocks the file/directory");
3235
Console.WriteLine($"Usage: {fileName} register [-s] - integrate into Explorer");
3336
Console.WriteLine($"Usage: {fileName} unregister [-s] - unintegrate from Explorer");
3437
Console.WriteLine($"-s {new string(' ', fileName.Length + 22)} - Optional parameter. Suppress user input");
3538
return;
3639
}
3740

41+
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
42+
3843
if (args[0].ToLowerInvariant() == "register")
3944
{
4045
ExplorerIntegration.RegisterMenuItem("Unlocker", fullPath);
4146
Console.WriteLine("Registered.");
42-
if (!suppressUserInput) Console.ReadKey();
47+
if (!SuppressUserInput) Console.ReadKey();
4348
return;
4449
}
4550

4651
if (args[0].ToLowerInvariant() == "unregister")
4752
{
4853
ExplorerIntegration.UnregisterMenuItem("Unlocker");
4954
Console.WriteLine("Unregistered.");
50-
if (!suppressUserInput) Console.ReadKey();
55+
if (!SuppressUserInput) Console.ReadKey();
5156
return;
5257
}
5358

59+
bool found;
5460
Console.WriteLine("Unlocking begin...");
55-
bool found = false;
5661
string path = args[0];
57-
var processes = Unlocker.FindLockerProcesses(path);
62+
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
63+
{
64+
if (!Path.EndsInDirectorySeparator(path))
65+
{
66+
path += Path.DirectorySeparatorChar;
67+
}
68+
69+
string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
70+
found = UnlockFiles(files, currentProcess);
71+
}
72+
else
73+
{
74+
found = UnlockFile(path, currentProcess);
75+
}
76+
77+
if (!found) Console.WriteLine("Nothing found. Can't unlock or the file/directory is already unlocked.");
78+
Console.WriteLine("End.");
79+
if (!SuppressUserInput) Console.ReadKey();
80+
}
81+
82+
private static bool UnlockFile(string file, Process currentProcess)
83+
{
84+
return UnlockFiles(new[] { file }, currentProcess);
85+
}
86+
87+
private static bool UnlockFiles(string[] files, Process currentProcess)
88+
{
89+
bool found = false;
90+
var processes = Unlocker.FindLockerProcesses(files);
91+
5892
foreach (RM_PROCESS_INFO info in processes)
5993
{
6094
Console.WriteLine($"Closing handle locked by {info.strAppName}...");
6195
using (SmartPtr sptr = SystemInformation.GetSystemHandleInformation())
6296
{
63-
var information = (SystemHandlesInformation)Marshal.PtrToStructure(sptr.Pointer, typeof(SystemHandlesInformation));
97+
var information = (SystemHandlesInformation) Marshal.PtrToStructure(sptr.Pointer, typeof(SystemHandlesInformation));
6498
int handleCount = information.Count;
6599
var process = Process.GetProcessById(info.Process.dwProcessId);
66100
var infoEnumerator = ProcessHelper.GetCurrentProcessOpenFilesEnumerator(info.Process.dwProcessId, sptr, handleCount);
67-
bool skip = false;
68-
while (infoEnumerator.MoveNext() && !skip)
101+
Dictionary<string, bool> skip = new Dictionary<string, bool>();
102+
while (infoEnumerator.MoveNext())
69103
{
70104
FileHandleInfo current = infoEnumerator.Current;
71-
if (string.Compare(path, current.FileSystemInfo.FullName, StringComparison.OrdinalIgnoreCase) != 0) continue;
72-
Console.WriteLine($"Found! {process.ProcessName} -> {process.MainModule.FileName}");
73-
found = true;
74-
skip = true;
105+
skip.TryGetValue(current.FileSystemInfo.FullName, out bool skipped);
106+
if (skipped
107+
|| files.All(file => string.Compare(file, current.FileSystemInfo.FullName, StringComparison.OrdinalIgnoreCase) != 0))
108+
{
109+
continue;
110+
}
111+
112+
Console.WriteLine(
113+
$"Found locked file {current.FileSystemInfo.FullName}! {process.ProcessName} -> {process.MainModule.FileName}");
114+
found = true;
115+
skip[current.FileSystemInfo.FullName] = true;
75116
var result = ProcessHelper.CloseHandle(process, current, currentProcess);
76117
Console.WriteLine(result == 0 ? "Success." : $"Error: {Enum.GetName(typeof(Error), result)}");
77118
}
78119
}
79120
}
80-
if (!found) Console.WriteLine("Nothing found. Can't unlock or the file is already unlocked.");
81-
Console.WriteLine("End.");
82-
if (!suppressUserInput) Console.ReadKey();
121+
122+
return found;
123+
}
124+
125+
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
126+
{
127+
if (e.IsTerminating && !SuppressUserInput)
128+
{
129+
Console.WriteLine(e.ExceptionObject.ToString());
130+
Console.ReadKey();
131+
}
83132
}
84133
}
85-
}
134+
}

src/Console.v2/Registry/ExplorerIntegration.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@ public static void RegisterMenuItem(string app, string path)
1313
throw new NotSupportedException("The platform is not supported.");
1414
}
1515

16-
RegistryKey classes = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes", writable: true);
17-
RegistryKey asterisk = classes.OpenSubKey("*", writable: true) ?? classes.CreateSubKey("*");
18-
RegistryKey shell = asterisk.OpenSubKey("shell", writable: true) ?? asterisk.CreateSubKey("shell");
19-
RegistryKey unlock = shell.CreateSubKey(app, writable: true);
20-
unlock.SetValue("Icon", $"\"{path}\"");
21-
RegistryKey command = unlock.CreateSubKey("command");
22-
command.SetValue("", $"\"{path}\" \"%1\"");
23-
command.Close();
24-
unlock.Close();
25-
shell.Close();
16+
void Register(RegistryKey registryKey)
17+
{
18+
using RegistryKey shell = registryKey.OpenSubKey("shell", writable: true) ?? registryKey.CreateSubKey("shell");
19+
using RegistryKey unlock = shell.CreateSubKey(app, writable: true);
20+
unlock.SetValue("Icon", $"\"{path}\"");
21+
using RegistryKey command = unlock.CreateSubKey("command");
22+
command.SetValue("", $"\"{path}\" \"%1\"");
23+
}
24+
25+
using RegistryKey classes = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes", writable: true);
26+
using RegistryKey asterisk = classes.OpenSubKey("*", writable: true) ?? classes.CreateSubKey("*");
27+
Register(asterisk);
28+
using RegistryKey directory = classes.OpenSubKey("Directory", writable: true) ?? classes.CreateSubKey("Directory");
29+
Register(directory);
2630
}
2731

2832
public static void UnregisterMenuItem(string app)
@@ -32,9 +36,11 @@ public static void UnregisterMenuItem(string app)
3236
throw new NotSupportedException("The platform is not supported.");
3337
}
3438

35-
RegistryKey shell = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell", writable: true);
36-
shell.DeleteSubKeyTree(app, false);
37-
shell.Close();
39+
using RegistryKey shellFiles = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell", writable: true);
40+
shellFiles?.DeleteSubKeyTree(app, throwOnMissingSubKey: false);
41+
42+
using RegistryKey shellDirectory = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\Directory\\shell", writable: true);
43+
shellDirectory?.DeleteSubKeyTree(app, throwOnMissingSubKey: false);
3844
}
3945
}
4046
}

src/Console.v2/Unlocker.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using FileLockedBy.Win32.System;
34

45
namespace FileLockedBy
@@ -7,15 +8,15 @@ public class Unlocker
78
{
89
private static uint MAX_LOCK_PROCESS_COUNT = 10;
910

10-
public static RM_PROCESS_INFO[] FindLockerProcesses(string path)
11+
public static RM_PROCESS_INFO[] FindLockerProcesses(string[] files)
1112
{
1213
int handle;
1314
if (RestartManagerNativeMethods.RmStartSession(out handle, 0, strSessionKey: Guid.NewGuid().ToString()) != RmResult.ERROR_SUCCESS)
1415
throw new Exception("Could not begin session. Unable to determine file lockers.");
1516

1617
try
1718
{
18-
string[] resources = { path };
19+
string[] resources = files.ToArray();
1920

2021
if (RestartManagerNativeMethods.RmRegisterResources(handle, (uint)resources.LongLength, resources, 0, null, 0, null) != RmResult.ERROR_SUCCESS)
2122
throw new Exception("Could not register resource.");

src/Console.v2/publish.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Use Publish in VS to publish a new .exe
2+
3+
TODO:
4+
Setup as a dotnet tool

0 commit comments

Comments
 (0)