在前一个实例中打开文件——SharpDevelop分析笔记

来源:互联网 发布:手机抢票软件下载 编辑:程序博客网 时间:2024/04/29 12:17

在前一个实例中打开文件

——SharpDevelop分析笔记

当我们在windows文件夹中双击一个文件的时候,有的程序可以实现在一个已经打开的应用程序中打开该文件。在sharpdevelop中提供了类似的实现。

 

原理是,在新开的应用程序实例中将接收到的参数,主要是文件名,写到一个临时的文件中,然后在window进程中寻找前一个实例,并向其主窗口发送自定义消息。而前一个实例的主窗口会捕获这个自定义消息,并从临时文件夹中读取参数信息,然后打开这些文件。

 

下面我们看具体实现。

 

从系统启动到主画面显示主要用到以下几个class.

 Class图

SharpDevelopMain:这是存放应用程序入口Main函数的类,Main函数主要实现启动splashForm,并试图找到前一个实例并从前一个实例打开文件,如果没有前一个实例的话,就通过SharpDevelopHostRunWorkbench方法开启主窗口。

这里我们主要介绍试图找到前一个实例并从前一个实例打开文件的这条路线。

首先SplashForm这个类接收命令行参数并从中找到要打开的文件列表。然后Main函数调用SingleInstanceHelper这个类的OpenFilesInPreviousInstance方法。下面具体看这个方法的内容。

public static bool OpenFilesInPreviousInstance(string[] fileList)

{

    LoggingService.Info("Trying to pass arguments to previous instance...");

    //获取当前进程的Id

    int currentProcessId = Process.GetCurrentProcess().Id;

    //获取入口Assembly的文件名

    string currentFile = Assembly.GetEntryAssembly().Location;

    //产生一个随机数

    int number = new Random().Next();

    //用这个随机数生成一个临时文件名

    string fileName = Path.Combine(Path.GetTempPath(), "sd" + number + ".tmp");

    try {

        //将要打来的文件列表写入临时文件夹

        File.WriteAllLines(fileName, fileList);

        List<IntPtr> alternatives = new List<IntPtr>();

        //循环所有进程名称为"SharpDevelop"的进程

        foreach (Process p in Process.GetProcessesByName("SharpDevelop")) {

            //忽略掉当前进程

            if (p.Id == currentProcessId) continue;

            //从主模块的文件名再次确认是否本系统进程

            if (FileUtility.IsEqualFileName(currentFile, p.MainModule.FileName)) {

                //取得该进程的主窗口的窗口句柄

                IntPtr hWnd = p.MainWindowHandle;

                if (hWnd != IntPtr.Zero) {

                    //向该窗口发送自定义消息,消息中包含存放要打开的文件列表的临时文件名,本次发送消息,是优先选择没有打开任何文件的进程。

                    long result = NativeMethods.SendMessage(hWnd, CUSTOM_MESSAGE, new IntPtr(number), IntPtr.Zero).ToInt64();

                    if (result == RESULT_FILES_HANDLED) {

                        return true;

                    } else if (result == RESULT_PROJECT_IS_OPEN) {

                        //已经打开文件的进程放入列表

                        alternatives.Add(hWnd);

                    }

                }

            }

        }

        //当所有的进程都已经打开文件的情况下,则用第一个进程打开文件。

        foreach (IntPtr hWnd in alternatives) {

            if (NativeMethods.SendMessage(hWnd, CUSTOM_MESSAGE, new IntPtr(number), new IntPtr(1)).ToInt64()== RESULT_FILES_HANDLED) {

                return true;

            }

        }

        return false;

    } finally {

        File.Delete(fileName);

    }

}

 

可以看到,这段代码首先把要打开的文件列表写到一个以随机数命名的文本文件中,然后找到前一个进程的主窗口句柄,并向其发送Windows消息,消息中包含存放着要打开的文件名列表的文本文件的文件名。

SharpDevelop进程的主窗口正是DefaultWorkbench类的实例。该类覆写了Form类的WndProc方法。

 

protected override void WndProc(ref Message m)

{

    if (!SingleInstanceHelper.PreFilterMessage(ref m)) {

        base.WndProc(ref m);

    }

}

 

    当这个窗口的实例接受到Windows消息的时候,这个方法会被系统调用。这个方法的内容是,首先调用SingleInstanceHelper.PreFilterMessage方法,返回值为False的时候,调用父类的WndProc方法。

 

    那么,SingleInstanceHelper.PreFilterMessage的内容是什么呢。

 

internal static bool PreFilterMessage(ref Message m)

{

    if (m.Msg != CUSTOM_MESSAGE)

        return false;

    long fileNumber = m.WParam.ToInt64();

    long openEvenIfProjectIsOpened = m.LParam.ToInt64();

    LoggingService.Info("Receiving custom message...");

    //如果收到的消息是寻找没有打开任何文件的进程,并且当前进程已经打开文件的情况下,返回RESULT_PROJECT_IS_OPEN。否则用当前进程打开文件。

    if (openEvenIfProjectIsOpened == 0 && ProjectService.OpenSolution != null) {

        m.Result = new IntPtr(RESULT_PROJECT_IS_OPEN);

    } else {

        m.Result = new IntPtr(RESULT_FILES_HANDLED);

        try {

            //将当前进程的主窗口设为前台窗口。

            WorkbenchSingleton.SafeThreadAsyncCall(delegate { NativeMethods.SetForegroundWindow(WorkbenchSingleton.MainForm.Handle) ; });

            foreach (string file in File.ReadAllLines(Path.Combine(Path.GetTempPath(), "sd" + fileNumber + ".tmp"))) {

                WorkbenchSingleton.SafeThreadAsyncCall(delegate(string openFileName) { FileService.OpenFile(openFileName); }, file);

            }

        } catch (Exception ex) {

            LoggingService.Warn(ex);

        }

    }

    return true;

}

 

这段代码是在符合条件的进程中打开文件。具体由FileService.OpenFile方法来实现。

这样就大致实现了在前面进程打开文件的功能。

 

这里简单提一下WndProc方法。可能没有做过Win32编程的朋友不太熟悉。

Windows操作系统是一个基于消息的窗口系统,该系统通过向窗口发送消息,并由窗口来响应消息来实现用户交互。在dotnet开发中,如果继承了Form类,就利用了Windows的窗口功能。Form类的WndProc方法用来处理送到该窗口的消息,被系统调用。该方法处理Windows的内置消息,比如最大化,最小化。我们也可以发送自定义消息,但是默认的WndProc方法不会处理自定义消息。这就需要我们覆写WndProc方法来处理自定义消息。

原创粉丝点击