一、Process.Start()调用了Win32的CreateProcess()函数,并且参数bInheritHandles设置为了true,这就导致了子进程继承了父进程的Handle。
二、由于上述原因导致的问题
如果父进程用Process.Start()启动了子进程,并使用ChannelServices.RegisterChannel(channel);注册了channel后父进程结束而子进程不结束,接下来再次运行父进程并使用ChannelServices.RegisterChannel(channel);注册了channel时就会出错:
System.Net.Sockets.SocketException: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
三、解决方法
通过P/Invoking直接调用Win32的CreateProcess()函数,并设置bInheritHandles为false。
四、例子参见
http://forums.msdn.microsoft.com/en-US/netfxremoting/thread/6343e5ea-b39e-4a59-aafa-371f042698f6/
我把这个页面中的几个关键帖子拷贝到下面:
帖子1:
The problem is that my .NET 1.1 remoted server application will not restart if it has spawned another process that is inherited (startInfo.UseShellExecute = false). The error when it is started the second time is"Only one usage of each socket address (protocol/network address/port) is normally permitted". Once the inherited process is stopped the server will restart with not problem. I cannot seem to find a way to get the remoting server to really let go of the port.
I have created a small application to demostate the problem. This is not a practical application but it does demostrate the problem.
1. Run the application once. No problem. The application spawns an command window.
2. Run the application again. It crashes.
3. Close the command window from the previous execution.
4. Run the application again. No problems. New command window is started.
Here is the source:
using System.Collections;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Lifetime;
namespace ConsoleApplication2
{
class Class1 : MarshalByRefObject
{
public override object InitializeLifetimeService()
{
return base .InitializeLifetimeService ();
}
public int GetVersion()
{
return 5 ;
}
[STAThread]
static void Main( string [] args)
{
// Create the channel
BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props[ " name " ] = " TCP1 " ;
props[ " timeout " ] = 5000 ;
props[ " port " ] = 4100 ;
props[ " exclusiveAddressUse " ] = false ;
TcpChannel channel = new TcpChannel(props, clientProv, serverProv);
ChannelServices.RegisterChannel(channel);
Class1 class1 = new Class1();
RemotingServices.Marshal(class1, " class1 " );
ProcessStartInfo startInfo = new ProcessStartInfo( " cmd.exe " );
startInfo.UseShellExecute = false ;
Process process = Process.Start(startInfo);
RemotingServices.Disconnect(class1);
ChannelServices.UnregisterChannel(channel);
channel = null ;
class1 = null ;
startInfo = null ;
process = null ;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
帖子2:
Sounds like cmd.exe is inheriting the handles of the parent process. That is normally controlled by the bInheritHandles argument of CreateProcess(). The .NET ProcessInfo class doesn't let you specify whether or not handles are inherited. And Process.Start() indeed calls CreateProcess() with bInheritHandles = true. You could try P/Invoking CreateProcess() to override this behavior.
五、在.net下调用CreateProcess()的方法参见http://www.pinvoke.net/default.aspx/kernel32/CreateProcess.html