Introduction
The basic problem is that I kept wanting to set my status to "away" for a while, then would forget to reset it later so I'd be marked as "away" for a lot longer than I wanted. By allowing you to set a status for a fixed period, knowing it'll be automatically changed later, this program prevents that particular error.
Background
Microsoft publishes a Lync SDK which presents a whole bunch of useful APIs to the Lync (or, more recently, Skype for Business) client using COM interop, they also have some very helpful samples.
Using the Program
You run the program. It will present a UI something like this:
Using that UI, you can set your current status (click where it says "Away"), sign out (or sign in if you're already signed out), select a future status you want to use, and how long until you want to use it (automatic, just lets Lync decide for itself based on system usage).
Once you're happy with that, just click "Start" and a timer will start to count down to the time when the new status is set. Handy hint 0.002 hours is about 7 seconds if you're looking to run a quick test.
Understanding the Code
This is a basic WPF program using COM interop. It starts out by connecting to the Lync (aka Skype for Office) client in the Window_Loaded
method. The code is obscured by a bunch of try
/catch
logic, but really all it is doing is.
lyncClient = LyncClient.GetClient();
Once there's a lyncClient
reference in hand subscribing to its StateChanged
event allows the program to watch for changes dynamically. The program only has to explicitly monitor for log on/off and availability changes because the MyStatusArea
control from the SDK monitors (and sometimes allows setting) other information.
Each of the radio buttons sets a desired future state for availability, and once the "Start" button is pressed, the desired delay is read and a countdown starts. The time left is displayed using a simple time string updated once a second.
The actual mechanism is that OnStartButtonClick
sets endTime
and starts the timer ticking; each tick calls OnTick
. When the time is up (determined by comparing now with endTime
, the desired new availability is pushed to Lync and the timer is stopped.
It's possible that the system will be suspended before the timer expires and resumed afterwards. If so, Lync requires a bit of time to reconnect to the server (between 5 and 10 seconds in my experience). In order to handle that and other oddball cases, if the program finds the timer has expired and the Lync client is running but the status is not "logged on" it just waits for another 5 seconds and tries again.
Points of Interest
The program is useful in its own right, but if you want to learn about event driven programming, or interfacing with Lync, this provides a fairly simple testbed, start by looking at the registration code in Window_Loaded
and work outwards from from there.
The use of dispatcher
is worth watching for - it is used to ensure that event driven code runs on the UI thread so as to allow it to perform UI updates without error.
Notice that the code uses an availability value of ContactAvailability.None
, which is documented as "do not use" but actually turns control of the availability over to the Lync client.
The program uses Trace.WriteLine
to create debug messages so you can use something like DebugView
from SysInternals (now Microsoft) to see the messages - very handy if you're trying to figure out what happens after suspend/resume.
Minimize to System Tray
When you attempt to close the program it minimizes itself to the system tray, the only way to get it to actually exit is to select "exit" from the context menu of the system tray icon. The idea of this is that you can request a future change of status, then minimize it to the system tray and forget all about it. Unfortunately, there is no support for system tray icons (NotifyIcon types) in WPF, so we have to use Winforms objects instead. The bulk of the work is in the MainWindow constructor, the most interesting part is perhaps loading the icon image from a resource stream.
using (Stream stream = Application.GetResourceStream(new Uri("LyncUtility.ico", UriKind.Relative)).Stream)
{
notifyIcon.Icon = new Icon(stream);
}
The icon file has a build action of "resource" and is also used for the program icon.
Allowing Only One Copy to Run
Once the program is minimized to the system tray it is really easy to forget it is there and run another copy - pretty soon you have a lot of copies in the system tray! So, when a new instance is initiated it tries to create a new EventWaitHandle with a unique name. If the create succeeds, that means this is the first instance of the program to run, and it fires off a background thread to wait for a subsequent instance to cause the event. If the event is caused it just makes itself visible in the foreground.
If this is NOT the first instance to run, it will be unable to create the event, but instead will be connected to the existing event, which it can then cause (Set) in order to notify the running copy that someone is interested in seeing it. Once that is done the new copy can go away without any further ado.
Source
The source is maintained on GitHub at https://github.com/david-maw/LyncUtility.
Ideas for Improvement
At the moment, this program is pretty simple, but it could get smarter:
- Integrating with Outlook would maybe let it automate working hours (the Lync client can already integrate with free/busy)
- Should the program persist unapplied changes through a reboot?
History
- July 2016 - First release
- August 7, 2016 - handle suspend/resume recovery and use .NET 4.5 instead of 4.6 in order to be a bit more generally compatible.
- June 12, 2017 - close to system tray rather than exiting.
- July 23, 2017 - allow only one instance to run.