我正在创建一个可以打开自定义文档的应用程序。我将文档扩展名连接到应用程序(使用注册表),但是当我打开文档时,它总是打开一个新的应用程序实例。
我想要一些可以打开运行当前进程的文档的逻辑(如果存在)。我不是指一个单一的例子。它应该能够由多个实例运行。与IE或Chrome一样,它应该能够在进程运行时打开带有选项卡的HTML文件,但它也可以运行新实例。
我该怎么做?
答案 0 :(得分:3)
This article包含一个很好的描述(从那里拍摄的图像)。
该方法使用带有EventWaitHandle
对象的ThreadPool对象在进程(.Net Remoting)之间传递消息(对象)。
应用程序启动时,它使用CreateSingleInstance()
将现有实例 OR 注册为单实例应用程序。
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;
}
}
}
答案 1 :(得分:2)
这里有很多选择,其中一些是:
尝试使用历史悠久的DDE,但它仍然被MS Office等许多应用程序使用。 DDE命令在open命令中注册用于文件扩展名(例如,查看HKEY_CLASSES_ROOT\Excel.Sheet.8\shell\Open
)。如果应用程序尚未启动,则由OS启动,并提交DDE命令。如果启动,则将DDE命令提交给正在运行的实例,该实例已注册为DDE服务器。
当您的流程开始时,尝试使用预定义的名称创建IpcChannel。如果使用file参数启动进程,请通过IpcChannel将文件名传递给正在运行的进程。问题是只有一个进程可以创建具有相同名称的IpcChannel。如果该流程退出,则其他流程没有开放渠道。
每个进程都使用进程ID创建一个IpcChannel。当您的进程以文件参数开头时,您枚举进程'path与您的路径相同的进程,然后使用IpcChannel连接到该进程(其中name可以通过查看进程ID获得),然后将filename传递给它。
枚举进程'路径与您的路径相同的进程,并发送包含您的文件名的WM_COPYDATA消息。