Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Zgoly authored Oct 31, 2024
1 parent 6f4343a commit bd0b610
Show file tree
Hide file tree
Showing 7 changed files with 823 additions and 0 deletions.
25 changes: 25 additions & 0 deletions MultiBloxy.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiBloxy", "MultiBloxy\MultiBloxy.csproj", "{9CBD2E5E-735B-4FE4-A56B-33032253C305}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9CBD2E5E-735B-4FE4-A56B-33032253C305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CBD2E5E-735B-4FE4-A56B-33032253C305}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CBD2E5E-735B-4FE4-A56B-33032253C305}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CBD2E5E-735B-4FE4-A56B-33032253C305}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9AF4AC17-D81F-4776-8363-7AF4F68E69AA}
EndGlobalSection
EndGlobal
78 changes: 78 additions & 0 deletions MultiBloxy/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.IO;
using System.Xml.Linq;

namespace MultiBloxy
{
public class Config
{
private static readonly string ConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml");
private static XDocument configDocument;

static Config()
{
Load();
}

public static void Load()
{
if (File.Exists(ConfigFilePath))
{
configDocument = XDocument.Load(ConfigFilePath);
}
else
{
configDocument = new XDocument(new XElement("Config"));
}
}

public static void Save()
{
if (!configDocument.Root.HasElements)
{
if (File.Exists(ConfigFilePath))
{
File.Delete(ConfigFilePath);
}
}
else
{
configDocument.Save(ConfigFilePath);
}
}

public static void Set(string key, object value)
{
var element = configDocument.Root.Element(key);
if (element != null)
{
element.Value = value.ToString();
}
else
{
configDocument.Root.Add(new XElement(key, value));
}
Save();
}

public static T Get<T>(string key, T defaultValue = default)
{
var element = configDocument.Root.Element(key);
if (element != null)
{
return (T)Convert.ChangeType(element.Value, typeof(T));
}
return defaultValue;
}

public static void Remove(string key)
{
var element = configDocument.Root.Element(key);
if (element != null)
{
element.Remove();
Save();
}
}
}
}
170 changes: 170 additions & 0 deletions MultiBloxy/HandleCloser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace MultiBloxy
{
public class HandleCloser
{
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_HANDLE_INFORMATION
{
public uint ProcessId;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public uint Object;
public uint GrantedAccess;
}

[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}

[StructLayout(LayoutKind.Sequential)]
private struct OBJECT_NAME_INFORMATION
{
public UNICODE_STRING Name;
}

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenEvent(uint dwDesiredAccess, bool bInheritHandle, string lpName);

[DllImport("ntdll.dll")]
private static extern uint NtQuerySystemInformation(int systemInformationClass, IntPtr systemInformation, uint systemInformationLength, out uint returnLength);

[DllImport("ntdll.dll")]
private static extern uint NtQueryObject(IntPtr handle, int objectInformationClass, IntPtr objectInformation, uint objectInformationLength, out uint returnLength);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetCurrentProcess();

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

private const int SystemHandleInformation = 16;
private const int ObjectNameInformation = 1;
private const uint PROCESS_ALL = 0x001F0FFF;
private const uint DUPLICATE_CLOSE_SOURCE = 0x0001;
private const uint DUPLICATE_SAME_ACCESS = 0x0002;

// I don't think this is the most ideal and optimized solution. Feel free to create a pull request with improvements / refactoring
public static void CloseAllHandles()
{
// Get all processes with the name "RobloxPlayerBeta"
Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta");

// Iterate through each process
foreach (var process in processes)
{
uint size = 0x10000;
IntPtr buffer = Marshal.AllocHGlobal((int)size);

// Loop to query system information until the buffer is large enough
while (true)
{
// Query system information for handles
uint status = NtQuerySystemInformation(SystemHandleInformation, buffer, size, out uint returnLength);

// If the buffer is too small, double its size and retry
if (status == 0xC0000004)
{
size *= 2;
Marshal.FreeHGlobal(buffer);
buffer = Marshal.AllocHGlobal((int)size);
}
else
{
break;
}
}

// Read the number of handles from the buffer
int handleCount = Marshal.ReadInt32(buffer);
IntPtr ptr = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(typeof(int)));

// Iterate through each handle
for (int i = 0; i < handleCount; i++)
{
// Get handle information from the buffer
SYSTEM_HANDLE_INFORMATION handleInfo = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ptr, typeof(SYSTEM_HANDLE_INFORMATION));

// Check if the handle belongs to the current process
if (handleInfo.ProcessId == process.Id)
{
// Open the process to get a handle
IntPtr processHandle = OpenProcess(PROCESS_ALL, false, process.Id);
if (processHandle == IntPtr.Zero)
{
// Move to the next handle if opening the process fails
ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION)));
continue;
}

IntPtr dupHandle = IntPtr.Zero;
// Duplicate the handle to the current process
bool success = DuplicateHandle(processHandle, new IntPtr(handleInfo.Handle), GetCurrentProcess(), out dupHandle, 0, false, DUPLICATE_SAME_ACCESS);
if (!success)
{
// Close the process handle and move to the next handle if duplication fails
CloseHandle(processHandle);
ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION)));
continue;
}

