c# 防止重复运行 弹出已运行窗口并传递消息

本文描述了一款软件的开发过程,该软件用于接收其他程序的命令行并以列表形式展示。作者遇到了界面美化和防止程序重复运行的问题。通过使用datagridview控件解决按钮绘制难题,并通过进程管理和消息传递机制确保只有一个窗口显示所有数据,同时新接收的命令行能传递给已存在的窗口。
摘要由CSDN通过智能技术生成

最近在写一款软件

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

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

界面预览

原本为了程序美观是打算用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

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值