using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace MultiboxMutexer;

public static class HandleManager
{
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	private struct SYSTEM_HANDLE_INFORMATION
	{
		public uint OwnerPID;

		public byte ObjectType;

		public byte HandleFlags;

		public ushort HandleValue;

		public nuint ObjectPointer;

		public nint AccessMask;
	}

	private struct OBJECT_BASIC_INFORMATION
	{
		public uint Attributes;

		public uint GrantedAccess;

		public uint HandleCount;

		public uint PointerCount;

		public uint PagedPoolUsage;

		public uint NonPagedPoolUsage;

		public uint Reserved1;

		public uint Reserved2;

		public uint Reserved3;

		public uint NameInformationLength;

		public uint TypeInformationLength;

		public uint SecurityDescriptorLength;

		public FILETIME CreateTime;
	}

	private struct IO_STATUS_BLOCK
	{
		public uint Status;

		public ulong Information;
	}

	[Flags]
	private enum DuplicateOptions : uint
	{
		DUPLICATE_CLOSE_SOURCE = 1u,
		DUPLICATE_SAME_ACCESS = 2u
	}

	[Flags]
	private enum ProcessAccessFlags : uint
	{
		All = 0x1F0FFFu,
		Terminate = 1u,
		CreateThread = 2u,
		VMOperation = 8u,
		VMRead = 0x10u,
		VMWrite = 0x20u,
		DupHandle = 0x40u,
		SetInformation = 0x200u,
		QueryInformation = 0x400u,
		Synchronize = 0x100000u
	}

	[Flags]
	private enum NTSTATUS : uint
	{
		STATUS_SUCCESS = 0u,
		STATUS_INFO_LENGTH_MISMATCH = 0xC0000004u
	}

	[Flags]
	private enum OBJECT_INFORMATION_CLASS : uint
	{
		ObjectBasicInformation = 0u,
		ObjectNameInformation = 1u,
		ObjectTypeInformation = 2u,
		ObjectAllTypesInformation = 3u,
		ObjectHandleInformation = 4u
	}

	[Flags]
	private enum SYSTEM_INFORMATION_CLASS : uint
	{
		SystemHandleInformation = 0x10u
	}

	[Flags]
	private enum FILE_INFORMATION_CLASS
	{
		FileNameInformation = 9
	}

	[DllImport("kernel32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool CloseHandle(nint hObject);

	[DllImport("kernel32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool DuplicateHandle(nint hSourceProcessHandle, nint hSourceHandle, nint hTargetProcessHandle, out nint lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, DuplicateOptions dwOptions);

	[DllImport("kernel32.dll")]
	private static extern nint OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessID);

	[DllImport("ntdll.dll", SetLastError = true)]
	private static extern NTSTATUS NtQueryInformationFile(nint FileHandle, ref IO_STATUS_BLOCK IoStatusBlock, nint FileInformation, int FileInformationLength, FILE_INFORMATION_CLASS FileInformationClass);

	[DllImport("ntdll.dll")]
	private static extern NTSTATUS NtQueryObject(nint ObjectHandle, OBJECT_INFORMATION_CLASS ObjectInformationClass, nint ObjectInformation, int ObjectInformationLength, out int ReturnLength);

	[DllImport("ntdll.dll")]
	private static extern NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, nint SystemInformation, int SystemInformationLength, out int ReturnLength);

	public static bool ClearMutex()
	{
		bool result = false;
		Process[] processes = Process.GetProcesses();
		foreach (Process process in processes)
		{
			if (process.ProcessName.Equals("ffxiv_dx11", StringComparison.OrdinalIgnoreCase) && KillHandle(process, "6AA83AB5-BAC4-4a36-9F66-A309770760CB_ffxiv_game00", isFile: false))
			{
				result = true;
			}
		}
		return result;
	}

	public static bool KillHandle(Process targetProcess, string handleName, bool isFile)
	{
		bool result = false;
		nint allHandles = GetAllHandles();
		if (allHandles == IntPtr.Zero)
		{
			return result;
		}
		List<SYSTEM_HANDLE_INFORMATION> handles = GetHandles(targetProcess, allHandles);
		Marshal.FreeHGlobal(allHandles);
		nint num = OpenProcess(ProcessAccessFlags.DupHandle, bInheritHandle: false, (uint)targetProcess.Id);
		foreach (SYSTEM_HANDLE_INFORMATION item in handles)
		{
			string text = ((!isFile) ? GetHandleName(item, num) : GetFileName(item, num));
			if (text.Contains(handleName) && CloseHandleEx(item.OwnerPID, new IntPtr(item.HandleValue)))
			{
				result = true;
			}
		}
		CloseHandle(num);
		return result;
	}

	private static bool CloseHandleEx(uint processID, nint handleToClose)
	{
		nint num = OpenProcess(ProcessAccessFlags.All, bInheritHandle: false, processID);
		nint lpTargetHandle;
		bool result = DuplicateHandle(num, handleToClose, IntPtr.Zero, out lpTargetHandle, 0u, bInheritHandle: false, DuplicateOptions.DUPLICATE_CLOSE_SOURCE);
		CloseHandle(num);
		return result;
	}

	private static string ConvertToString(nint pStringBuffer)
	{
		long num = ((IntPtr)pStringBuffer).ToInt64();
		int num2 = IntPtr.Size * 2;
		return Marshal.PtrToStringAnsi(new IntPtr(num + num2));
	}

	private static nint GetAllHandles()
	{
		int num = 65536;
		nint num2 = Marshal.AllocHGlobal(num);
		int ReturnLength;
		NTSTATUS nTSTATUS = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, num2, num, out ReturnLength);
		while (true)
		{
			switch (nTSTATUS)
			{
			case NTSTATUS.STATUS_INFO_LENGTH_MISMATCH:
				break;
			case NTSTATUS.STATUS_SUCCESS:
				return num2;
			default:
				Marshal.FreeHGlobal(num2);
				return IntPtr.Zero;
			}
			Marshal.FreeHGlobal(num2);
			num *= 2;
			num2 = Marshal.AllocHGlobal(num);
			nTSTATUS = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, num2, num, out ReturnLength);
		}
	}

