如何操作我的程序的另一个实例

前言

 

最近发现CSDN删除了我搜集的链接,这些链接收集了一些我过去对CSDN网友问题的回答和讨论。有些问题比较典型,有些代码值得回头引用。更可惜的是,在用户参与的帖子列表中,我再找不到2009年以前的我参与的贴子。

 

想一想还是整理一些贴子,发到博客中,也好留底。

 

问题

 

我的目的是:在已经有一个实例运行的情况下,如果再运行同一个程序,再次运行的程序会找到那个已经运行的进程并取得其主窗口句柄,然后调用已经存在的主窗口的一个方法进行一些操作,比如传递个数据,之后再次运行的那个进程退出,而已经存在的进程接收到传入的数据之后做相应的处理,并把自己的主窗口激活。

 

我想到的方法有两个:

1.在Main里给找到的已经存在的主窗口发送自定义消息,主窗口接收并处理。
2.把找到的主窗口句柄转换为类MainForm的实例,然后直接调用MainForm的一个方法。

觉得1可能能实现,但2如何做转换呢?或者还有其他方法?

 

回答

 

2.把找到的主窗口句柄转换为类MainForm的实例,然后直接调用MainForm的一个方法。
不能这样做。 由于进程的隔离,如果窗口句柄不属于在本进程中创建的托管窗口,你并不能将找到的主窗口句柄转换为MainForm的实例。

当一个托管Control(Winform也是Control)收到第一个窗口消息的时候,这个托管实例以及它对应的窗口句柄被缓存到Control的一个静态数组中(HandleBucket[] hashBuckets)。随后,当你调用Control.FromHandle(IntPtr)的时候,实际上是到hashBuckets中匹配窗口句柄,并返回缓存的托管实例。 一个进程并不能接触到其他进程的hashBuckets,所以不能用Control.FromHandle()等方法。

1.在Main里给找到的已经存在的主窗口发送自定义消息,主窗口接收并处理。
这个可以。 要小心的是,如果已经运行的窗口被隐藏(比如放到系统托盘上),那么它Process.MainWindowHandle返回的句柄可能为空。 拿不到它的句柄,你就不能向它发窗口消息。


觉得1可能能实现,但2如何做转换呢?或者还有其他方法?
可以用命名事件来通知,而对于数量不多的参数,可以通过注册表来传递,详见例子。

其他进程间通讯还可以用Remoting(包括WCF),Socket,窗口消息,共享内存,命名管道,数据库中转等等。

 

// Program.cs
using System;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Win32;
using System.Diagnostics;

namespace WindowsApplication1
{
   
static class Program
    {
        [STAThread]
       
static void Main()
        {
           
// 尝试创建一个命名事件
            bool createNew;
            ProgramStarted
= new EventWaitHandle(false, EventResetMode.AutoReset, "MyStartEvent", out createNew);

           
// 如果该命名事件已经存在(存在有前一个运行实例),则
            if (!createNew)
            {
               
// 先写一些数据到注册表中,以便传递给前一个运行实例
                Registry.SetValue(@"HKEY_CURRENT_USER/Software/MyMy", "", DateTime.Now.ToLongTimeString());

               
// 发事件通知
                ProgramStarted.Set();

               
// 等一小会以便前一个运行实例接到通知后恢复显示(也可以采用等待事件通知的方式)
                Thread.Sleep(200);

               
// 将焦点转移到前一个实例
                foreach (Process p in Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName))
                {
                   
if (p.MainWindowHandle != IntPtr.Zero)
                    {
                        SetForegroundWindow(p.MainWindowHandle);
                    }
                }

               
// 就此退出第二个进程
                return;
            }

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(
false);
            Application.Run(
new Form1());
        }
       
public static EventWaitHandle ProgramStarted;

        [System.Runtime.InteropServices.DllImport(
"user32.dll")]
       
static extern bool SetForegroundWindow(IntPtr hWnd);
    }
}

 

// Form1.cs
using System;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Win32;

namespace WindowsApplication1
{
   
public partial class Form1 : Form
    {
        NotifyIcon notifyIcon1
= new NotifyIcon();

       
public Form1()
        {
           
//InitializeComponent();
            this.Controls.Add(new TextBox());

           
this.notifyIcon1.Text = "Double click me to show window";
           
this.notifyIcon1.Icon = System.Drawing.SystemIcons.Question;
           
this.notifyIcon1.DoubleClick += OnNotifyIconDoubleClicked;

           
this.SizeChanged += OnSizeChanged;
            ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted,
null, -1, false);
        }

       
// 当最小化时,放到系统托盘。
        void OnSizeChanged(object sender, EventArgs e)
        {
           
if (this.WindowState == FormWindowState.Minimized)
            {
               
this.notifyIcon1.Visible = true;
               
this.Visible = false;
            }
        }

       
// 当双击托盘图标时,恢复窗口显示
        void OnNotifyIconDoubleClicked(object sender, EventArgs e)
        {
           
this.Visible = true;
           
this.notifyIcon1.Visible = false;
           
this.WindowState = FormWindowState.Normal;
        }

       
// 当收到第二个进程的通知时,从注册表中获得传入的参数,并恢复窗口显示
        void OnProgramStarted(object state, bool timeout)
        {
           
if (this.InvokeRequired)
            {
               
this.Invoke(new WaitOrTimerCallback(OnProgramStarted), state, timeout);
            }
           
else
            {
               
object param = Registry.GetValue(@"HKEY_CURRENT_USER/Software/MyMy", "", string.Empty);
               
this.Text = param as string;
                OnNotifyIconDoubleClicked(
this, EventArgs.Empty);
            }
        }
    }
}

原文连接为http://topic.csdn.net/u/20081221/01/457bb3b1-2f19-47e2-9621-cf4117ee45ce.html

本文有一些小改动。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值