Compare commits

..

8 Commits

@ -11,7 +11,7 @@
!define APP_NAME "UWPHook" !define APP_NAME "UWPHook"
!define COMP_NAME "Briano" !define COMP_NAME "Briano"
!define WEB_SITE "https://briano.dev" !define WEB_SITE "https://briano.dev"
!define VERSION "2.11.00.00" !define VERSION "2.12.00.00"
!define COPYRIGHT "Briano <20> 2020 2021 2022" !define COPYRIGHT "Briano <20> 2020 2021 2022"
!define DESCRIPTION "The easy way to add UWP and XGP games to Steam" !define DESCRIPTION "The easy way to add UWP and XGP games to Steam"
!define LICENSE_TXT "C:\Users\Brian\Documents\GitHub\UWPHook\README.md" !define LICENSE_TXT "C:\Users\Brian\Documents\GitHub\UWPHook\README.md"

@ -35,6 +35,16 @@ namespace UWPHook
set { _name = value; } set { _name = value; }
} }
private string _executable;
/// <summary>
/// Gets or sets the executable of the application
/// </summary>
public string Executable
{
get { return _executable; }
set { _executable = value; }
}
private string _aumid; private string _aumid;
/// <summary> /// <summary>
/// Gets or sets the aumid of the application /// Gets or sets the aumid of the application

