[WIP] Moving Shortcut run logic to repo

Moved the shortcut running logic from the
main Program into the ShortcutRepository
so that it can be used from the
Shortcut Library UI as well as from the
commandline.

It's marked as WIP because I also moved
the ApplyingChangesForm and accompanying
ApplyTopos/ApplyProfiles logic and I broke
it. It needs refactoring and hopefully
simplifying but I need to work on that next!
This commit is contained in:
terrymacdonald 2020-07-23 18:31:00 +12:00
parent add7d0ff14
commit c358bc9087
8 changed files with 392 additions and 351 deletions

View File

@ -333,84 +333,7 @@ namespace HeliosPlus.Shared
}
return uncheckedFilename;
}
public void ApplyTopos()
{
Debug.Print("_applyTopos()");
try
{
var surroundTopologies =
Viewports.SelectMany(viewport => viewport.TargetDisplays)
.Select(target => target.SurroundTopology)
.Where(topology => topology != null)
.Select(topology => topology.ToGridTopology())
.ToArray();
if (surroundTopologies.Length == 0)
{
var currentTopologies = GridTopology.GetGridTopologies();
if (currentTopologies.Any(topology => topology.Rows * topology.Columns > 1))
{
surroundTopologies =
GridTopology.GetGridTopologies()
.SelectMany(topology => topology.Displays)
.Select(displays => new GridTopology(1, 1, new[] { displays }))
.ToArray();
}
}
if (surroundTopologies.Length > 0)
{
GridTopology.SetGridTopologies(surroundTopologies, SetDisplayTopologyFlag.MaximizePerformance);
}
}
catch (Exception ex)
{
Console.WriteLine($"ProfileItem/ApplyTopos exception: {ex.Message}: {ex.InnerException}");
// ignored
}
}
public void ApplyPathInfos()
{
Debug.Print("_applyPathInfos()");
if (!IsPossible)
{
throw new InvalidOperationException(
$"Problem applying the '{Name}' Display Profile! The display configuration changed since this profile is created. Please re-create this profile.");
}
var pathInfos = Viewports.Select(viewport => viewport.ToPathInfo()).Where(info => info != null).ToArray();
PathInfo.ApplyPathInfos(pathInfos, true, true, true);
}
public IDictionary<string, Action> applyProfileActions()
{
var dict = new Dictionary<string, Action>()
{
{ "Applying_Topos", ApplyTopos },
{ "Applying_Paths", ApplyPathInfos }
};
return dict;
}
public IDictionary<string, string> applyProfileMsgs()
{
var dict = new Dictionary<string, string>()
{
{ "Applying_Topos", Language.Applying_First_Message },
{ "Applying_Paths", Language.Applying_Second_Message }
};
return dict;
}
public List<string> applyProfileSequence()
{
var list = new List<string>() { "Applying_Topos", "Applying_Paths" };
return list;
}
}
// Custom comparer for the Profile class

View File

