Skip to content

Commit

Permalink
Added Directory support
Browse files Browse the repository at this point in the history
  • Loading branch information
zanybaka committed Nov 8, 2020
1 parent 35ff61d commit b2a6810
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 41 deletions.
Binary file modified build/v2.zip
Binary file not shown.
6 changes: 0 additions & 6 deletions src/Console.v2/Console.v2.csproj.user

This file was deleted.

89 changes: 69 additions & 20 deletions src/Console.v2/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using FileLockedBy.Enums;
Expand All @@ -14,72 +16,119 @@ namespace FileLockedBy
{
class Program
{
private static bool SuppressUserInput = false;

static void Main(string[] args)
{
Console.Title = "Unlocker";
Process currentProcess = Process.GetCurrentProcess();
string fullPath = currentProcess.MainModule.FileName;
string fileName = Path.GetFileName(currentProcess.MainModule.FileName);
string fullPath = currentProcess.MainModule.FileName;
string fileName = Path.GetFileName(currentProcess.MainModule.FileName);

bool suppressUserInput = false;
if (args.Length == 2)
{
suppressUserInput = args[1].ToLowerInvariant() == "-s";
SuppressUserInput = args[1].ToLowerInvariant() == "-s";
}
else if (args.Length != 1)
{
Console.WriteLine($"Usage: {fileName} <file_to_unlock> [-s] - unlocks the file");
Console.WriteLine($"Usage: {fileName} <file_to_unlock> [-s] - unlocks the file/directory");
Console.WriteLine($"Usage: {fileName} register [-s] - integrate into Explorer");
Console.WriteLine($"Usage: {fileName} unregister [-s] - unintegrate from Explorer");
Console.WriteLine($"-s {new string(' ', fileName.Length + 22)} - Optional parameter. Suppress user input");
return;
}

AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

if (args[0].ToLowerInvariant() == "register")
{
ExplorerIntegration.RegisterMenuItem("Unlocker", fullPath);
Console.WriteLine("Registered.");
if (!suppressUserInput) Console.ReadKey();
if (!SuppressUserInput) Console.ReadKey();
return;
}

if (args[0].ToLowerInvariant() == "unregister")
{
ExplorerIntegration.UnregisterMenuItem("Unlocker");
Console.WriteLine("Unregistered.");
if (!suppressUserInput) Console.ReadKey();
if (!SuppressUserInput) Console.ReadKey();
return;
}

bool found;
Console.WriteLine("Unlocking begin...");
bool found = false;
string path = args[0];
var processes = Unlocker.FindLockerProcesses(path);
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
{
if (!Path.EndsInDirectorySeparator(path))
{
path += Path.DirectorySeparatorChar;
}

string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
found = UnlockFiles(files, currentProcess);
}
else
{
found = UnlockFile(path, currentProcess);
}

if (!found) Console.WriteLine("Nothing found. Can't unlock or the file/directory is already unlocked.");
Console.WriteLine("End.");
if (!SuppressUserInput) Console.ReadKey();
}

private static bool UnlockFile(string file, Process currentProcess)
{
return UnlockFiles(new[] { file }, currentProcess);
}

private static bool UnlockFiles(string[] files, Process currentProcess)
{
bool found = false;
var processes = Unlocker.FindLockerProcesses(files);

foreach (RM_PROCESS_INFO info in processes)
{
Console.WriteLine($"Closing handle locked by {info.strAppName}...");
using (SmartPtr sptr = SystemInformation.GetSystemHandleInformation())
{
var information = (SystemHandlesInformation)Marshal.PtrToStructure(sptr.Pointer, typeof(SystemHandlesInformation));
var information = (SystemHandlesInformation) Marshal.PtrToStructure(sptr.Pointer, typeof(SystemHandlesInformation));
int handleCount = information.Count;
var process = Process.GetProcessById(info.Process.dwProcessId);
var infoEnumerator = ProcessHelper.GetCurrentProcessOpenFilesEnumerator(info.Process.dwProcessId, sptr, handleCount);
bool skip = false;
while (infoEnumerator.MoveNext() && !skip)
Dictionary<string, bool> skip = new Dictionary<string, bool>();
while (infoEnumerator.MoveNext())
{
FileHandleInfo current = infoEnumerator.Current;
if (string.Compare(path, current.FileSystemInfo.FullName, StringComparison.OrdinalIgnoreCase) != 0) continue;
Console.WriteLine($"Found! {process.ProcessName} -> {process.MainModule.FileName}");
found = true;
skip = true;
skip.TryGetValue(current.FileSystemInfo.FullName, out bool skipped);
if (skipped
|| files.All(file => string.Compare(file, current.FileSystemInfo.FullName, StringComparison.OrdinalIgnoreCase) != 0))
{
continue;
}

Console.WriteLine(
$"Found locked file {current.FileSystemInfo.FullName}! {process.ProcessName} -> {process.MainModule.FileName}");
found = true;
skip[current.FileSystemInfo.FullName] = true;
var result = ProcessHelper.CloseHandle(process, current, currentProcess);
Console.WriteLine(result == 0 ? "Success." : $"Error: {Enum.GetName(typeof(Error), result)}");
}
}
}
if (!found) Console.WriteLine("Nothing found. Can't unlock or the file is already unlocked.");
Console.WriteLine("End.");
if (!suppressUserInput) Console.ReadKey();

return found;
}

private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.IsTerminating && !SuppressUserInput)
{
Console.WriteLine(e.ExceptionObject.ToString());
Console.ReadKey();
}
}
}
}
}
32 changes: 19 additions & 13 deletions src/Console.v2/Registry/ExplorerIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ public static void RegisterMenuItem(string app, string path)
throw new NotSupportedException("The platform is not supported.");
}

