ColinOL

A Small Website

用户工具

站点工具


wiki:csharp:winform-fz-cx-dkdc

防止窗体打开多次

/// <summary>
/// 当前已经打开的窗口对象名称列表
/// </summary>
public static ArrayList FormList = new ArrayList();
/// <summary>
/// 查看已打开的窗口是否包括该名称的对象
/// </summary>
/// <param name="formName"></param>
/// <returns></returns>
public static bool IsFormExist(ref Form form, string formName)
{
    bool opened = false;
    foreach (Form frm in FormList)
    {
        if (frm.Name == formName)
        {
            opened = true;
            form = frm;
			break;
        }
    }
    return opened;
}
 
private void frmProjectManager_FormClosed(object sender, FormClosedEventArgs e)
{
    Functions.FormList.Remove(this);
}
 
/// <summary>
/// 新建项目
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ctBtb_NewProject_Click(object sender, EventArgs e)
{
    frmProjectManager frmProjectManager1 = null;
    Form frm = null;
    if (Functions.IsFormExist(ref frm, "frmProjectManager1"))
    {
        frmProjectManager1 = frm as frmProjectManager;
    }
    else
    {
        frmProjectManager1 = new frmProjectManager();
        Functions.FormList.Add(frmProjectManager1);
    }
    frmProjectManager1.Name = "frmProjectManager1";
    frmProjectManager1.Location = new Point(Functions.FrmBounds.Left, Functions.FrmBounds.Top);
    if(!frmProjectManager1.Visible)
        frmProjectManager1.Show(this);
}

防止程序打开、运行、启动多次(a,GUID)

实现思路:

在Main()方法开始时遍历所有进程,获取每个进程的程序集GUID和PID,若发现有跟自己GUID相同且PID不同的进程,就勒令自身退出。 注:

  1. 采用GUID是为了尽可能保证判定的可靠性,采用进程名太不靠谱。而且程序集GUID是建立项目时就生成的,不随版本、内容的变化而变化,所以除非人为改动,否则同一项目编译若干次都还是那个GUID,用来判断程序集身份再适合不过。题外,网上盛传的互斥体方法,互斥名也建议用GUID;
  2. 之所以要加上进程ID的判断,是因为遍历的进程中已经包含自身进程,所以必须排除自身;
  3. 访问某些进程的MainModule属性会引发异常,所以采用try-catch跳过这些进程;
  4. 经尝试只有C#写的程序才能获取到GUID(有点废话~),但这样已经足够;
  5. 退出自身这里采用的是Environment.Exit()方法,Application.Exit()方法不管用,程序仍然会运行,我猜原因是Application都还没Run过,所以Exit不了~(小弟入门水平,很多东西只能靠坑蒙拐骗~哦不,是连蒙带猜)
testallapirefreshpolicy.cs
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
namespace TestCallAPIRefreshPolicy
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Guid ownGUID = new Guid(((GuidAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(GuidAttribute))).Value);
            Guid proGUID;
            int ownPID = Process.GetCurrentProcess().Id;
            int proPID;
 
            foreach (Process p in Process.GetProcesses())
            {
                try
                {
                    proGUID = new Guid(((GuidAttribute)Attribute.GetCustomAttribute(Assembly.LoadFile(p.MainModule.FileName), typeof(GuidAttribute))).Value);
                    proPID = p.Id;
                    if (proGUID.Equals(ownGUID) && proPID != ownPID)
                    {
                        MessageBox.Show("程序已运行");
                        Environment.Exit(Environment.ExitCode);
                    }
                }
                catch
                {
                    continue;//遇上进程访问异常就跳过该进程
                }
            }
 
            //若未被Exit,正常启动
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FmMain());
        }
    }
}

防止程序启动多次

经常我们会有这样的需求,只让应用程序运行一个实体。通常我们的情况是,双击一个exe文件,就运行一个程序的实体,再双击一次这个exe文件,又运行这个应用程序的另一个实体。就拿QQ游戏来说吧,一台电脑上一般只能运行一个QQ游戏大厅(不过以前听说过有双开的)。 那我们的程序也能像QQ游戏那里禁止多次启动吗,答案是可以的,下面介绍下一个简单的实现方法,那就是Mutex(互斥)。

Mutex(mutual exclusion,互斥)是.Net Framework中提供跨多个线程同步访问的一个类。它非常类似了Monitor类,因为他们都只有一个线程能拥有锁定。而操作系统能够识别有名称的互斥,我们可以给互斥一个唯一的名称,在程序启动之前加一个这样的互斥。这样每次程序启动之前,都会检查这个命名的互斥是否存在。如果存在,应用程序就退出。

main.cs
static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        bool createdNew;
        //系统能够识别有名称的互斥,因此可以使用它禁止应用程序启动两次
        //第二个参数可以设置为产品的名称:Application.ProductName
 
        //每次启动应用程序,都会验证名称为SingletonWinAppMutex的互斥是否存在
        Mutex mutex = new Mutex(false, "SingletonWinAppMutex", out createdNew);
 
        //如果已运行,则在前端显示
        //createdNew == false,说明程序已运行
        if (!createdNew)
        {
            Process instance = GetExistProcess();
            if (instance != null)
            {
                SetForegroud(instance);
                Application.Exit();
                return;
            }
        }
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }
 
    /// <summary>
    /// 查看程序是否已经运行
    /// </summary>
    /// <returns></returns>
    private static Process GetExistProcess()
    {
        Process currentProcess = Process.GetCurrentProcess();
        foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
        {
            if ((process.Id != currentProcess.Id) && 
                (Assembly.GetExecutingAssembly().Location == currentProcess.MainModule.FileName))
            {
                return process;
            }
        }
        return null;
    }
 
    /// <summary>
    /// 使程序前端显示
    /// </summary>
    /// <param name="instance"></param>
    private static void SetForegroud(Process instance)
    {
        IntPtr mainFormHandle = instance.MainWindowHandle;
        if (mainFormHandle != IntPtr.Zero)
        {
            ShowWindowAsync(mainFormHandle, 1);
            SetForegroundWindow(mainFormHandle);
        }
    }
 
    [DllImport("User32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
 
    [DllImport("User32.dll")]
    private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
}
wiki/csharp/winform-fz-cx-dkdc.txt · 最后更改: 2018/02/03 20:42 (外部编辑)