@ -21,6 +21,9 @@ using System.Threading;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Resources;
using System.Net.NetworkInformation;
using NvAPIWrapper.Mosaic;
using NvAPIWrapper.Native.Mosaic;
namespace HeliosPlus.Shared
{
@ -527,7 +530,8 @@ namespace HeliosPlus.Shared
Name = "Current Display Profile",
Viewports = PathInfo.GetActivePaths().Select(info => new ProfileViewport(info)).ToArray()
};
}
}
public static bool ApplyProfile(ProfileItem profile)
{
@ -536,14 +540,14 @@ namespace HeliosPlus.Shared
Debug.Print("Begin profile change");
Thread.Sleep(2000);
profile.ApplyTopos();
ApplyTopos(profile);
Debug.Print("Finished setting topologies");
Debug.Print("Sleep");
Thread.Sleep(18000);
Debug.Print("Awake");
profile.ApplyPathInfos();
ApplyPathInfos(profile);
Debug.Print("Applying pathInfos");
Debug.Print("Sleep");
@ -565,6 +569,83 @@ namespace HeliosPlus.Shared
}
}
public static void ApplyTopos(ProfileItem profile)
{
Debug.Print("_applyTopos()");
try
{
var surroundTopologies =
profile.Viewports.SelectMany(viewport => viewport.TargetDisplays)
.Select(target => target.SurroundTopology)
.Where(topology => topology != null)
.Select(topology => topology.ToGridTopology())
.ToArray();
if (surroundTopologies.Length == 0)
{
var currentTopologies = GridTopology.GetGridTopologies();
if (currentTopologies.Any(topology => topology.Rows * topology.Columns > 1))
{
surroundTopologies =
GridTopology.GetGridTopologies()
.SelectMany(topology => topology.Displays)
.Select(displays => new GridTopology(1, 1, new[] { displays }))
.ToArray();
}
}
if (surroundTopologies.Length > 0)
{
GridTopology.SetGridTopologies(surroundTopologies, SetDisplayTopologyFlag.MaximizePerformance);
}
}
catch (Exception ex)
{
Console.WriteLine($"ProfileItem/ApplyTopos exception: {ex.Message}: {ex.InnerException}");
// ignored
}
}
public static void ApplyPathInfos(ProfileItem profile)
{
Debug.Print("_applyPathInfos()");
if (!profile.IsPossible)
{
throw new InvalidOperationException(
$"Problem applying the '{profile.Name}' Display Profile! The display configuration changed since this profile is created. Please re-create this profile.");
}
var pathInfos = profile.Viewports.Select(viewport => viewport.ToPathInfo()).Where(info => info != null).ToArray();
PathInfo.ApplyPathInfos(pathInfos, true, true, true);
}
/* public static IDictionary<string, Action> applyProfileActions(ProfileItem profile)
{
var dict = new Dictionary<string, Action>()
{
{ "Applying_Topos", ApplyTopos(profile) },
{ "Applying_Paths", ApplyPathInfos(profile) }
};
return dict;
}
public static IDictionary<string, string> applyProfileMsgs()
{
var dict = new Dictionary<string, string>()
{
{ "Applying_Topos", Language.Applying_First_Message },
{ "Applying_Paths", Language.Applying_Second_Message }
};
return dict;
}
public static List<string> applyProfileSequence()
{
var list = new List<string>() { "Applying_Topos", "Applying_Paths" };
return list;
}*/
public static bool IsValidFilename(string testName)
{
string strTheseAreInvalidFileNameChars = new string(Path.GetInvalidFileNameChars());

View File

@ -252,7 +252,6 @@ namespace HeliosPlus {
// ReSharper disable once CyclomaticComplexity
private static void RunShortcut(string shortcutUUID)
{
ProfileItem rollbackProfile = ProfileRepository.CurrentProfile;
ShortcutItem shortcutToRun = null;
// Check there is only one version of this application so we won't
@ -282,276 +281,16 @@ namespace HeliosPlus {
throw new Exception(Language.Cannot_find_shortcut_in_library);
}
// Do some validation to make sure the shortcut is sensible
// And that we have enough to try and action within the shortcut
// (in other words check everything in the shortcut is still valid)
(bool valid, string reason) = shortcutToRun.IsValid();
if (!valid)
if (shortcutToRun is ShortcutItem)
{
throw new Exception(string.Format("Unable to run the shortcut '{0}': {1}",shortcutToRun.Name,reason));
ShortcutRepository.RunShortcut(shortcutToRun);
}
// Try to change to the wanted profile
if (!SwitchProfile(shortcutToRun.ProfileToUse))
{
throw new Exception(Language.Cannot_change_active_profile);
}
// Now run the pre-start applications
// TODO: Add the prestart applications
// Now start the main game, and wait if we have to
if (shortcutToRun.Category.Equals(ShortcutCategory.Application))
{
// Start the executable
Process process = null;
if (shortcutToRun.ExecutableArgumentsRequired)
process = System.Diagnostics.Process.Start(shortcutToRun.ExecutableNameAndPath, shortcutToRun.ExecutableArguments);
else
process = System.Diagnostics.Process.Start(shortcutToRun.ExecutableNameAndPath);
// Create a list of processes to monitor
Process[] processesToMonitor = Array.Empty<Process>();
// Work out if we are monitoring another process other than the main executable
if (shortcutToRun.ProcessNameToMonitorUsesExecutable)
{
// If we are monitoring the same executable we started, then lets do that
processesToMonitor = new[] { process };
}
else
{
// Now wait a little while for all the processes we want to monitor to start up
var ticks = 0;
while (ticks < shortcutToRun.ExecutableTimeout * 1000)
{
// Look for the processes with the ProcessName we want (which in Windows is the filename without the extension)
processesToMonitor = System.Diagnostics.Process.GetProcessesByName(Path.GetFileNameWithoutExtension(shortcutToRun.DifferentExecutableToMonitor));
// TODO: Fix this logic error that will only ever wait for the first process....
if (processesToMonitor.Length > 0)
{
break;
}
Thread.Sleep(300);
ticks += 300;
}
// If none started up before the timeout, then ignore the
if (processesToMonitor.Length == 0)
{
processesToMonitor = new[] { process };
}
}
// Store the process to monitor for later
IPCService.GetInstance().HoldProcessId = processesToMonitor.FirstOrDefault()?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
// Add a status notification icon in the status area
NotifyIcon notify = null;
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.HeliosPlus,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
processesToMonitor[0].ProcessName),
Visible = true
};
Application.DoEvents();
}
catch (Exception ex)
{
Console.WriteLine($"Program/SwitchToExecutable exception: {ex.Message}: {ex.InnerException}");
// ignored
}
// Wait for the monitored process to exit
foreach (var p in processesToMonitor)
{
try
{
p.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine($"Program/SwitchToExecutable exception 2: {ex.Message}: {ex.InnerException}");
// ignored
}
}
// Remove the status notification icon from the status area
// once we've existed the game
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
}
else if (shortcutToRun.Category.Equals(ShortcutCategory.Game))
{
// If the game is a Steam Game we check for that
if (shortcutToRun.GameLibrary.Equals(SupportedGameLibrary.Steam))
{
// We now need to get the SteamGame info
SteamGame steamGameToRun = SteamLibrary.GetSteamGame(shortcutToRun.GameAppId);
// If the GameAppID matches a Steam game, then lets run it
if (steamGameToRun is SteamGame)
{
// Prepare to start the steam game using the URI interface
// as used by Steam for it's own desktop shortcuts.
var address = $"steam://rungameid/{steamGameToRun.GameId}";
if (shortcutToRun.GameArgumentsRequired)
{
address += "/" + shortcutToRun.GameArguments;
}
// Start the URI Handler to run Steam
var steamProcess = System.Diagnostics.Process.Start(address);
// Wait for Steam game to update if needed
var ticks = 0;
while (ticks < shortcutToRun.GameTimeout * 1000)
{
if (steamGameToRun.IsRunning)
{
break;
}
Thread.Sleep(300);
if (!steamGameToRun.IsUpdating)
{
ticks += 300;
}
}
// Store the Steam Process ID for later
IPCService.GetInstance().HoldProcessId = steamProcess?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
// Add a status notification icon in the status area
NotifyIcon notify = null;
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.HeliosPlus,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
steamGameToRun.GameName),
Visible = true
};
Application.DoEvents();
}
catch (Exception ex)
{
Console.WriteLine($"Program/SwitchToSteamGame exception: {ex.Message}: {ex.InnerException}");
// ignored
}
// Wait for the game to exit
if (steamGameToRun.IsRunning)
{
while (true)
{
if (!steamGameToRun.IsRunning)
{
break;
}
Thread.Sleep(300);
}
}
// Remove the status notification icon from the status area
// once we've existed the game
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
}
}
// If the game is a Uplay Game we check for that
/*else if (GameLibrary.Equals(SupportedGameLibrary.Uplay))
{
// We need to look up details about the game
if (!UplayGame.IsInstalled(GameAppId))
{
return (false, string.Format("The Uplay Game with AppID '{0}' is not installed on this computer.", GameAppId));
}
}*/
}
IPCService.GetInstance().Status = InstanceStatus.Busy;
// Change back to the original profile if it is different
if (!ProfileRepository.IsActiveProfile(rollbackProfile))
{
if (!SwitchProfile(rollbackProfile))
{
throw new Exception(Language.Cannot_change_active_profile);
}
}
}
internal static bool SwitchProfile(ProfileItem profile)
{
// If we're already on the wanted profile then no need to change!
if (ProfileRepository.IsActiveProfile(profile))
return true;
var instanceStatus = IPCService.GetInstance().Status;
try
{
IPCService.GetInstance().Status = InstanceStatus.Busy;
var failed = false;
if (new ApplyingChangesForm(() =>
{
Task.Factory.StartNew(() =>
{
if (!(ProfileRepository.ApplyProfile(profile)))
{
failed = true;
}
}, TaskCreationOptions.LongRunning);
}, 3, 30).ShowDialog() !=
DialogResult.Cancel)
{
if (failed)
{
throw new Exception(Language.Profile_is_invalid_or_not_possible_to_apply);
}
return true;
}
return false;
}
finally
{
IPCService.GetInstance().Status = instanceStatus;
}
}
private static void EditProfile(ProfileItem profile)
/* private static void EditProfile(ProfileItem profile)
{
// Get the status of the thing
IPCService.GetInstance().Status = InstanceStatus.User;
@ -562,7 +301,7 @@ namespace HeliosPlus {
// Then we close down as we're only here to edit one profile
Application.Exit();
}
*/
public static bool IsValidFilename(string testName)
{
string strTheseAreInvalidFileNameChars = new string(Path.GetInvalidFileNameChars());
@ -573,7 +312,7 @@ namespace HeliosPlus {
return true;
}
public static string GetValidFilename(string uncheckedFilename)
/* public static string GetValidFilename(string uncheckedFilename)
{
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalid)
@ -581,6 +320,6 @@ namespace HeliosPlus {
uncheckedFilename = uncheckedFilename.Replace(c.ToString(), "");
}
return uncheckedFilename;
}
}*/
}
}

