zoukankan      html  css  js  c++  java
  • c# 防止重复运行 弹出已运行窗口并传递消息

    最近在写一款软件

    软件是用来接收其他程序传递过来的命令行,并形成列表

    大概的最终效果就像下图一样

    原本为了程序美观是打算用listbox自绘列表,字和图片都绘制好了发现自己不会绘制按钮

    所以最终采用了datagridview控件,这个直接就可以插入按钮,就省去了那些问题,不过界面美观就一直是遗憾了(希望有界面方面的大牛可以指导我!)

    因为程序启动的方式是被其他程序启动并发送的有命令行

    所以,如果有多条消息默认情况下会被打开多个程序,因此开始了如下的各种解决方法

    1.命令行的读取

    这个相对就比较简单了,修改Program的Main方法

            [STAThread]
            static void Main(string[] args)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1(args));
            }

    这样就实现了获取命令行,命令行中间用空格隔开,最终程序得到args文本数组

    窗口新增获取数组的初始化方法

     public Form1(string[] args)
            {
                InitializeComponent();
                try
                {
                //这里是将args插入列表的操作
                   
                }
                catch (Exception)
                {
                }
            }            

    当然这些都是十分简单的,网上随手查阅就能找到

    接下来是程序禁止重复运行,因为我们想把列表显示在一个窗口中,而不是每个窗口都显示一条数据

    所以程序是不可以被重复运行的.Program.cs新增代码如下

            #region 防止重复运行
            public static Process RunningInstance()
            {
        
                Process current = Process.GetCurrentProcess();
                Process[] processes = Process.GetProcessesByName(current.ProcessName);
                foreach (Process process in processes)
                {
                    if (process.Id != current.Id)
                    {
                        if (Assembly.GetExecutingAssembly().Location.Replace("/", "\") == current.MainModule.FileName)
                        {
                            return process;
                        }
                    }
                }
                return null;
            }
            public static void HandleRunningInstance(Process instance)
            {
                ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);
                SetForegroundWindow(instance.MainWindowHandle);
            }
            [DllImport("User32.dll")]
            private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
            [DllImport("User32.dll")]
            private static extern bool SetForegroundWindow(IntPtr hWnd);
            private const int WS_SHOWNORMAL = 1; 
            #endregion

    新增之后再Main方法做判断,并且如果已经存在打开的窗口的话,就激活打开的窗口

    static void Main(string[] args)
            { 
                Process instance = RunningInstance();
                if (instance == null)
                {
     System.Windows.Forms.Application.EnableVisualStyles();   
                    System.Windows.Forms.Application.DoEvents();
                    System.Windows.Forms.Application.Run(new Form1(args));
                    
                }    
                else
                {
                    HandleRunningInstance(instance);
                }
            }

    做完这些,程序已经不能被重复运行了,再测试,新的问题又来了...程序没有被多次打开,但是同样的,列表永远只会有一条数据,不会被更新

    新运行的程序收到的命令行没能告知到旧的窗口,因此需要传递值到旧的窗口,这个方法也有很多,最终我采用的是SendMessage

    新增代码如下

           
    #region 发送消息 const int WM_COPYDATA = 0x004A; [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hWnd, int msg, uint wParam, ref COPYDATASTRUCT lParam); [DllImport("User32.dll", EntryPoint = "FindWindow")] private static extern int FindWindow(string lpClassName, string lpWindowName); public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } #endregion

    然后再封装一个方法

            static void Send(string[] args)
            {
                int WINDOW_HANDLER = FindWindow(null, @"这里要改成你的窗口标题,Form1的Text");
                if (WINDOW_HANDLER != 0)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (string item in args)
                    {
                        sb.Append(item + " ");
                    }
                    byte[] sarr = System.Text.Encoding.Default.GetBytes(sb.ToString());
                int len = sarr.Length;
                COPYDATASTRUCT cds;
                cds.dwData = (IntPtr)100;
                cds.lpData = sb.ToString();
                cds.cbData = len + 1;
                int sendRet = SendMessage((IntPtr)WINDOW_HANDLER, WM_COPYDATA, 0, ref cds);
                if (sendRet != 0)
                {
                    MessageBox.Show("传递参数失败!");
                }
                }
                else{
                MessageBox.Show("没有发现旧窗口!");
                }
            }

    接下来重新改动一下Main方法

     static void Main(string[] args)
            {
                Process instance = RunningInstance();
               //如果不存在旧窗口
                if (instance == null)
                {
                   System.Windows.Forms.Application.EnableVisualStyles();   
                    System.Windows.Forms.Application.DoEvents();
                    System.Windows.Forms.Application.Run(new Form1(args));
                    
                }    
                else
                {
                  //如果存在先激活
                    HandleRunningInstance(instance);
                    //如果收到的有参数就传递,没有就忽略
                    if (args.Length>0)
                    {
                        Send(args);    
                    }
                    
                    
                }
            }                

    这样,发送参数的部分就完成了.还剩下最后一步,Form中接收传递过来的参数

    在Form中新增如下代码

            #region 接收消息
            protected override void DefWndProc(ref System.Windows.Forms.Message m)
            {
                const int WM_COPYDATA = 0x004A;
                switch (m.Msg)
                {
                    case WM_COPYDATA:
                        COPYDATASTRUCT mystr = new COPYDATASTRUCT();
                        Type mytype = mystr.GetType();
                        mystr = (COPYDATASTRUCT)m.GetLParam(mytype);
                        string[] s = mystr.lpData.Split(new string[]{" "},StringSplitOptions.RemoveEmptyEntries);
                        if (s.Length>0)
                        {
                            //这里写收到参数的情况操作
                           //mystr.lpData为实际收到的文本,可以直接操作,我这里分割为数组来区分标题,网址等了
                        }
                        break;
    
                    default:
                        base.DefWndProc(ref m);
                        break;
                }
            }
            public struct COPYDATASTRUCT
            {
                public IntPtr dwData;
                public int cbData;
                [MarshalAs(UnmanagedType.LPStr)]
                public string lpData;
            }
            #endregion

    做完这些我们想要的效果就完全实现了,每次我们的程序被运行都只会有一个窗口,并且所有的数据会汇总在这里

    以上文章都乃小生的个人见解,可能会有小的错误或者更好的方法来实现.也希望大家能评论出来让我学习

    另外,希望有会自绘列表中带有按钮的大大们,可以指导一下小生

  • 相关阅读:
    JdbcTemplate增删改查案例
    顾问和注解
    使用多种方式实现AOP
    面试题
    Spring Bean的生命周期和作用域
    IOC和AOP交互拓展(二)
    AOP
    错题
    Spring核心概念
    hadoop-MapReduce框架原理之OutputFormat数据输出
  • 原文地址:https://www.cnblogs.com/jianzhan/p/4777678.html
Copyright © 2011-2022 走看看