以管理员身份重新启动应用程序,使用互斥锁

时间:2015-04-17 20:49:28

标签: c# .net mutex

我有一个应用程序,它使用互斥锁来阻止多个实例同时运行,并接受命令行输入到正在运行的实例。

我在应用程序中有一个功能,询问用户是否要在需要时以管理员身份重新启动。例如,他们启用的功能可能需要管理员权限。

互斥类看起来像这样:

namespace SingleInstanceClassLibrary
{
/// <summary>
/// Enforces single instance for an application.
/// </summary>
public class SingleInstance : IDisposable
{
    private Mutex mutex = null;
    private Boolean ownsMutex = false;
    private Guid identifier = Guid.Empty;

    /// <summary>
    /// Enforces single instance for an application.
    /// </summary>
    /// <param name="identifier">An identifier unique to this application.       </param>

    public SingleInstance(Guid identifier)
    {
        this.identifier = identifier;
        mutex = new Mutex(true, identifier.ToString(), out ownsMutex);
    }

    /// <summary>
    /// Indicates whether this is the first instance of this application.
    /// </summary>
    public Boolean IsFirstInstance
    { get { return ownsMutex; } }

    /// <summary>
    /// Passes the given arguments to the first running instance of the     application.
    /// </summary>
    /// <param name="arguments">The arguments to pass.</param>
    /// <returns>Return true if the operation succeded, false otherwise.    </returns>

    public Boolean PassArgumentsToFirstInstance(String[] arguments)
    {
        if (IsFirstInstance)
            throw new InvalidOperationException("This is the first instance.");

        try
        {
            using (NamedPipeClientStream client = new NamedPipeClientStream(identifier.ToString()))
            using (StreamWriter writer = new StreamWriter(client))
            {
                client.Connect(200);

                foreach (String argument in arguments)
                    writer.WriteLine(argument);
            }
            return true;
        }
        catch (TimeoutException)
        { } //Couldn't connect to server
        catch (IOException)
        { } //Pipe was broken

        return false;
    }

    /// <summary>
    /// Listens for arguments being passed from successive instances of the applicaiton.
    /// </summary>
    public void ListenForArgumentsFromSuccessiveInstances()
    {
        if (!IsFirstInstance)
            throw new InvalidOperationException("This is not the first instance.");
        ThreadPool.QueueUserWorkItem(new WaitCallback(ListenForArguments));
    }

    /// <summary>
    /// Listens for arguments on a named pipe.
    /// </summary>
    /// <param name="state">State object required by WaitCallback delegate.</param>
    private void ListenForArguments(Object state)
    {
        try
        {
            using (NamedPipeServerStream server = new NamedPipeServerStream(identifier.ToString()))
            using (StreamReader reader = new StreamReader(server))
            {
                server.WaitForConnection();

                List<String> arguments = new List<String>();
                while (server.IsConnected)
                    arguments.Add(reader.ReadLine());

                ThreadPool.QueueUserWorkItem(new WaitCallback(CallOnArgumentsReceived), arguments.ToArray());
            }
        }
        catch (IOException)
        { } //Pipe was broken
        finally
        {
            ListenForArguments(null);
        }
    }

    /// <summary>
    /// Calls the OnArgumentsReceived method casting the state Object to String[].
    /// </summary>
    /// <param name="state">The arguments to pass.</param>
    private void CallOnArgumentsReceived(Object state)
    {
        OnArgumentsReceived((String[])state);
    }
    /// <summary>
    /// Event raised when arguments are received from successive instances.
    /// </summary>
    public event EventHandler<ArgumentsReceivedEventArgs> ArgumentsReceived;
    /// <summary>
    /// Fires the ArgumentsReceived event.
    /// </summary>
    /// <param name="arguments">The arguments to pass with the ArgumentsReceivedEventArgs.</param>
    private void OnArgumentsReceived(String[] arguments)
    {
        if (ArgumentsReceived != null)
            ArgumentsReceived(this, new ArgumentsReceivedEventArgs() { Args = arguments });
    }

    #region IDisposable
    private Boolean disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (mutex != null && ownsMutex)
            {
                mutex.ReleaseMutex();
                mutex = null;
            }
            disposed = true;
        }
    }

    ~SingleInstance()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}

我称之为:

private static void Main()
    {
        Guid guid = new Guid("{6EAE2E61-E7EE-42bf-8EBE-BAB890C5410F}");



        //SingleInstance ensures only 1 instance of the app runs at one time. If another instance is started
        //it will be closed. If the 2nd instance included arguments these will be passed to 
        //the singleInstance_ArgumentsReceived event for originally running process
        using (SingleInstance singleInstance = new SingleInstance(guid))
        {
            //MessageBox.Show(Environment.GetCommandLineArgs().ToString());
            //if (Environment.GetCommandLineArgs().Contains("RunAsAdmin"))

                //MessageBox.Show("YES");

            if (singleInstance.IsFirstInstance || Environment.GetCommandLineArgs().Contains("RunAsAdmin"))
            {                 
                singleInstance.ArgumentsReceived += singleInstance_ArgumentsReceived;
                singleInstance.ListenForArgumentsFromSuccessiveInstances();

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                // Show the system tray icon.                   
                //using (ProcessIcon pi = new ProcessIcon())
                using (ProcessIcon pi = new ProcessIcon())
                {
                    //Use to pass instance of ProcessIcon to LyncPresenceSwitcher
                    lyncPresenceSwitcher.processIcon = pi;

                    //Pass Lync instance
                    pi.lync = lyncClientController;

                    pi.Display();

                    // Make sure the application runs!
                    Application.Run();
                }
            }
            else
                singleInstance.PassArgumentsToFirstInstance(Environment.GetCommandLineArgs());
        }
    }

    //Process arguments past with app execution
    private static void singleInstance_ArgumentsReceived(object sender, ArgumentsReceivedEventArgs e)
    {
        if (settingsBox == null)
            return;

        foreach (String arg in e.Args)
        {
            //if arguments include OpenSettings open SettingsBox
            if (arg == "OpenSettings")
            {
                settingsBox.ShowDialog();
            }

            if (arg == "RunAsAdmin")
            {
                //singleInstance.Dispose();

            }

        }
    }

应用程序会检查设置是否需要管理员访问权限,并在需要时提示用户以管理员身份重新启动。要重新启动应用程序,请执行以下操作:

 if (!IsRunAsAdmin())
        {
            // Launch itself as administrator
            ProcessStartInfo proc = new ProcessStartInfo();
            proc.UseShellExecute = true;
            proc.WorkingDirectory = Environment.CurrentDirectory;
            proc.FileName = Application.ExecutablePath;
            proc.Verb = "runas";
            proc.Arguments = "RunAsAdmin";

            try
            {

                Process.Start(proc);
                System.Environment.Exit(2);

                return true;

            }
            catch
            {
                // The user refused the elevation.
                // Do nothing and return directly ...
                return false;
            }

问题是新实例启动但由于互斥锁而关闭。然后,当我关闭原始实例时,我得到了这个:

null reference error

所以我想我可以将一个参数传递给正在运行的实例,告诉它允许一个新的生成,然后关闭原始实例。麻烦的是我无法弄清楚如何使这项工作。

非常感谢任何帮助:)

P.S。我是C#新手

谢谢!

1 个答案:

答案 0 :(得分:-1)

解决方案是在重新启动应用程序之前简单地销毁你的互斥锁:

mutex.Dispose();