uint bufferSize = 0x1000;
IntPtr nameBuffer = Marshal.AllocHGlobal((int)bufferSize);

// Query the object name information for the duplicated handle
uint status = NtQueryObject(dupHandle, ObjectNameInformation, nameBuffer, bufferSize, out uint returnLength);

if (status != 0)
{
// Free resources and move to the next handle if querying the object name fails
Marshal.FreeHGlobal(nameBuffer);
CloseHandle(dupHandle);
CloseHandle(processHandle);
ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION)));
continue;
}

// Get the object name information from the buffer
OBJECT_NAME_INFORMATION objectNameInfo = (OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(nameBuffer, typeof(OBJECT_NAME_INFORMATION));
if (objectNameInfo.Name.Length > 0)
{
// Convert the object name to a string
string name = Marshal.PtrToStringUni(objectNameInfo.Name.Buffer, objectNameInfo.Name.Length / 2);
if (name.Contains("ROBLOX_singletonEvent"))
{
// Close the handle if it matches the target name
bool success2 = DuplicateHandle(processHandle, new IntPtr(handleInfo.Handle), IntPtr.Zero, out _, 0, false, DUPLICATE_CLOSE_SOURCE);
}
}

// Free resources
Marshal.FreeHGlobal(nameBuffer);
CloseHandle(dupHandle);
CloseHandle(processHandle);
}
// Move to the next handle
ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION)));
}

// Free the allocated buffer
Marshal.FreeHGlobal(buffer);
}
}
}
}
104 changes: 104 additions & 0 deletions MultiBloxy/Localization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.Globalization;

