none
Detecting client, workflow

    Question

  • My application must listen to the Lync client with GetClient(). This is how things should work.

    1. When client is available I must grab it and subscribe to some of its events.
    2. When a user is logged in, I must sub to some events.
    3. Client is exited out, go back to 1.

    But its not as simple as it looks. Some of my hurdles:

    • I can't detect when Lync is exited. How do you detect Client Exit?
    • What should I do about all the events I have subscribed to? Must I manually unsubscribe from events when disconnected? If I don't then the events will be subbed to by the same client and trigger multiple times unnecessarily.

    How do you guys handle this kind of situation? I'm not a pro and Id like to know how this should be handled.

    ps: I saw the threats about the disconnection issue, but it didn't help.
    • Edited by patkoDev Thursday, December 06, 2012 1:00 PM
    Thursday, December 06, 2012 12:48 PM

Answers

  • There, now perhaps future developers can have it a bit easier. There should be more clear documentation on the Lync SDK imo. Ofcourse you can always visit My Blog (+tab) for some more guides on Lync SDK.

    RECAP:

    • Use Processes to check Lync client for exit and when it comes back.
    • After the Client is exited and turned back on it is possible that your GetClient() will return an invalid client, this is because the client only gets returned after you signIn for some reason. So keep looping through GetClient() untill its state is no longer Invalid.
    • Make sure you GetClient() on the same thread if you call it multiple times in your code.
    • Get the client on the UI thread by dispatching.

    Code to listen for a running client:

            private Process _LyncProcess = null;
            private void getProcess()
            {
                Process[] list = Process.GetProcessesByName("communicator");

                foreach (var proc in list)
                {
                    if (proc.ProcessName.Equals("communicator"))
                        _LyncProcess = proc;
                }
            }

            private void startEnterListener()
            {
                Thread l = new Thread(new ThreadStart(checkEnter));
                l.IsBackground = true;
                l.Start();
            }

            private void checkEnter()
            {
                bool stop = false;
                while (stop == false)
                {
                    if (((Process[])Process.GetProcessesByName("communicator")).Length != 0)
                    {
                        stop = true;
                        initClient();
                    }
                    Thread.Sleep(1000);
                }            
            }

    Code to listen for Client Exit.

            private void startExitListener()
            {
                Thread l = new Thread(new ThreadStart(checkExit));
                l.IsBackground = true;
                l.Start();
            }

            private void checkExit()
            {
                bool stop = false;
                getProcess();
                if (_LyncProcess.Id != 0)
                {
                    while (stop == false)
                    {
                        Thread.Sleep(1000);
                        try
                        {
                            Process.GetProcessById(_LyncProcess.Id);
                        }
                        catch (ArgumentException e) {
                            stop = true;
                            startEnterListener();
                            Client_Disconnected();
                        }
                    }
                }
            }

    Initialize client succesfully:

          private void initClient()
            {
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
                    {
                        do
                        {
                            try
                            {
                                _LyncClient = LyncClient.GetClient();
                            }
                            catch (Exception e) { Thread.Sleep(100); }

                        } while (_LyncClient == null || _LyncClient.State == ClientState.Invalid);                 
                    }));

                if (_LyncClient != null && _LyncClient.State != ClientState.Invalid)
                {
                    _LyncClient.StateChanged += Cient_StateChanged;
                    _LyncClient.ConversationManager.ConversationAdded += Client_ConversationAdded;

                    startExitListener();
                    CheckIfSignedIn();
                }
            }


    • Marked as answer by patkoDev Tuesday, December 11, 2012 11:47 AM
    • Edited by patkoDev Tuesday, December 11, 2012 12:23 PM
    Tuesday, December 11, 2012 11:47 AM