RegistryKey classes = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes", writable: true);
RegistryKey asterisk = classes.OpenSubKey("*", writable: true) ?? classes.CreateSubKey("*");
RegistryKey shell = asterisk.OpenSubKey("shell", writable: true) ?? asterisk.CreateSubKey("shell");
RegistryKey unlock = shell.CreateSubKey(app, writable: true);
unlock.SetValue("Icon", $"\"{path}\"");
RegistryKey command = unlock.CreateSubKey("command");
command.SetValue("", $"\"{path}\" \"%1\"");
command.Close();
unlock.Close();
shell.Close();
void Register(RegistryKey registryKey)
{
using RegistryKey shell = registryKey.OpenSubKey("shell", writable: true) ?? registryKey.CreateSubKey("shell");
using RegistryKey unlock = shell.CreateSubKey(app, writable: true);
unlock.SetValue("Icon", $"\"{path}\"");
using RegistryKey command = unlock.CreateSubKey("command");
command.SetValue("", $"\"{path}\" \"%1\"");
}

using RegistryKey classes = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes", writable: true);
using RegistryKey asterisk = classes.OpenSubKey("*", writable: true) ?? classes.CreateSubKey("*");
Register(asterisk);
using RegistryKey directory = classes.OpenSubKey("Directory", writable: true) ?? classes.CreateSubKey("Directory");
Register(directory);
}

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

RegistryKey shell = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell", writable: true);
shell.DeleteSubKeyTree(app, false);
shell.Close();
using RegistryKey shellFiles = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell", writable: true);
shellFiles?.DeleteSubKeyTree(app, throwOnMissingSubKey: false);

using RegistryKey shellDirectory = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Classes\\Directory\\shell", writable: true);
shellDirectory?.DeleteSubKeyTree(app, throwOnMissingSubKey: false);
}
}
}
5 changes: 3 additions & 2 deletions src/Console.v2/Unlocker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using FileLockedBy.Win32.System;

namespace FileLockedBy
Expand All @@ -7,15 +8,15 @@ public class Unlocker
{
private static uint MAX_LOCK_PROCESS_COUNT = 10;

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

try
{
string[] resources = { path };
string[] resources = files.ToArray();

if (RestartManagerNativeMethods.RmRegisterResources(handle, (uint)resources.LongLength, resources, 0, null, 0, null) != RmResult.ERROR_SUCCESS)
throw new Exception("Could not register resource.");
Expand Down
4 changes: 4 additions & 0 deletions src/Console.v2/publish.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Use Publish in VS to publish a new .exe

TODO:
Setup as a dotnet tool

0 comments on commit b2a6810

Please sign in to comment.