namespace MultiBloxy
{
public class Localization
{
private readonly Dictionary<string, Dictionary<string, string>> _translations;
private string _currentLocale;

public Localization()
{
_translations = new Dictionary<string, Dictionary<string, string>>();
_currentLocale = CultureInfo.CurrentCulture.Name;
LoadTranslations();
}

private void LoadTranslations()
{
_translations["en"] = new Dictionary<string, string>
{
{ "ContextMenu.StatusMenuItem.Running", "Status: Running" },
{ "ContextMenu.StatusMenuItem.Paused", "Status: Paused" },
{ "ContextMenu.StatusMenuItem.Error", "Status: Error creating Mutex" },
{ "ContextMenu.PauseMenuItem.Pause", "Pause" },
{ "ContextMenu.PauseMenuItem.Resume", "Resume" },
{ "ContextMenu.ReloadMenuItem.Reload", "Reload" },
{ "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Start New Roblox Instance" },
{ "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Stop All Roblox Instances" },
{ "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Show In Explorer" },
{ "ContextMenu.SettingsMenuItem.Settings", "Settings" },
{ "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Pause on Launch" },
{ "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Reset Remembered Options" },
{ "ContextMenu.ExitMenuItem.Exit", "Exit" },
{ "Error.Mutex.Caption", "Failed to Create Mutex" },
{ "Error.Mutex.Message", "An error occurred while creating the Mutex. This likely happened because when {0} was launched, Roblox was already running and had registered its handle. You can do the following:" },
{ "Error.Mutex.Action.Fix", "Close the handle for all instances of Roblox" },
{ "Error.Mutex.Action.Abort", "Stop all Roblox instances" },
{ "Error.Mutex.Action.Retry", "Try again" },
{ "Error.Mutex.Action.Ignore", "Ignore the error and continue" },
{ "Error.Mutex.Action.Remember", "Remember this choice" },
{ "Error.Mutex.Action.Confirm", "Confirm" },
{ "Error.Singleton.Caption", "Singleton Error" },
{ "Error.Singleton.Message", "{0} is already running. Try looking in the system tray." }
};

_translations["ru"] = new Dictionary<string, string>
{
{ "ContextMenu.StatusMenuItem.Running", "Статус: Работает" },
{ "ContextMenu.StatusMenuItem.Paused", "Статус: Приостановлено" },
{ "ContextMenu.StatusMenuItem.Error", "Статус: Ошибка создания Mutex" },
{ "ContextMenu.PauseMenuItem.Pause", "Приостановить" },
{ "ContextMenu.PauseMenuItem.Resume", "Возобновить" },
{ "ContextMenu.ReloadMenuItem.Reload", "Перезагрузить" },
{ "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Запустить новый экземпляр Roblox" },
{ "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Закрыть все экземпляры Roblox" },
{ "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Показать в проводнике" },
{ "ContextMenu.SettingsMenuItem.Settings", "Настройки" },
{ "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Приостановить при запуске" },
{ "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Сбросить запомненные параметры" },
{ "ContextMenu.ExitMenuItem.Exit", "Выход" },
{ "Error.Mutex.Caption", "Не удалось создать Mutex" },
{ "Error.Mutex.Message", "Произошла ошибка при создании Mutex. Скорее всего, это связано с тем, что при запуске {0} Roblox уже был запущен и успел зарегистрировать свой дескриптор. Вы можете сделать следующее:" },
{ "Error.Mutex.Action.Fix", "Закрыть дескриптор для всех экземпляров Roblox" },
{ "Error.Mutex.Action.Abort", "Закрыть все экземпляры Roblox" },
{ "Error.Mutex.Action.Retry", "Попробовать снова" },
{ "Error.Mutex.Action.Ignore", "Игнорировать ошибку и продолжить" },
{ "Error.Mutex.Action.Remember", "Запомнить этот выбор" },
{ "Error.Mutex.Action.Confirm", "Подтвердить" },
{ "Error.Singleton.Caption", "Ошибка одиночного экземпляра" },
{ "Error.Singleton.Message", "{0} уже запущен. Попробуйте поискать в области уведомлений." }
};
}

public string GetTranslation(string key)
{
string locale = GetLocaleWithoutRegion(_currentLocale);

if (_translations.ContainsKey(locale) && _translations[locale].ContainsKey(key))
{
return _translations[locale][key];
}

// Fallback to English if the translation is not found
if (_translations.ContainsKey("en") && _translations["en"].ContainsKey(key))
{
return _translations["en"][key];
}

// Fallback to the key if the translation is not found
return key;
}

private string GetLocaleWithoutRegion(string locale)
{
int index = locale.IndexOf('-');
if (index != -1)
{
return locale.Substring(0, index);
}
return locale;
}
}
}
Loading

0 comments on commit bd0b610

Please sign in to comment.