@ -1,4 +1,5 @@
using System; using Serilog;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -7,6 +8,8 @@ using System.Management;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using UWPHook.Properties;
namespace UWPHook namespace UWPHook
{ {
@ -17,6 +20,7 @@ namespace UWPHook
{ {
private static int runningProcessId; private static int runningProcessId;
private static bool isLauncherProcess; private static bool isLauncherProcess;
private static string executablePath;
/// <summary> /// <summary>
/// Launch a UWP App using a ApplicationActivationManager and sets a internal id to launched proccess id /// Launch a UWP App using a ApplicationActivationManager and sets a internal id to launched proccess id
@ -24,29 +28,29 @@ namespace UWPHook
/// <param name="aumid">The AUMID of the app to launch</param> /// <param name="aumid">The AUMID of the app to launch</param>
public static void LaunchUWPApp(string[] args) public static void LaunchUWPApp(string[] args)
{ {
string aumid = args[1]; // We receive the args from Steam,
// 0 is application location,
// 1 is the aumid, the rest are extras // We receive the args from Steam,
// 0 is application location,
// 1 is the aumid,
// 2 is the executable, the rest are extras
string aumid = args[1];
executablePath = args[2].Contains("/") ? args[2].Replace('/', '\\') : args[2];
FileStream debug = File.OpenWrite("debug.log");
Log.Debug("Arguments => " + String.Join("/", args));
var mgr = new ApplicationActivationManager(); var mgr = new ApplicationActivationManager();
uint processId; uint processId;
string extra_args = String.Join(" ", args.Skip(2) string extra_args = String.Join(" ", args.Skip(3)
.Take(args.Length - 2) .Take(args.Length - 3)
.Select(eachElement => eachElement.Clone() .Select(eachElement => eachElement.Clone()
).ToArray()); ).ToArray());
try try
{ {
mgr.ActivateApplication(aumid, extra_args, ActivateOptions.None, out processId); mgr.ActivateApplication(aumid, extra_args, ActivateOptions.None, out processId);
runningProcessId = (int)processId; runningProcessId = (int) processId;
Log.Debug("Process ID => " + runningProcessId.ToString());
//if this is a launch aided by GameLaunchHelper, deal with child launch later
var possibleLauncher = Process.GetProcessById(runningProcessId);
if (possibleLauncher.ProcessName.Equals("gamelaunchhelper", StringComparison.OrdinalIgnoreCase))
{
isLauncherProcess = true;
}
//Bring the launched app to the foreground, this fixes in-home streaming //Bring the launched app to the foreground, this fixes in-home streaming
BringProcess(); BringProcess();
@ -63,34 +67,58 @@ namespace UWPHook
/// <returns>True if the perviously launched app is running, false otherwise</returns> /// <returns>True if the perviously launched app is running, false otherwise</returns>
public static Boolean IsRunning() public static Boolean IsRunning()
{ {
//If 0, no app was launched most probably try
if (runningProcessId != 0)
{ {
try Log.Debug("Checking PID => " + runningProcessId.ToString());
// PID 0 means an error during launch for the game
// (Example : Game no more available on gamepass or problem with installation) so instant exit in this case
if (runningProcessId == 0)
{ {
Process.GetProcessById(runningProcessId); Log.Debug("PID is 0");
return true; return false;
} }
catch Process.GetProcessById(runningProcessId);
Log.Debug("Process is running");
return true;
}
catch
{
// Check only at launch if started by a launcher
if (!isLauncherProcess)
{ {
//the process we launched is no longer running. did it spawn any others? Log.Debug("initial PID is not running anymore, checking other possible process runing before stop");
if (isLauncherProcess) bool secondCheck = false;
do
{ {
var tree = GetProcessChildren(); // Handle process running by some launcher by checking their executable path and name
if (tree.TryGetValue(runningProcessId, out var children)) var processes = GetProcess();
foreach (var process in processes)
{ {
//retarget to the first child we find string executableFile = executablePath.Contains('\\') ? executablePath.Substring(executablePath.LastIndexOf('\\') + 1) : executablePath;
isLauncherProcess = false; Log.Debug("Process " + process.Value.Path + " contains " + executablePath + " ? : " + process.Value.Path.Contains(executablePath).ToString());
Log.Debug("Process " + process.Key + " contains " + executableFile + " ? : " + process.Key.Contains(executableFile).ToString());
var newId = children.First(); if (process.Value.Path.Contains(executablePath) || process.Key.Contains(executableFile))
Debug.WriteLine($"Launcher opened child process ({runningProcessId}->{newId}), using new process as target"); {
runningProcessId = newId; int pid = process.Value.Pid;
Log.Debug($"Launcher opened child process ({runningProcessId}->{pid}), using new process as target");
runningProcessId = pid;
isLauncherProcess = true;
//bring the "real" launched process
BringProcess();
return true;
}
}
//bring the "real" launched process // Handle the last chance if process was not found due to slow running like Farming Simulator 2022 or Halo MCC
BringProcess(); secondCheck = !secondCheck;
return true; if (secondCheck)
{
Log.Debug("Process has not been found. Last chance to find it !");
Thread.Sleep(Settings.Default.Seconds * 5000);
} }
} } while (secondCheck);
} }
} }
@ -98,26 +126,24 @@ namespace UWPHook
} }
/// <summary> /// <summary>
/// Find pid<->parent_pid relationships /// Find process path with their dedicated pid
/// </summary> /// </summary>
/// <returns>Map of processes to their child IDs. Any process in this object may have already terminated</returns> /// <returns>Map of processes to their path and pid. Any process in this object may have already terminated</returns>
private static Dictionary<int, List<int>> GetProcessChildren() private static Dictionary<string, (string Path, int Pid)> GetProcess()
{ {
var result = new Dictionary<int, List<int>>(); var result = new Dictionary<string, (string Path, int Pid)>();
using (var searcher = new ManagementObjectSearcher("select processid,parentprocessid from win32_process")) using (var searcher = new ManagementObjectSearcher("select processid, Name, ExecutablePath from win32_process"))
{ {
foreach (var process in searcher.Get()) foreach (var process in searcher.Get())
{ {
string processName = Convert.ToString(process.Properties["Name"].Value);
int processId = Convert.ToInt32(process.Properties["processid"].Value); int processId = Convert.ToInt32(process.Properties["processid"].Value);
int parentId = Convert.ToInt32(process.Properties["parentprocessid"].Value); string processPath = Convert.ToString(process.Properties["ExecutablePath"].Value);
if (!result.ContainsKey(parentId)) if (String.IsNullOrWhiteSpace(processName) || result.ContainsKey(processName)) continue;
{
result.Add(parentId, new List<int>());
}
result[parentId].Add(processId); result.Add(processName, (processPath, processId));
} }
} }
@ -125,7 +151,7 @@ namespace UWPHook
} }
/// <summary> /// <summary>
/// Gets a list of installed UWP Apps on the system, containing each app name + AUMID, separated by '|' /// Gets a list of installed UWP Apps on the system, containing each app name + AUMID + executable path, separated by '|'
/// </summary> /// </summary>
/// <returns>List of installed UWP Apps</returns> /// <returns>List of installed UWP Apps</returns>
public static List<String> GetInstalledApps() public static List<String> GetInstalledApps()

@ -1,4 +1,5 @@
using Force.Crc32; using Force.Crc32;
using Serilog;
using SharpSteam; using SharpSteam;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -35,6 +36,10 @@ namespace UWPHook
Debug.WriteLine("Init GamesWindow"); Debug.WriteLine("Init GamesWindow");
Apps = new AppEntryModel(); Apps = new AppEntryModel();
var args = Environment.GetCommandLineArgs(); var args = Environment.GetCommandLineArgs();
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.File("debug.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
// If null or 1, the app was launched normally // If null or 1, the app was launched normally
if (args?.Length > 1) if (args?.Length > 1)
@ -400,7 +405,7 @@ namespace UWPHook
AppName = app.Name, AppName = app.Name,
Exe = exePath, Exe = exePath,
StartDir = exeDir, StartDir = exeDir,
LaunchOptions = app.Aumid, LaunchOptions = app.Aumid + " " + app.Executable,
AllowDesktopConfig = 1, AllowDesktopConfig = 1,
AllowOverlay = 1, AllowOverlay = 1,
Icon = icon, Icon = icon,
@ -667,7 +672,7 @@ namespace UWPHook
string logosPath = Path.GetDirectoryName(values[1]); string logosPath = Path.GetDirectoryName(values[1]);
Application.Current.Dispatcher.BeginInvoke((Action)delegate () Application.Current.Dispatcher.BeginInvoke((Action)delegate ()
{ {
Apps.Entries.Add(new AppEntry() { Name = values[0], IconPath = logosPath, Aumid = values[2], Selected = false }); Apps.Entries.Add(new AppEntry() { Name = values[0], Executable = values[3], IconPath = logosPath, Aumid = values[2], Selected = false });
}); });
} }
} }

@ -51,6 +51,6 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.11.0.0")] [assembly: AssemblyVersion("2.12.0.0")]
[assembly: AssemblyFileVersion("2.11.0.0")] [assembly: AssemblyFileVersion("2.12.0.0")]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]

@ -10,6 +10,11 @@ foreach ($app in $installedapps)
{ {
$appx = Get-AppxPackageManifest $app; $appx = Get-AppxPackageManifest $app;
$name = $appx.Package.Properties.DisplayName; $name = $appx.Package.Properties.DisplayName;
$executable = (Select-Xml -Path ($app.InstallLocation + "\MicrosoftGame.Config") -XPath "/Game/ExecutableList/Executable/@Name").Node.Value
# Convert object to ensure is the String of execuble (cf Halo Master Chief Collection example below)
# mcclauncher.exe
# MCC\Binaries\Win64\MCCWinStore-Win64-Shipping.exe
if($executable -is [Object[]]) { $executable = $executable[1].ToString() }
if($name -like '*DisplayName*' -or $name -like '*ms-resource*') if($name -like '*DisplayName*' -or $name -like '*ms-resource*')
{ {
@ -22,8 +27,7 @@ foreach ($app in $installedapps)
$logo = $app.InstallLocation + "\" + $appx.Package.Applications.Application.VisualElements.Square150x150Logo; $logo = $app.InstallLocation + "\" + $appx.Package.Applications.Application.VisualElements.Square150x150Logo;
$aumidList += $name + "|" + $logo + "|" + $aumidList += $name + "|" + $logo + "|" + $app.packagefamilyname + "!" + $id + "|" + $executable + ";"
$app.packagefamilyname + "!" + $id+ ";"
} }
} }
} }

@ -2,6 +2,6 @@
"Microsoft.SeaofThieves": "Sea of Thieves", "Microsoft.SeaofThieves": "Sea of Thieves",
"Microsoft.DeltaPC": "Gears of War: Ultimate Edition", "Microsoft.DeltaPC": "Gears of War: Ultimate Edition",
"MotionTwin.DeadCellsWin10": "Dead Cells", "MotionTwin.DeadCellsWin10": "Dead Cells",
"SystemEraSoftworks.29415440E1269_ftk5pbg2rayv2!ASTRONEER": "ASTRONEER", "SystemEraSoftworks.29415440E1269": "ASTRONEER",
"T Tablet": "Taiko no Tatsujin: The Drum Master!" "NAMCOBANDAIGamesInc.TTablet": "Taiko no Tatsujin: The Drum Master!"
} }

@ -213,6 +213,12 @@
<PackageReference Include="Newtonsoft.Json"> <PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version> <Version>13.0.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Serilog">
<Version>2.11.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.File">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.

Loading…
Cancel
Save