mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-07-15 15:19:57 +00:00
Improve exe matching on Linux (#1567)
Co-authored-by: Alexandru Macocian <amacocian@microsoft.com>
This commit is contained in:
@@ -25,6 +25,10 @@ internal static partial class NativeMethods
|
||||
[LibraryImport("kernel32.dll")]
|
||||
public static partial nint OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessID);
|
||||
|
||||
[LibraryImport("kernel32.dll", EntryPoint = "QueryFullProcessImageNameW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool QueryFullProcessImageName(nint hProcess, uint dwFlags, [Out] char[] lpExeName, ref uint lpdwSize);
|
||||
|
||||
[LibraryImport("kernel32.dll", EntryPoint = "GetModuleHandleW", StringMarshalling = StringMarshalling.Utf16)]
|
||||
public static partial nint GetModuleHandle(string lpModuleName);
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using Daybreak.Shared.Models;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Daybreak.Injector;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a Wine-internal process id from a Guild Wars executable path.
|
||||
///
|
||||
/// On Linux multiple Guild Wars instances (each in its own install directory) run under
|
||||
/// the same Wine prefix. Matching by executable name alone cannot tell them apart, so we
|
||||
/// enumerate the running processes and compare each one's full image path — which the
|
||||
/// caller already knows from the Linux side — to find the unique owning Wine process.
|
||||
///
|
||||
/// This runs inside Wine, where <see cref="Process.Id"/> is the Wine pid, so the matched
|
||||
/// id is exactly what the stub/winapi injectors expect.
|
||||
/// </summary>
|
||||
public static class ProcessResolver
|
||||
{
|
||||
private const string GuildWarsProcessName = "Gw";
|
||||
|
||||
public static InjectorResponses.ResolveResult Resolve(string executablePath, out int processId)
|
||||
{
|
||||
processId = 0;
|
||||
var target = NormalizePath(executablePath);
|
||||
|
||||
foreach (var process in Process.GetProcessesByName(GuildWarsProcessName))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TryGetImagePath(process.Id) is { } imagePath &&
|
||||
string.Equals(NormalizePath(imagePath), target, StringComparison.Ordinal))
|
||||
{
|
||||
processId = process.Id;
|
||||
return InjectorResponses.ResolveResult.Success;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
process.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return InjectorResponses.ResolveResult.ProcessNotFound;
|
||||
}
|
||||
|
||||
private static string? TryGetImagePath(int processId)
|
||||
{
|
||||
var handle = NativeMethods.OpenProcess(
|
||||
NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, (uint)processId);
|
||||
if (handle is 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var buffer = new char[1024];
|
||||
var size = (uint)buffer.Length;
|
||||
if (!NativeMethods.QueryFullProcessImageName(handle, 0, buffer, ref size) || size is 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new string(buffer, 0, (int)size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a Windows/Wine path for comparison: unifies separators, trims a trailing
|
||||
/// null/whitespace, and lower-cases (Windows paths are case-insensitive).
|
||||
/// </summary>
|
||||
private static string NormalizePath(string path) =>
|
||||
path.Trim().TrimEnd('\0').Replace('/', '\\').ToLowerInvariant();
|
||||
}
|
||||
@@ -12,11 +12,13 @@ static void PrintUsage()
|
||||
Console.WriteLine("- stub");
|
||||
Console.WriteLine("- launch");
|
||||
Console.WriteLine("- resume");
|
||||
Console.WriteLine("- resolve");
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine("1) Daybreak.Injector winapi 1234 C:\\path\\to\\dll.dll");
|
||||
Console.WriteLine("2) Daybreak.Injector stub 1234 entryPoint C:\\path\\to\\dll.dll");
|
||||
Console.WriteLine("3) Daybreak.Injector launch true C:\\path\\to\\dll.dll arg1 arg2 arg3");
|
||||
Console.WriteLine("4) Daybreak.Injector resume 1234");
|
||||
Console.WriteLine("5) Daybreak.Injector resolve \"Z:\\path\\to\\Gw.exe\"");
|
||||
Console.WriteLine("======================================================");
|
||||
}
|
||||
|
||||
@@ -173,6 +175,24 @@ static bool TryParseThreadResumeArgs(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryParseResolveArgs(
|
||||
string[] args,
|
||||
[NotNullWhen(true)] out string? executablePath,
|
||||
out InjectorResponses.ResolveResult exitCode)
|
||||
{
|
||||
executablePath = default;
|
||||
if (args.Length < 2 || string.IsNullOrWhiteSpace(args[1]))
|
||||
{
|
||||
PrintUsage();
|
||||
exitCode = InjectorResponses.ResolveResult.InvalidArgs;
|
||||
return false;
|
||||
}
|
||||
|
||||
executablePath = args[1];
|
||||
exitCode = InjectorResponses.ResolveResult.Success;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
PrintUsage();
|
||||
@@ -244,6 +264,24 @@ switch (mode)
|
||||
Console.WriteLine($"ExitCode: {result}");
|
||||
return result;
|
||||
}
|
||||
case "resolve":
|
||||
{
|
||||
if (!TryParseResolveArgs(args, out var executablePath, out var parseResult))
|
||||
{
|
||||
Console.WriteLine($"ExitCode: {(int)parseResult}");
|
||||
return (int)parseResult;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Resolving Wine PID for {executablePath}");
|
||||
var result = ProcessResolver.Resolve(executablePath, out var processId);
|
||||
if (result is InjectorResponses.ResolveResult.Success)
|
||||
{
|
||||
Console.WriteLine($"ProcessId: {processId}");
|
||||
}
|
||||
|
||||
Console.WriteLine($"ExitCode: {(int)result}");
|
||||
return (int)result;
|
||||
}
|
||||
default:
|
||||
PrintUsage();
|
||||
Console.WriteLine($"ExitCode: {(int)InjectorResponses.GenericResults.InvalidMode}");
|
||||
|
||||
Reference in New Issue
Block a user