我正在制作一个可以打开自定义文档的应用程序。我将文档扩展名连接到应用程序(使用注册表),但是当我打开文档时,它总是以应用程序的新实例打开。
我想要一些可以打开运行当前进程的文档(如果存在)的逻辑。我不是指单个实例。它应该能够由多个实例运行。与 IE 或 chrome 一样,它应该能够在进程运行时使用 tab 打开 HTML 文件,但它也可以运行新实例。
我该怎么做?
我正在制作一个可以打开自定义文档的应用程序。我将文档扩展名连接到应用程序(使用注册表),但是当我打开文档时,它总是以应用程序的新实例打开。
我想要一些可以打开运行当前进程的文档(如果存在)的逻辑。我不是指单个实例。它应该能够由多个实例运行。与 IE 或 chrome 一样,它应该能够在进程运行时使用 tab 打开 HTML 文件,但它也可以运行新实例。
我该怎么做?
这篇文章包含一个很好的描述(图片也是从那里拍摄的)。
该方法使用带有对象的ThreadPoolEventWaitHandle
对象在进程之间传递消息(对象)( . Net Remoting)。
当应用程序启动时,它使用CreateSingleInstance()
调用现有实例或将自己注册为单实例应用程序。
public static bool CreateSingleInstance( string name, EventHandler<InstanceCallbackEventArgs> callback )
{
EventWaitHandle eventWaitHandle = null;
int curSessionId = System.Diagnostics.Process.GetCurrentProcess().SessionId;
name += curSessionId;
string eventName = string.Format( "{0}-{1}", Environment.MachineName, name );
// If there is another instance
InstanceProxy.IsFirstInstance = false;
InstanceProxy.CommandLineArgs = Environment.GetCommandLineArgs();
try
{
//try to open a handle with the eventName
eventWaitHandle = EventWaitHandle.OpenExisting( eventName );
}
catch
{
InstanceProxy.IsFirstInstance = true;
}
if( InstanceProxy.IsFirstInstance )
{
eventWaitHandle = new EventWaitHandle( false, EventResetMode.AutoReset, eventName );
// register wait handle for this instance (process)
ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false );
eventWaitHandle.Close();
// register shared type (used to pass data between processes)
RegisterRemoteType( name );
}
else
{
// here will be the code for the second instance/
}
return InstanceProxy.IsFirstInstance;
}
private static void RegisterRemoteType( string uri )
{
// register remote channel (net-pipes)
var serverChannel = new IpcServerChannel( Environment.MachineName + uri );
ChannelServices.RegisterChannel( serverChannel, true );
// register shared type
RemotingConfiguration.RegisterWellKnownServiceType(
typeof( InstanceProxy ), uri, WellKnownObjectMode.Singleton );
// close channel, on process exit
Process process = Process.GetCurrentProcess();
process.Exited += delegate
{
ChannelServices.UnregisterChannel( serverChannel );
};
}
[Serializable]
[System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust" )]
internal class InstanceProxy : MarshalByRefObject
{
private static bool firstInstance;
private static string[] arrCommandLineArgs;
public static bool IsFirstInstance
{
get
{
return firstInstance;
}
set
{
firstInstance = value;
}
}
public static string[] CommandLineArgs
{
get
{
return arrCommandLineArgs;
}
set
{
arrCommandLineArgs = value;
}
}
public void SetCommandLineArgs( bool isFirstInstance, string[] commandLineArgs )
{
firstInstance = isFirstInstance;
arrCommandLineArgs = commandLineArgs;
}
}
public class InstanceCallbackEventArgs : EventArgs
{
private bool firstInstance;
private string[] arrCommandLineArgs;
internal InstanceCallbackEventArgs( bool isFirstInstance, string[] commandLineArgs )
{
firstInstance = isFirstInstance;
arrCommandLineArgs = commandLineArgs;
}
public bool IsFirstInstance
{
get
{
return firstInstance;
}
set
{
firstInstance = value;
}
}
public string[] CommandLineArgs
{
get
{
return arrCommandLineArgs;
}
set
{
arrCommandLineArgs = value;
}
}
}
这里有很多选择,其中一些是:
尝试使用历史悠久的 DDE,但它仍然被 MS Office 等许多应用程序使用。DDE 命令在文件扩展名的打开命令上注册(HKEY_CLASSES_ROOT\Excel.Sheet.8\shell\Open
例如)。如果应用程序尚未启动,则由操作系统启动,并提交 DDE 命令。如果启动,DDE 命令将提交给注册为 DDE 服务器的运行实例。
当您的进程开始时,尝试创建一个具有预定义名称的 IpcChannel。如果您的进程使用文件参数启动,请通过 IpcChannel 将文件名传递给正在运行的进程。问题是只有一个进程可以创建同名的 IpcChannel。如果该进程退出,则其他进程将没有开放通道。
每个进程都使用进程 ID 创建一个 IpcChannel。当您的进程以文件参数开头时,您枚举进程路径与您的路径相同的进程,然后使用 IpcChannel 连接到该进程(其中名称可以通过查看进程 id 获得),然后将文件名传递给它。
枚举进程路径与您的路径相同的进程,并发送包含您的文件名的 WM_COPYDATA 消息。