using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.Win32; using NLog; using Shadowsocks.Util; namespace Shadowsocks.Controller { static class AutoStartup { private static Logger logger = LogManager.GetCurrentClassLogger(); // Don't use Application.ExecutablePath // see https://stackoverflow.com/questions/12945805/odd-c-sharp-path-issue private static readonly string ExecutablePath = Assembly.GetEntryAssembly().Location; private static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode(); public static bool Set(bool enabled) { RegistryKey runKey = null; try { runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); if (runKey == null) { logger.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run"); return false; } if (enabled) { runKey.SetValue(Key, ExecutablePath); } else { runKey.DeleteValue(Key); } // When autostartup setting change, change RegisterForRestart state to avoid start 2 times RegisterForRestart(!enabled); return true; } catch (Exception e) { logger.LogUsefulException(e); return false; } finally { if (runKey != null) { try { runKey.Close(); runKey.Dispose(); } catch (Exception e) { logger.LogUsefulException(e); } } } } public static bool Check() { RegistryKey runKey = null; try { runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); if (runKey == null) { logger.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run"); return false; } string[] runList = runKey.GetValueNames(); foreach (string item in runList) { if (item.Equals(Key, StringComparison.OrdinalIgnoreCase)) return true; else if (item.Equals("Shadowsocks", StringComparison.OrdinalIgnoreCase)) // Compatibility with older versions { string value = Convert.ToString(runKey.GetValue(item)); if (ExecutablePath.Equals(value, StringComparison.OrdinalIgnoreCase)) { runKey.DeleteValue(item); runKey.SetValue(Key, ExecutablePath); return true; } } } return false; } catch (Exception e) { logger.LogUsefulException(e); return false; } finally { if (runKey != null) { try { runKey.Close(); runKey.Dispose(); } catch (Exception e) { logger.LogUsefulException(e); } } } } [DllImport("kernel32.dll", SetLastError = true)] static extern int RegisterApplicationRestart([MarshalAs(UnmanagedType.LPWStr)] string commandLineArgs, int Flags); [DllImport("kernel32.dll", SetLastError = true)] static extern int UnregisterApplicationRestart(); [Flags] enum ApplicationRestartFlags { RESTART_ALWAYS = 0, RESTART_NO_CRASH = 1, RESTART_NO_HANG = 2, RESTART_NO_PATCH = 4, RESTART_NO_REBOOT = 8, } // register restart after system reboot/update public static void RegisterForRestart(bool register) { // requested register and not autostartup if (register && !Check()) { // escape command line parameter string[] args = new List(Program.Args) .Select(p => p.Replace("\"", "\\\"")) // escape " to \" .Select(p => p.IndexOf(" ") >= 0 ? "\"" + p + "\"" : p) // encapsule with " .ToArray(); string cmdline = string.Join(" ", args); // first parameter is process command line parameter // needn't include the name of the executable in the command line RegisterApplicationRestart(cmdline, (int)(ApplicationRestartFlags.RESTART_NO_CRASH | ApplicationRestartFlags.RESTART_NO_HANG)); logger.Debug("Register restart after system reboot, command line:" + cmdline); } // requested unregister, which has no side effect else if (!register) { UnregisterApplicationRestart(); logger.Debug("Unregister restart after system reboot"); } } } }