【问题标题】:Open file in the currently running instance of my program?在我的程序当前运行的实例中打开文件?
【发布时间】:2015-01-12 09:00:26
【问题描述】:

一个例子:

如果您已打开并运行 Visual Studio ( 2010 ),然后双击 PC 桌面上的 misc *.cs 文件,该文件将在当前运行的 Visual Studio 实例中打开,而不是打开另一个 VS 实例.

如何让自己的 C# 程序模仿这种行为?

换句话说,如果我有一个文件类型,例如与我的程序关联的 *.myfile,并且用户在 Windows 资源管理器中双击 *.myfile,并且.... 我的程序已经在运行..... 它应该在没有 Windows 启动我的程序的另一个实例的情况下打开文件。如果程序没有运行,那么 Windows 可以正常启动实例。

请注意,我的程序允许多个实例 - 与 Visual Studio 相同。

任何建议将不胜感激!

【问题讨论】:

  • 您是否尝试检查系统中当前正在运行的进程,如果您的程序已经在运行,可能会向它传递一些信息?澄清一下,您在程序开始时执行此操作,然后再显示任何 UI,如果有实例,只需将信息传递给它并关闭新实例
  • @Jafar Kofahi - 感谢您的回复。根据您的评论,听起来该程序的第二个实例需要短暂启动,检查文件是否可以传递给另一个正在运行的实例,然后传递文件并在找到时关闭。我想过这个,但是程序很大。我想我正在寻找一种方法来告诉 Windows 不要启动另一个实例,然后在用户双击相关文件时以某种方式收到通知。
  • @Neolisk - 感谢您的链接。如果我无法找到一种方法来告诉 Windows 禁止启动第二个实例并只收到通知,这就是后备计划。

标签: c# instance double-click file-association


【解决方案1】:

如果您查看注册表中为 .cs 文件注册的内容,您会发现它不是 Visual Studio。对于 Express 版本,例如注册的应用程序是“VCSExpress.exe”,工作室在“WDExpress.exe”中运行。在高级版本中,我认为工作室运行为“devenv.exe”。有趣的一点是有两个应用程序:您的 UI 应用程序和一种启动器应用程序。我不知道 VS 是如何做到的,但我可以这样想象:启动器通过任何类型的进程间通信与 UI 通信,例如命名管道。 (See here) 不妨试试这个:

  • 启动器应用程序(您的文件扩展名已向其注册)尝试作为客户端打开到 UI 应用程序的管道。
  • 如果失败,它会启动一个新的 UI 应用程序实例并将文件名作为参数传递。 UI 应用启动命名管道的服务器端
  • 如果管道打开成功,即已经有一个 UI 实例正在运行,启动器通过管道将文件名发送到现有的 UI 进程。
  • 将作业传递给 UI 后启动器退出

【讨论】:

  • 当用户双击关联的文件时,似乎没有什么可以启动“某些”程序,因此在后台启动一个单独的加载程序是最有意义的。该程序可以保持很小,因此加载速度非常快。作为奖励,我可以使用相同的程序来协调主应用程序的自动更新。感谢您调查 Visual Studio 行为。由于这在技术上回答了我的问题(如何模仿 Visual Studio),我将其标记为答案。谢谢弗拉蒂克斯!!
【解决方案2】:

我使用 windows-messaging 制作了一些实现这些东西的项目模板。 该模板很大,并且包含一些其他内容(如本地化、更新、表单关闭、剪贴板和文档界面,这样可以轻松地将 MDI 中的操作转发给 MDI 子级)。 如果要查看模板,请尝试this link(或this link

部分代码:

Win32.cs:

public partial class Win32
{
    //public const int WM_CLOSE = 16;
    //public const int BN_CLICKED = 245;
    public const int WM_COPYDATA = 0x004A;

    public struct CopyDataStruct : IDisposable
    {
        public IntPtr dwData;
        public int cbData;
        public IntPtr lpData;

        public void Dispose()
        {
            if (this.lpData != IntPtr.Zero)
            {
                LocalFree(this.lpData);
                this.lpData = IntPtr.Zero;
            }
        }
    }

    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr LocalAlloc(int flag, int size);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr LocalFree(IntPtr p);

}

程序.cs:

static class Program
{
    static Mutex mutex = new Mutex(true, guid());
    static string guid()
    {
        // http://stackoverflow.com/questions/502303/how-do-i-programmatically-get-the-guid-of-an-application-in-net2-0
        Assembly assembly = Assembly.GetExecutingAssembly();
        var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
        return attribute.Value;
    }

    static int MainWindowHandle
    {
        get
        {
            return Settings.Default.hwnd;
        }
        set
        {
            Settings sett = Settings.Default;
            sett.hwnd = value;
            sett.Save();
        }
    }
    public static string GetFileName()
    {
        ActivationArguments a = AppDomain.CurrentDomain.SetupInformation.ActivationArguments;
        // aangeklikt bestand achterhalen
        string[] args = a == null ? null : a.ActivationData;
        return args == null ? "" : args[0];
    }

    [STAThread]
    static void Main()
    {
        if (mutex.WaitOne(TimeSpan.Zero, true))
        {
            #region standaard
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            #endregion
            #region Culture instellen
            string cult = CultureInfo.CurrentCulture.Name;
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(cult);
            Thread.CurrentThread.CurrentCulture = new CultureInfo(cult);
            #endregion
            MainForm frm = new MainForm();
            MainWindowHandle = (int)frm.Handle;
            Application.Run(frm);
            MainWindowHandle = 0;
            mutex.ReleaseMutex();
        }
        else
        {
            int hwnd = 0;
            while (hwnd == 0)
            {
                Thread.Sleep(600);
                hwnd = MainWindowHandle;
            }
            if (hwnd != 0)
            {
                Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
                try
                {
                    string data = GetFileName();
                    cds.cbData = (data.Length + 1) * 2; // number of bytes
                    cds.lpData = Win32.LocalAlloc(0x40, cds.cbData); // known local-pointer in RAM
                    Marshal.Copy(data.ToCharArray(), 0, cds.lpData, data.Length); // Copy data to preserved local-pointer
                    cds.dwData = (IntPtr)1;
                    Win32.SendMessage((IntPtr)hwnd, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
                }
                finally
                {
                    cds.Dispose();
                }
            }
        }
    }
}

MainFrom.cs:

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case Win32.WM_COPYDATA:
            Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
            string strData = Marshal.PtrToStringUni(st.lpData);
            OpenFile(strData);
            Activate();
            break;
        default:
            // let the base class deal with it
            base.WndProc(ref m);
            break;
    }
}

它甚至可以同时启动多达 15 个文件。

【讨论】:

    【解决方案3】:

    我不得不做这样的事情已经快 20 年了,但是 IIRC,你做这样的事情:

    1. 首先,创建一个 Mailslot(或任何其他方便的 IPC 工具)
    2. 如果要求您打开应该转到现有实例的类型的文档,并且如果没有其他中转槽,则继续
    3. 如果有 Mailslot,则向 Mailslot 发送带有文件信息的打开消息,然后退出。
    4. 编写代码以响应 Mailslot open 消息。

    如果您在创建窗口之前执行这些步骤,它应该会像您想要的那样。

    【讨论】:

    • 感谢您的回答。从技术上讲,这仍在启动该程序的另一个实例,但它只会与第一个实例通信并关闭。你是对的,我可以在创建任何窗口之前执行此检查,并且它对用户是不可见的。如果没有人可以在不启动其他实例的情况下建议一种方法来执行此操作,我会将其标记为已接受的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 2023-03-12
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 2012-05-23
    • 1970-01-01
    相关资源
    最近更新 更多