ColinOL

A Small Website

用户工具

站点工具


wiki:csharp:winform-fz-cx-dkdc

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

wiki:csharp:winform-fz-cx-dkdc [2018/02/03 15:42] (当前版本)
行 1: 行 1:
 +====== 防止窗体打开多次 ======
 +
 +<code csharp>
 +/// <​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);​
 +}
 +
 +</​code>​
 +
 +====== 防止程序打开、运行、启动多次(a,GUID) ======
 +
 +实现思路:
 +
 +在Main()方法开始时遍历所有进程,获取每个进程的程序集GUID和PID,若发现有跟自己GUID相同且PID不同的进程,就勒令自身退出。
 +注:
 +  - 采用GUID是为了尽可能保证判定的可靠性,采用进程名太不靠谱。而且程序集GUID是建立项目时就生成的,不随版本、内容的变化而变化,所以除非人为改动,否则同一项目编译若干次都还是那个GUID,用来判断程序集身份再适合不过。题外,网上盛传的互斥体方法,互斥名也建议用GUID;
 +  - 之所以要加上进程ID的判断,是因为遍历的进程中已经包含自身进程,所以必须排除自身;
 +  - 访问某些进程的MainModule属性会引发异常,所以采用try-catch跳过这些进程;
 +  - 经尝试只有C#​写的程序才能获取到GUID(有点废话~),但这样已经足够;​
 +  - 退出自身这里采用的是Environment.Exit()方法,Application.Exit()方法不管用,程序仍然会运行,我猜原因是Application都还没Run过,所以Exit不了~(小弟入门水平,很多东西只能靠坑蒙拐骗~哦不,是连蒙带猜)
 +
 +
 +
 +<code csharp 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());
 +        }
 +    }
 +}
 +
 +</​code>​
 +
 +====== 防止程序启动多次 ======
 +
 +经常我们会有这样的需求,只让应用程序运行一个实体。通常我们的情况是,双击一个exe文件,就运行一个程序的实体,再双击一次这个exe文件,又运行这个应用程序的另一个实体。就拿QQ游戏来说吧,一台电脑上一般只能运行一个QQ游戏大厅(不过以前听说过有双开的****)。
 +那我们的程序也能像QQ游戏那里禁止多次启动吗,答案是可以的,下面介绍下一个简单的实现方法,那就是Mutex(互斥)。
 +
 +Mutex(mutual exclusion,​互斥)是.Net Framework中提供跨多个线程同步访问的一个类。它非常类似了Monitor类,因为他们都只有一个线程能拥有锁定。而操作系统能够识别有名称的互斥,我们可以给互斥一个唯一的名称,在程序启动之前加一个这样的互斥。这样每次程序启动之前,都会检查这个命名的互斥是否存在。如果存在,应用程序就退出。
 +
 +
 +<code csharp 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);
 +}
 +
 +</​code>​
 +
  
wiki/csharp/winform-fz-cx-dkdc.txt · 最后更改: 2018/02/03 15:42 (外部编辑)