All replies

  • Here's some code from a walkthrough topic in the MSDN docs. This should give you an idea how to detect whether the user is signed in or not:

    private void btn_SignOut_Click(object sender, EventArgs e)
            {
                if (_lyncClient.State == ClientState.SignedIn)
                {
                    _lyncClient.BeginSignOut(SignOutCallback, _asyncState);
                }
            }

    See also the topic itself, here:

    Walkthrough: Sign Out of Lync with UI Suppressed (Lync 2010 SDK)

    I also suggest re-submitting your other question about events separately, in order to draw attention to it.

    Thursday, December 06, 2012 5:14 PM
  • I'm sorry if I wasn't clear.

    First of, this is NOT a UIsuppressionMode applications. This means that my application is simply running in the background constantly checking the state of the Lync Client. So I do not signOut, I do not signIn. 

            private void connectToClient()
            {
                do
                {
                    try
                    {
                        _LyncClient = LyncClient.GetClient();
                        Thread.Sleep(1000);
                    }
                    catch (Exception e) { }
                } while (_LyncClient == null);

                _LyncClient.StateChanged += Cient_StateChanged;
                _LyncClient.ConversationManager.ConversationAdded += Client_ConversationAdded;

                CheckIfSignedIn();
            }

    If my application is started after the LyncClient is open and SignedIn then I will check wether the Client is signed in by this method.

            private void CheckIfSignedIn()
            {
                if (_LyncClient.Self.Contact != null)
                {
                    _LyncClient.Self.Contact.ContactInformationChanged += Client_ContactInformationChanged;
                    SetNotifyIcon("Green");
                }
            }

    If the user was not signedIn then I will do nothing. I have subscribed to the Client_StateChanged event so were he to signIn I will detect it in that manner.

            void Cient_StateChanged(object source, ClientStateChangedEventArgs data)
            {
                if (data.NewState == ClientState.SignedOut)
                {
                    SetNotifyIcon("Red");
                }

                if (data.NewState == ClientState.SignedIn)
                {
                    CheckIfSignedIn();
                }
            }

    So I can act accordingly to most scenarios. Now comes the last case I need to catch with my application. Imagine a person has my application running, its offline because he did not start up his Lync Client.

    He Starts up the client and automatically the client signs in, my application subscribes to all events and works perfectly as it should.

    However now the user closes down the Lync Client. I need to be able to put my application back into "monitoring mode". I need to start listening to the client again with the connectToClient() method. 

    One problem with this, I don't know how to detect the client exit. Thats all I need to know.

    ps: My code might not be the best code out there, but I concider myself to be a beginner, so please understand. I don't do anything with the exception because I don't care about it (GASP) yes, I said it. I don't want it to bother me, it probebly never triggers if you are working with the Lync Client and if it does then that means the client isn't running as it should in which case _LyncClient will stay null and nothing would change.

    All tips about coding welcome.

    Friday, December 07, 2012 9:35 AM
  • So apparently you have to trigger the ClientDisconnected event by trying to access the LyncClient object. This is how everything works, I post it because tracking and adapting to the client state is something many applications have to do.

    This little example has an icon in the systray and according to the state of the Lync Client it will display either a green light or a red light. 

    I don't know wether this is the "pro" way to do it but heck it works and I like simple code. If anyone sees a disaster waiting to happen in this logic please tell me. For now it works perfectly.

    Check my blog for this and more Here. (opens in other tab)

          

            public MainWindow()
            {
                InitializeComponent();

                //Put Icon in systray
                initNotifyIcon();

                connectToClient();

                Thread l = new Thread(new ThreadStart(InvokeDisconnectedEvent));
                l.IsBackground = true;
                l.Start();      
            }


            private void connectToClient()
            {
                do
                {
                    try
                    {
                        _LyncClient = LyncClient.GetClient();
                    }
                    catch (Exception e) { Thread.sleep(2000); }
                } while (_LyncClient == null);

                _LyncClient.StateChanged += Cient_StateChanged;
                _LyncClient.ConversationManager.ConversationAdded += Client_ConversationAdded;
                _LyncClient.ClientDisconnected += Client_Disconnected;

               CheckIfSignedIn();
            }

            void Cient_StateChanged(object source, ClientStateChangedEventArgs data)
            {
                if (data.NewState == ClientState.SignedOut)
                {
                    SetNotifyIcon("Red");
                }

                if (data.NewState == ClientState.SignedIn)
                {
                    CheckIfSignedIn();
                }
            }       

            private void CheckIfSignedIn()
            {
                if (_LyncClient.Self.Contact != null)
                {
                    _LyncClient.Self.Contact.ContactInformationChanged += Client_ContactInformationChanged;
                    SetNotifyIcon("Green");
                }
            }

            void Client_Disconnected(object source, EventArgs e)
            {
                SetNotifyIcon("red");
                _LyncClient = null;
                connectToClient();
            }

      


            private void InvokeDisconnectedEvent()
            {
                while (true)
                {
                    if (_LyncClient != null)
                    {
                        _LyncClient.State.ToString();
                    }
                }
            }

    EDIT:

    One bad thing about this implementation is that when InvokeDisconnectEvent is running, my CPU usage is 40-50% contrast that with the 10% without running it. If I put a Thread.sleep in  it of only 1 second I can push it back to 10% easily. However if I delay with only 1 second the Client_StateChanged event is called first and the routine for SignedOut is executed.

    So basicly I mess up the timing if I delay that method, but if I don't then I'm stuck with 40-50% CPU usage. Even delaying with a few microseconds will mess it up.

    Any ideas?

    EDIT 2:

    Someone on the forum also said that you could check the ClientState.ShuttingDown but when I exit, the client never reaches that state. It just goes to signingout. SigningOut is not a good state to check for disconnecting because when I start monitoring for a client after SignOut theres still going to be a Client for a few seconds!

    This is totally messed up. 

    • Marked as answer by patkoDev Monday, December 10, 2012 10:34 AM
    • Edited by patkoDev Monday, December 10, 2012 3:33 PM
    • Unmarked as answer by patkoDev Tuesday, December 11, 2012 8:57 AM
    Monday, December 10, 2012 10:34 AM
  • EDIT:

    Im monitoring the process state for both the exit event and the start event. The first time I start up the Lync while running my app everything works as I want it to. However, when I exit Lync and start it again the LyncClient is always invalid even though I get it from the UI thread. Because of this none of the subscribe events work.

    What is causing my client to become invalid? When the LyncClient shuts down? I put my LyncClient object to NULL when the exit happens. But this code worked in my other implementations so this shouldn't be a problem.

    EDIT SOLUTION:

                Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
                    {
                        do
                        {
                            try
                            {
                                _LyncClient = LyncClient.GetClient();
                            }
                            catch (Exception e) { Thread.Sleep(100); }

                        } while (_LyncClient == null || _LyncClient.State == ClientState.Invalid);                 
                    }));

    Make sure you GetClient() on same thread if you're doing it multiple times also keep getting client if its state is invalid. Thats the answer I found on the forum.

    Everything works perfectly, THANK GOD!

    ORIGINAL POST:

    Such a simple requirement and this is basicly impossible to do. I keep running into trouble no matter how many different ideas I have to get this to work!

    PROPOSED SOLUTION 1
    In this thread
    someone talks about invoking ClientDisconnected so as to clear the cached client. 

    HOW TO
    To do this, you have to constantly invoke _LyncClient.State to trigger the ClientDisconnected. 

    PROBLEM
    If you are looping through an infinite loop inside a thread its taking up too much CPU usage. But if you put in a Thread.Sleep the event will not fire anymore.

    PROPOSED SOLUTION 2
    Checking the ClientState.Shuttingdown

    PROBLEM
    Does not trigger AT ALL.

    PROPOSED SOLUTION 3
    Check the process and do something when it ends.

    PROBLEM
    This works however because I did not trigger ClientDisconnected the cache is not cleared. If I thus want to put my applciation into monitoring mode after the process got shut down I can't because if I do GetClient() it will not be NULL. Because the cached client will be given to me EVEN THOUGH IT DOES NOT EXIST!

    Check ClientState.Invalid is also not a possibility with it ALWAYS being invalid due to the fact that I can't let it run on the UI thread because it would freeze my whole UI. So it has to run outside.

    Iv hit a brick wall. I know how to detect a closed client but I guess my real problem is going back to listening mode. while(LynClient == null){GetClient();} Does not work because of the caching. Help needed please.



    • Edited by patkoDev Tuesday, December 11, 2012 11:02 AM
    • Marked as answer by patkoDev Tuesday, December 11, 2012 11:02 AM
    • Unmarked as answer by patkoDev Tuesday, December 11, 2012 11:02 AM
    Tuesday, December 11, 2012 8:51 AM
  • There, now perhaps future developers can have it a bit easier. There should be more clear documentation on the Lync SDK imo. Ofcourse you can always visit My Blog (+tab) for some more guides on Lync SDK.

    RECAP:

    • Use Processes to check Lync client for exit and when it comes back.
    • After the Client is exited and turned back on it is possible that your GetClient() will return an invalid client, this is because the client only gets returned after you signIn for some reason. So keep looping through GetClient() untill its state is no longer Invalid.
    • Make sure you GetClient() on the same thread if you call it multiple times in your code.
    • Get the client on the UI thread by dispatching.

    Code to listen for a running client:

            private Process _LyncProcess = null;
            private void getProcess()
            {
                Process[] list = Process.GetProcessesByName("communicator");

                foreach (var proc in list)
                {
                    if (proc.ProcessName.Equals("communicator"))
                        _LyncProcess = proc;
                }
            }

            private void startEnterListener()
            {
                Thread l = new Thread(new ThreadStart(checkEnter));
                l.IsBackground = true;
                l.Start();
            }

            private void checkEnter()
            {
                bool stop = false;
                while (stop == false)
                {
                    if (((Process[])Process.GetProcessesByName("communicator")).Length != 0)
                    {
                        stop = true;
                        initClient();
                    }
                    Thread.Sleep(1000);
                }            
            }

    Code to listen for Client Exit.

            private void startExitListener()
            {
                Thread l = new Thread(new ThreadStart(checkExit));
                l.IsBackground = true;
                l.Start();
            }

            private void checkExit()
            {
                bool stop = false;
                getProcess();
                if (_LyncProcess.Id != 0)
                {
                    while (stop == false)
                    {
                        Thread.Sleep(1000);
                        try
                        {
                            Process.GetProcessById(_LyncProcess.Id);
                        }
                        catch (ArgumentException e) {
                            stop = true;
                            startEnterListener();
                            Client_Disconnected();
                        }
                    }
                }
            }

    Initialize client succesfully:

          private void initClient()
            {
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
                    {
                        do
                        {
                            try
                            {
                                _LyncClient = LyncClient.GetClient();
                            }
                            catch (Exception e) { Thread.Sleep(100); }

                        } while (_LyncClient == null || _LyncClient.State == ClientState.Invalid);                 
                    }));

                if (_LyncClient != null && _LyncClient.State != ClientState.Invalid)
                {
                    _LyncClient.StateChanged += Cient_StateChanged;
                    _LyncClient.ConversationManager.ConversationAdded += Client_ConversationAdded;

                    startExitListener();
                    CheckIfSignedIn();
                }
            }


    • Marked as answer by patkoDev Tuesday, December 11, 2012 11:47 AM
    • Edited by patkoDev Tuesday, December 11, 2012 12:23 PM
    Tuesday, December 11, 2012 11:47 AM
  • Thanks for the suggestion. I passed it on to the writer for our lync client SDk docs. If you have other suggestions - or anyone else - I will pass those on as well.
    Tuesday, December 11, 2012 2:57 PM
  • If you want to find out if the Lync.exe is running and you are not in UI suppressed mode,  then the following information may help you:

    Lync 2013 creates the HKEY_CURRENT_USER\Software\IM Providers\Lync key and sets the UpAndRunning DWORD value to 2 when the Lync.exe process is running. The DWORD value is 0 when Lync.exe is not running.

    The HKEY_CURRENT_USER\Software\IM Providers\Communicator registry key value stored the flag in all versions before Lync 2013.

    You will have to register for events on your system registry to catch the change in the DWORD value from 0 to 2 as the user starts Lync up.

    In UI suppression mode, you start Lync programmatically by calling LyncClient.BeginInitialize.

    To catch the event raised when Lync shuts down, simply register for LyncClient.StateChanged.  The new state will be ClientState.ShuttingDown and then with the next event, ClientState.Uninitialized.


    John Austin Senior Programming Writer Microsoft

    Tuesday, December 11, 2012 5:06 PM