View File

@ -20,6 +20,10 @@ using System.Windows.Forms;
using NvAPIWrapper.Native.Display.Structures;
using System.Text.RegularExpressions;
using IWshRuntimeLibrary;
using System.Diagnostics;
using System.Threading;
using HeliosPlus.InterProcess;
using HeliosPlus.UIForms;
namespace HeliosPlus
{
@ -662,6 +666,9 @@ namespace HeliosPlus
return shortcutFileName != null && System.IO.File.Exists(shortcutFileName);
}
public void AutoSuggestShortcutName()
{
if (AutoName && _profileToUse is ProfileItem)

View File

@ -1,7 +1,12 @@
using HeliosPlus.Shared;
using HeliosPlus.GameLibraries;
using HeliosPlus.InterProcess;
using HeliosPlus.Resources;
using HeliosPlus.Shared;
using HeliosPlus.UIForms;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.IconLib;
using System.Globalization;
using System.IO;
@ -9,6 +14,7 @@ using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -434,6 +440,278 @@ namespace HeliosPlus
}
}
// ReSharper disable once CyclomaticComplexity
public static void RunShortcut(ShortcutItem shortcutToUse)
{
// Do some validation to make sure the shortcut is sensible
// And that we have enough to try and action within the shortcut
// (in other words check everything in the shortcut is still valid)
(bool valid, string reason) = shortcutToUse.IsValid();
if (!valid)
{
throw new Exception(string.Format("Unable to run the shortcut '{0}': {1}", shortcutToUse.Name, reason));
}
// Remember the profile we are on now
ProfileItem rollbackProfile = ProfileRepository.CurrentProfile;
// Try to change to the wanted profile
if (!ProfileRepository.ApplyProfile(shortcutToUse.ProfileToUse))
{
throw new Exception(Language.Cannot_change_active_profile);
}
// Now run the pre-start applications
// TODO: Add the prestart applications
// Now start the main game, and wait if we have to
if (shortcutToUse.Category.Equals(ShortcutCategory.Application))
{
// Start the executable
Process process = null;
if (shortcutToUse.ExecutableArgumentsRequired)
process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments);
else
process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath);
// Create a list of processes to monitor
Process[] processesToMonitor = Array.Empty<Process>();
// Work out if we are monitoring another process other than the main executable
if (shortcutToUse.ProcessNameToMonitorUsesExecutable)
{
// If we are monitoring the same executable we started, then lets do that
processesToMonitor = new[] { process };
}
else
{
// Now wait a little while for all the processes we want to monitor to start up
var ticks = 0;
while (ticks < shortcutToUse.ExecutableTimeout * 1000)
{
// Look for the processes with the ProcessName we want (which in Windows is the filename without the extension)
processesToMonitor = System.Diagnostics.Process.GetProcessesByName(Path.GetFileNameWithoutExtension(shortcutToUse.DifferentExecutableToMonitor));
// TODO: Fix this logic error that will only ever wait for the first process....
if (processesToMonitor.Length > 0)
{
break;
}
Thread.Sleep(300);
ticks += 300;
}
// If none started up before the timeout, then ignore the
if (processesToMonitor.Length == 0)
{
processesToMonitor = new[] { process };
}
}
// Store the process to monitor for later
IPCService.GetInstance().HoldProcessId = processesToMonitor.FirstOrDefault()?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
// Add a status notification icon in the status area
NotifyIcon notify = null;
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.HeliosPlus,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
processesToMonitor[0].ProcessName),
Visible = true
};
Application.DoEvents();
}
catch (Exception ex)
{
Console.WriteLine($"ShortcutItem/Run exception: {ex.Message}: {ex.InnerException}");
// ignored
}
// Wait for the monitored process to exit
foreach (var p in processesToMonitor)
{
try
{
p.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine($"ShortcutItem/Run exception 2: {ex.Message}: {ex.InnerException}");
// ignored
}
}
// Remove the status notification icon from the status area
// once we've existed the game
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
}
else if (shortcutToUse.Category.Equals(ShortcutCategory.Game))
{
// If the game is a Steam Game we check for that
if (shortcutToUse.GameLibrary.Equals(SupportedGameLibrary.Steam))
{
// We now need to get the SteamGame info
SteamGame steamGameToRun = SteamLibrary.GetSteamGame(shortcutToUse.GameAppId);
// If the GameAppID matches a Steam game, then lets run it
if (steamGameToRun is SteamGame)
{
// Prepare to start the steam game using the URI interface
// as used by Steam for it's own desktop shortcuts.
var address = $"steam://rungameid/{steamGameToRun.GameId}";
if (shortcutToUse.GameArgumentsRequired)
{
address += "/" + shortcutToUse.GameArguments;
}
// Start the URI Handler to run Steam
var steamProcess = System.Diagnostics.Process.Start(address);
// Wait for Steam game to update if needed
var ticks = 0;
while (ticks < shortcutToUse.GameTimeout * 1000)
{
if (steamGameToRun.IsRunning)
{
break;
}
Thread.Sleep(300);
if (!steamGameToRun.IsUpdating)
{
ticks += 300;
}
}
// Store the Steam Process ID for later
IPCService.GetInstance().HoldProcessId = steamProcess?.Id ?? 0;
IPCService.GetInstance().Status = InstanceStatus.OnHold;
// Add a status notification icon in the status area
NotifyIcon notify = null;
try
{
notify = new NotifyIcon
{
Icon = Properties.Resources.HeliosPlus,
Text = string.Format(
Language.Waiting_for_the_0_to_terminate,
steamGameToRun.GameName),
Visible = true
};
Application.DoEvents();
}
catch (Exception ex)
{
Console.WriteLine($"Program/SwitchToSteamGame exception: {ex.Message}: {ex.InnerException}");
// ignored
}
// Wait for the game to exit
if (steamGameToRun.IsRunning)
{
while (true)
{
if (!steamGameToRun.IsRunning)
{
break;
}
Thread.Sleep(300);
}
}
// Remove the status notification icon from the status area
// once we've existed the game
if (notify != null)
{
notify.Visible = false;
notify.Dispose();
Application.DoEvents();
}
}
}
// If the game is a Uplay Game we check for that
/*else if (GameLibrary.Equals(SupportedGameLibrary.Uplay))
{
// We need to look up details about the game
if (!UplayGame.IsInstalled(GameAppId))
{
return (false, string.Format("The Uplay Game with AppID '{0}' is not installed on this computer.", GameAppId));
}
}*/
}
// Change back to the original profile if it is different
if (!ProfileRepository.IsActiveProfile(rollbackProfile))
{
if (!ProfileRepository.ApplyProfile(rollbackProfile))
{
throw new Exception(Language.Cannot_change_active_profile);
}
}
}
private static bool ChangeToProfile(ProfileItem profile)
{
// If we're already on the wanted profile then no need to change!
if (ProfileRepository.IsActiveProfile(profile))
return true;
var instanceStatus = IPCService.GetInstance().Status;
try
{
IPCService.GetInstance().Status = InstanceStatus.Busy;
var failed = false;
if (new ApplyingChangesForm(() =>
{
Task.Factory.StartNew(() =>
{
if (!(ProfileRepository.ApplyProfile(profile)))
{
failed = true;
}
}, TaskCreationOptions.LongRunning);
}, 3, 30).ShowDialog() !=
DialogResult.Cancel)
{
if (failed)
{
throw new Exception(Language.Profile_is_invalid_or_not_possible_to_apply);
}
return true;
}
return false;
}
finally
{
IPCService.GetInstance().Status = instanceStatus;
}
}
#endregion
}

