How to return the port from a program that self hosts as a WCF service

15 views Asked by At

I have a program that is able to expose itself as a self hosted WCF service. It does this when it's started as a process with certain parameters. In the past I've passed in a port for it to host on but I want to change that so it finds an available port and then returns that to the caller. (It also sets up a SessionBound End Point but that's incidental to this question). It self hosts to do this using the following code (this is working fine):-

        Uri baseAddress = new Uri("net.tcp://localhost:0/AquatorXVService");
        m_host = new ServiceHost(typeof(AquatorXV.Server.AquatorXVServiceInstance), baseAddress);
        ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
        smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        m_host.Description.Behaviors.Add(smb);

        m_host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
        NetTcpBinding endPointBinding = new NetTcpBinding()
        {
            MaxReceivedMessageSize = 2147483647,
            MaxBufferSize = 2147483647,
            MaxBufferPoolSize = 2147483647,
            SendTimeout = TimeSpan.MaxValue,
            OpenTimeout = new TimeSpan(0, 0, 20)
        };
        ServiceEndpoint endPoint = m_host.AddServiceEndpoint(typeof(AquatorXVServiceInterface.IAquatorXVServiceInstance), endPointBinding, baseAddress);
        endPoint.ListenUriMode = ListenUriMode.Unique;
        ServiceDebugBehavior debug = m_host.Description.Behaviors.Find<ServiceDebugBehavior>();
        if (debug == null) 
            m_host.Description.Behaviors.Add( new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
        else if (!debug.IncludeExceptionDetailInFaults) 
            debug.IncludeExceptionDetailInFaults = true;
        m_host.Open();
        int port = m_host.ChannelDispatchers.First().Listener.Uri.Port;

        //Start the session bound factory service
        Uri sessionBoundFactoryBaseAddress = new Uri("net.tcp://localhost:" + port.ToString() + "/AquatorXVSessionBoundFactoryService");
        m_sessionBoundFactoryHost = new ServiceHost(typeof(AquatorXV.Server.SessionBoundFactory), sessionBoundFactoryBaseAddress);
        ServiceMetadataBehavior smbFactory = new ServiceMetadataBehavior();
        smbFactory.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        m_sessionBoundFactoryHost.Description.Behaviors.Add(smbFactory);
        m_sessionBoundFactoryHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
        m_sessionBoundFactoryHost.AddServiceEndpoint(typeof(AquatorXVServiceInterface.ISessionBoundFactory), new NetTcpBinding(), sessionBoundFactoryBaseAddress);
        m_sessionBoundFactoryHost.Open();

        return port;

Ultimately, port is set as the return value of Main().

On the client side it starts the program as a process. It then needs to access the port number so it can connect to it. Here's what I've been trying to do:-

        ProcessStartInfo psi = new ProcessStartInfo(exePath, $"/remotingWCF") { UseShellExecute = false };
        mProcess = Process.Start(psi);
        mProcess.WaitForInputIdle();
        mPort = mProcess.ExitCode;
        CreateWCFClient(mPort);

The problem is that this fails when trying to access mProcess.ExitCode with a message: InvalidOperationException - Process Must Exit before requested information can be determined.

Googling around suggests that I use WaitForExit instead of WaitForInputIdle but that requires the program to actually be shut down before I can access ExitCode. I need to get the port from the running program.

I think this means that I won't be able to use ExitCode as a way to get the port back but I can't find a different mechanism. Can anyone suggest a way I can return a value from Process.Start without waiting for the program to close?

0

There are 0 answers