	private static List<SYSTEM_HANDLE_INFORMATION> GetHandles(Process targetProcess, nint pSysHandles)
	{
		List<SYSTEM_HANDLE_INFORMATION> list = new List<SYSTEM_HANDLE_INFORMATION>();
		long num = ((IntPtr)pSysHandles).ToInt64();
		int num2 = Marshal.ReadInt32(pSysHandles);
		for (int i = 0; i < num2; i++)
		{
			long num3 = IntPtr.Size + i * Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION));
			SYSTEM_HANDLE_INFORMATION item = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(new IntPtr(num + num3), typeof(SYSTEM_HANDLE_INFORMATION));
			if (item.OwnerPID == (uint)targetProcess.Id)
			{
				list.Add(item);
			}
		}
		return list;
	}

	private static string GetFileName(SYSTEM_HANDLE_INFORMATION handleInfo, nint hProcess)
	{
		try
		{
			nint handle = Process.GetCurrentProcess().Handle;
			DuplicateHandle(hProcess, new IntPtr(handleInfo.HandleValue), handle, out var lpTargetHandle, 0u, bInheritHandle: false, DuplicateOptions.DUPLICATE_SAME_ACCESS);
			int num = 512;
			nint num2 = Marshal.AllocHGlobal(num);
			IO_STATUS_BLOCK IoStatusBlock = default(IO_STATUS_BLOCK);
			NtQueryInformationFile(lpTargetHandle, ref IoStatusBlock, num2, num, FILE_INFORMATION_CLASS.FileNameInformation);
			CloseHandle(lpTargetHandle);
			int num3 = 4;
			string? result = Marshal.PtrToStringUni(new IntPtr(((IntPtr)num2).ToInt64() + num3));
			Marshal.FreeHGlobal(num2);
			return result;
		}
		catch (Exception)
		{
			return string.Empty;
		}
	}

	private static string GetHandleName(SYSTEM_HANDLE_INFORMATION targetHandleInfo, nint hProcess)
	{
		if (((IntPtr)targetHandleInfo.AccessMask).ToInt64() == 1180063)
		{
			return string.Empty;
		}
		nint handle = Process.GetCurrentProcess().Handle;
		DuplicateHandle(hProcess, new IntPtr(targetHandleInfo.HandleValue), handle, out var lpTargetHandle, 0u, bInheritHandle: false, DuplicateOptions.DUPLICATE_SAME_ACCESS);
		int ReturnLength = GetHandleNameLength(lpTargetHandle);
		nint num = Marshal.AllocHGlobal(ReturnLength);
		NtQueryObject(lpTargetHandle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, num, ReturnLength, out ReturnLength);
		nint zero = IntPtr.Zero;
		zero = Marshal.AllocHGlobal(ReturnLength);
		string result = "";
		if (NtQueryObject(lpTargetHandle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, zero, ReturnLength, out ReturnLength) == NTSTATUS.STATUS_SUCCESS)
		{
			result = Marshal.PtrToStringUni((nint)((long)zero + (long)(2 * IntPtr.Size)));
			Marshal.FreeHGlobal(zero);
		}
		CloseHandle(lpTargetHandle);
		ConvertToString(num);
		Marshal.FreeHGlobal(num);
		return result;
	}

	private static int GetHandleNameLength(nint handle)
	{
		int ReturnLength = Marshal.SizeOf(typeof(OBJECT_BASIC_INFORMATION));
		nint num = Marshal.AllocHGlobal(ReturnLength);
		NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectBasicInformation, num, ReturnLength, out ReturnLength);
		OBJECT_BASIC_INFORMATION oBJECT_BASIC_INFORMATION = (OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(num, typeof(OBJECT_BASIC_INFORMATION));
		Marshal.FreeHGlobal(num);
		if (oBJECT_BASIC_INFORMATION.NameInformationLength == 0)
		{
			return 256;
		}
		return (int)oBJECT_BASIC_INFORMATION.NameInformationLength;
	}
}