View File

@ -42,7 +42,7 @@ namespace HeliosPlus.UIForms
return;
}
IDictionary<string, Action> actions = dv_profile.Profile.applyProfileActions();
/*IDictionary<string, Action> actions = dv_profile.Profile.applyProfileActions();
IDictionary<string, string> messages = dv_profile.Profile.applyProfileMsgs();
List<string> sequence = dv_profile.Profile.applyProfileSequence();
@ -68,7 +68,10 @@ namespace HeliosPlus.UIForms
Console.WriteLine("Applying profile " + _selectedProfile.Name);
}
Activate();
Activate();*/
}
private void Exit_Click(object sender, EventArgs e)

View File

@ -108,6 +108,7 @@
this.btn_run.TabIndex = 25;
this.btn_run.Text = "&Run";
this.btn_run.UseVisualStyleBackColor = false;
this.btn_run.Click += new System.EventHandler(this.btn_run_Click);
//
// btn_edit
//

View File

@ -218,5 +218,14 @@ namespace HeliosPlus.UIForms
RefreshShortcutLibraryUI();
}
private void btn_run_Click(object sender, EventArgs e)
{
if (_selectedShortcut == null)
return;
// Run the shortcut
ShortcutRepository.RunShortcut(_selectedShortcut);
}
}
}