mirror of
https://github.com/cyanfish/naps2.git
synced 2024-11-13 06:27:11 +03:00
211 lines
7.5 KiB
C#
211 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security.Principal;
|
|
using System.Windows.Forms;
|
|
using NAPS2.Config;
|
|
using NAPS2.Logging;
|
|
|
|
namespace NAPS2.Util
|
|
{
|
|
/// <summary>
|
|
/// A class to help manage the lifecycle of the NAPS2 GUI.
|
|
/// </summary>
|
|
public class Lifecycle
|
|
{
|
|
private readonly StillImage sti;
|
|
private readonly WindowsEventLogger windowsEventLogger;
|
|
private readonly ConfigProvider<CommonConfig> configProvider;
|
|
|
|
private bool shouldCreateEventSource;
|
|
private int returnCode;
|
|
|
|
public Lifecycle(StillImage sti, WindowsEventLogger windowsEventLogger, ConfigProvider<CommonConfig> configProvider)
|
|
{
|
|
this.sti = sti;
|
|
this.windowsEventLogger = windowsEventLogger;
|
|
this.configProvider = configProvider;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses the NAPS2 GUI command-line arguments.
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
public void ParseArgs(string[] args)
|
|
{
|
|
bool silent = args.Any(x => x.Equals("/Silent", StringComparison.InvariantCultureIgnoreCase));
|
|
bool noElevation = args.Any(x => x.Equals("/NoElevation", StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
// Utility function to send a message to the user (if /Silent is not specified)
|
|
void Out(string message)
|
|
{
|
|
if (!silent)
|
|
{
|
|
MessageBox.Show(message);
|
|
}
|
|
}
|
|
|
|
// Utility function to run the given action, elevating to admin permissions if necessary (and /NoElevation is not specified)
|
|
bool ElevationRequired(Action action)
|
|
{
|
|
try
|
|
{
|
|
action();
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (!noElevation && !IsElevated)
|
|
{
|
|
RelaunchAsElevated();
|
|
return false;
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// Let StillImage figure out what it should do from the command-line args
|
|
sti.ParseArgs(args);
|
|
|
|
// Actually do any specified StillImage actions
|
|
if (sti.ShouldRegister)
|
|
{
|
|
try
|
|
{
|
|
if (ElevationRequired(sti.Register))
|
|
{
|
|
Out("Successfully registered STI. A reboot may be needed.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.ErrorException("Error registering STI", ex);
|
|
Out("Error registering STI. Maybe run as administrator?");
|
|
returnCode = 1;
|
|
}
|
|
}
|
|
else if (sti.ShouldUnregister)
|
|
{
|
|
try
|
|
{
|
|
if (ElevationRequired(sti.Unregister))
|
|
{
|
|
Out("Successfully unregistered STI. A reboot may be needed.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.ErrorException("Error unregistering STI", ex);
|
|
Out("Error unregistering STI. Maybe run as administrator?");
|
|
returnCode = 1;
|
|
}
|
|
}
|
|
|
|
shouldCreateEventSource = args.Any(x => x.Equals("/CreateEventSource", StringComparison.InvariantCultureIgnoreCase));
|
|
if (shouldCreateEventSource)
|
|
{
|
|
try
|
|
{
|
|
if (ElevationRequired(windowsEventLogger.CreateEventSource))
|
|
{
|
|
Out("Successfully created event source.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.ErrorException("Error creating event source", ex);
|
|
Out("Error creating event source. Maybe run as administrator?");
|
|
returnCode = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsElevated
|
|
{
|
|
get
|
|
{
|
|
var identity = WindowsIdentity.GetCurrent();
|
|
if (identity == null)
|
|
{
|
|
return false;
|
|
}
|
|
var pricipal = new WindowsPrincipal(identity);
|
|
return pricipal.IsInRole(WindowsBuiltInRole.Administrator);
|
|
}
|
|
}
|
|
|
|
private void RelaunchAsElevated()
|
|
{
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
Verb = "runas",
|
|
FileName = Assembly.GetEntryAssembly().Location,
|
|
Arguments = string.Join(" ", Environment.GetCommandLineArgs().Skip(1)) + " /NoElevation"
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// May terminate the NAPS2 GUI based on the command-line arguments and running processes, sending messages to other processes if appropriate.
|
|
/// </summary>
|
|
public void ExitIfRedundant()
|
|
{
|
|
if (sti.ShouldRegister || sti.ShouldUnregister || shouldCreateEventSource)
|
|
{
|
|
// Was just started by the user to (un)register STI
|
|
Environment.Exit(returnCode);
|
|
}
|
|
|
|
// If this instance of NAPS2 was spawned by STI, then there may be another instance of NAPS2 we want to get the scan signal instead
|
|
if (sti.ShouldScan)
|
|
{
|
|
// Try each possible process in turn until one receives the message (most recently started first)
|
|
foreach (var process in GetOtherNaps2Processes())
|
|
{
|
|
// Another instance of NAPS2 is running, so send it the "Scan" signal
|
|
ActivateProcess(process);
|
|
if (Pipes.SendMessage(process, Pipes.MSG_SCAN_WITH_DEVICE + sti.DeviceID))
|
|
{
|
|
// Successful, so this instance can be closed before showing any UI
|
|
Environment.Exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only start one instance if configured for SingleInstance
|
|
if (configProvider.Get(c => c.SingleInstance))
|
|
{
|
|
// See if there's another NAPS2 process running
|
|
foreach (var process in GetOtherNaps2Processes())
|
|
{
|
|
// Another instance of NAPS2 is running, so send it the "Activate" signal
|
|
ActivateProcess(process);
|
|
if (Pipes.SendMessage(process, Pipes.MSG_ACTIVATE))
|
|
{
|
|
// Successful, so this instance should be closed
|
|
Environment.Exit(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ActivateProcess(Process process)
|
|
{
|
|
if (process.MainWindowHandle != IntPtr.Zero)
|
|
{
|
|
Win32.SetForegroundWindow(process.MainWindowHandle);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<Process> GetOtherNaps2Processes()
|
|
{
|
|
Process currentProcess = Process.GetCurrentProcess();
|
|
var otherProcesses = Process.GetProcessesByName(currentProcess.ProcessName)
|
|
.Where(x => x.Id != currentProcess.Id)
|
|
.OrderByDescending(x => x.StartTime);
|
|
return otherProcesses;
|
|
}
|
|
}
|
|
}
|