0

I'll open with this: I know you can only have one TcpListener listening to any given port, I'm trying to find a way to work around this limitation.

I am attempting to build an application that supports another application on the same machine. The other application, which I have no control over, sends out a signal on a TCP Port when it opens or closes a study.

In order to accept this TCP signal, I built a Windows Tray Application that listens to that port, accepts the signal, and then processes the data that was transmitted to it. The code block that manages this Listener looks like this...

    private TcpListener _tcpListener;
    private Thread _listenerThread;

    #region TCP Connection

    public void StartListener()
    {
        if (_listenerThread != null)
            StopListener();

        _listenerThread = new Thread(RunListener);
        _listenerThread.Start();
    }

    private void RunListener()
    {
        _tcpListener = new TcpListener(IPAddress.Any, this.TcpListenerPort);
        _tcpListener.Start();
        while (true)
        {
            Console.WriteLine(string.Format("Listening for connections on port {0}", this.TcpListenerPort));

            try
            {
                TcpClient client = _tcpListener.AcceptTcpClient();

                ThreadPool.QueueUserWorkItem(ProcessClient, client);
            }
            catch (SocketException ex)
            {
                // This occurs when we close the parent thread that runs the TcpListener because
                // the TcpListener call to AcceptTcpClient is a blocking call.
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("Exception trying to AcceptTcpClient: {0}", ex.Message));
            }
        }
    }

    private void StopListener()
    {
        if (_tcpListener != null)
            _tcpListener.Stop();

        if (_listenerThread != null)
            _listenerThread.Abort();
    }

As you can see, this is pretty simple and straight-forward. I spool up a thread to hold the Listener, then wait for a signal to come through, then do something with the data that it sent me. And all of this is working just fine.

The problem comes from this: This tray application is set up to run when a user logs in to the machine. However, due to the 'One listener per port' rule of the TCP Listener, if more than one user logs on to the machine at once (either via VPN or through 'switching' rather than logging off), then a second instance of this application attempts to start. And promptly crashes because it attempts to listen to a port that is already being listened to.

I cannot change the way information is sent to me by the other program, and I cannot force Windows Users to properly log off before switching to a new user. I cannot relocate any of these pieces to a different machine, either. However, this tray-app needs to be available to each and every user.

I have quite run out of ideas for how to resolve this, and any input would be extremely helpful.

Specifically, I'm trying to locate a method to hijack an existing thread hosting a TcpListener, co-opting it for use by more than one Windows Account at a time on the same machine. Granted, I will certainly not turn down a better idea than the one I have, either.

EDIT: I have considered running it as a Service, but one of the outcomes of data being received by the Listener is that the Tray Application launches a web browser, and it has a user interface for configuring its settings.

guildsbounty
  • 3,326
  • 3
  • 22
  • 34
  • 1
    Have you considered running your application as a Windows Service instead? – HasaniH Aug 25 '16 at 19:41
  • Unfortunately, this same app that manages the listener, also has a user interface for managing its settings, and needs to be able to launch desktop applications. Specifically, a web browser. I'll clarify that in the main question – guildsbounty Aug 25 '16 at 19:48
  • Then you probably want a separate service that acts as the listener for all attached UI apps. – David Schwartz Aug 25 '16 at 19:50
  • Settings can be managed with the ServiceController command interface and if you allow the service to interact with the desktop it can launch a browser. Is there a reason you can't have separate applications for different functionality you want? – HasaniH Aug 25 '16 at 19:52
  • Mostly because 'interactive services' is a legacy function...and only properly interacts with with the Terminal Services Session 0...and I'm dealing with machines that could see rapid user switching. MS recommends not using interactive services in such an environment...and the Interactive Services Detection service in Windows 10 can be....unreliable, to say the least. – guildsbounty Aug 25 '16 at 20:00
  • In all fairness, some of my information on that is potentially out of date...so if things have changed, it would be nice to know. – guildsbounty Aug 25 '16 at 20:03

2 Answers2

1

I cannot make comments yet, due to my rep, so this answer isn't really an answer, but you have a problem with your design. So you have 4 users logged into this machine, thus 4 instances of the UI for user settings and such. When any/all of the 4 UI apps receive the communication from the sending app, which if any are supposed to respond to it?

Is it all 4? If it is all 4, then the suggestion to use a Service to "Listen" is correct, 1 service listens to 1 port. When it gets something, it can then communicate with the UI applications. Have them Talk to the Service on starting and setup their own listening Port, so that the Service can keep track of who is still listening and command that app to open the web page, thus all 4 actively listening UI apps would open the web page.

If it is only 1 of the UI's should be responding, then you need to identify the rule to pick which one, then I would still think the Service would be the right way to go, and the Service could determine which of the 4 based on the rules defined, should get the command to open the webpage.

In the end, I still wouldn't do that, opening applications on users without their involvement can end in bad data, i.e. they are typing away on the keyboard and not even looking at the monitor and all of a sudden realize they have been typing into the wrong form for the last 4 enter presses, thinking they were typing an email. You sure Task-bar notifications isn't a better idea? You could make them as Intrusive as you wanted.

Earthworm
  • 116
  • 7
  • In fairness, these machines generally would only be accessed by an Admin via VPN. You would only have one person physically at the machine. Multiple accounts could be logged in to that machine in parallel, but only one will be active at any given time. So users will not see other things happening, since they won't be active if another user is. – guildsbounty Aug 25 '16 at 20:14
  • Hey, what do you know, I can comment now. That may be true, but, what do you do when the other session becomes active again and the web page is open there? Assume it was dealt with previously? – Earthworm Aug 25 '16 at 20:17
  • Unfortunately, this is something of a moot point due to the way the application works. It MUST be controlling the browser window, and everyone using this tray app will have that browser window open. The tray app simply launches the window if it isn't already there. Essentially the tray app is providing a means for a Desktop application to communicate with a Web Application, in absence of being able to change the Desktop Application to contact the web app independently, since I can't change the Desktop Application. However, you are correct that I would rather two sessions not step on each other – guildsbounty Aug 25 '16 at 21:00
  • @usr No, unfortunately, it does not. I already laid out in the comments on the main question why I can't use a Service (Only interacts with the Terminal Services Session 0 + instability in Win 10)... – guildsbounty Aug 26 '16 at 01:04
  • The service would not need a GUI. Each session starts its own GUI. Each session opens a communication channel with the service. The service only manages the port and relays data. Would that work? – usr Aug 26 '16 at 09:53
0

This is contrary to the idea of ports. The best that you could hope for is that all of the copies of the program receive and process the data in parallel.

Perhaps a better direction would be to have the tray app relinquish the port when the session becomes locked? See How to detect Windows is locked?

Community
  • 1
  • 1
JefferMC
  • 686
  • 6
  • 14
  • I'm going to look into this...it did not occur to me that it was possible to detect when a Windows Session locked or unlocked. I'll let ya know if I get somewhere with it. – guildsbounty Aug 25 '16 at 20:15