Lync Client SDK

Um Lync als Entwickler anzusprechen gibt es eine ganze Menge Schnittstellen. Diese Seite behandelt die Schnittstellen auf dem Client, d.h. auf dem PC, auf dem der Lync Communicator installiert ist.

Welche Client API gibt es ?

Hier gibt es gleich mehrere Zugänge

  • Lync Model API
    Über diese API können Sie als Entwickler von "außen" an den Lync Client heran. Dies geht sowohl über eine COM-API als auch per .NET Klasse
    PowerShell Scripting Lync 2010 SDK using Lync Model API (http://msdn.microsoft.com/en-us/library/hh243705.aspx)
  • Lync Extensibility API
    Diese API erlaubt ihnen, den Lync Communicator mit eigenem Code zu erweitern, d.h. Lync nutzt ihren Code, um Mehrwerte mit zu integrieren.
    PowerShell Scripting Lync 2010 SDK using Lync Extensibility API. (http://msdn.microsoft.com/en-us/library/gg581082.aspx)
  • Communicator API (Component Object Model)
    Diese API gab es damals schon zu OCS-Zeiten zur Steuerung des Office Communicator

Ich werde auf dieser Seite den Schwerpunkt auf die Lync Model API legen, die auch per PowerShell sehr einfach anzusprechen ist.

Diese API funktioniert auch problemlos mit Lync Online/Office 365, da Sie rein auf dem Client aufsetzt.

Model API vorhanden ?

Ehe sie die ein oder andere API nutzen, sollen Sie natürlich sicherstellen, dass der Lync Client installiert ist. In verschiedenen Quellen wird geraten, einfach in der Registrierung zu suchen:

HKLM\Software\Wow6432Node\Microsoft\Communicator\4.0
HKLM\Software\Microsoft\Communicator\4.0

Interessanter ist aber die direkte Nutzung der API mit entsprechender Fehlerbehandlung, wenn das Objekt nicht vorhanden sein sollte. Als mögliche Objekte kommen aber eine ganze Menge von DLLs als Kandidaten zum Einsatz, die auf verschiedenen Webseiten auch durcheinander genutzt werden.

DLL-Name Beschreibung und Links

CommunicatorAPI.dll

 

Microsoft.Office.Uc.dll

 

Microsoft.Lync.Model.dllc

PowerShell Scripting Lync 2010 SDK using Lync Model API
http://msdn.microsoft.com/en-us/library/hh243705.aspx

 

PowerShell Scripting Lync 2010 SDK using Lync Extensibility API
http://msdn.microsoft.com/en-us/library/gg581082.aspx

UccApi.dll

 

Ich beschränke mich aber auf die "Microsoft.Lync.Model.dll", die von verschiedenen Programme auch immer mal mitgebracht wird. Wundern Sie sich nicht, wenn Sie diese mehrfach auf ihrem PC finden. Hier mal eine "Suche" auf meinem Notebook mit vielen installierten Tools:

C:\>dir microsoft.lync.model.dll /s
 Datenträger in Laufwerk C: ist NAWNBFC-C-SSD
 Volumeseriennummer: 212C-D5C1

 Verzeichnis von C:\group\Technik\Skripte\IWYLight
22.10.2010  03:22           366.368 Microsoft.Lync.Model.dll
               1 Datei(en),        366.368 Bytes

 Verzeichnis von C:\Program Files\Busylight
18.08.2014  14:20           284.184 Microsoft.Lync.Model.dll
               1 Datei(en),        284.184 Bytes

 Verzeichnis von C:\Program Files\LyncWizard.com\Lync Wizard
14.04.2014  07:12           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Program Files\Microsoft Lync Server 2013\ResKit\RGAgentLive
03.12.2013  12:07           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Program Files\ThinkPad\Bluetooth Software\BtwLyncIntf
04.12.2010  01:42           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Program Files (x86)\Audiocodes\Better2Gether USB Over Ethernet
27.07.2014  15:15           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Program Files (x86)\greiginsydney\Profiles für Lync 2013
05.03.2014  01:13           413.888 Microsoft.Lync.Model.dll
               1 Datei(en),        413.888 Bytes

 Verzeichnis von C:\Program Files (x86)\Jabra\Jabra PC Suite
06.02.2015  16:30           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Program Files (x86)\Microsoft Dynamics CRM\Client\res\web\bin
21.09.2013  02:16           285.360 Microsoft.Lync.Model.dll
               1 Datei(en),        285.360 Bytes

 Verzeichnis von C:\Program Files (x86)\Microsoft Office\Office15
04.12.2012  21:56           278.016 Microsoft.Lync.Model.dll
               1 Datei(en),        278.016 Bytes

 Verzeichnis von C:\Program Files (x86)\Microsoft Office\Office15\LyncSDK\Assemblies\Desktop
01.08.2013  19:28           412.352 Microsoft.Lync.Model.dll
               1 Datei(en),        412.352 Bytes

 Verzeichnis von C:\Program Files (x86)\Microsoft Office\Office15\LyncSDK\Assemblies\Silverlight
31.07.2013  01:03           291.008 Microsoft.Lync.Model.dll
               1 Datei(en),        291.008 Bytes

 Verzeichnis von C:\Program Files (x86)\Xstran\Lync Contact Cleaner
22.11.2010  15:15           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\\AppData\Local\Apps\2.0\ADD8MPR1.KEX\LWE8NDZR.NZB\atte...0000_8d61d826e04
f37ca
01.07.2014  10:14           402.528 Microsoft.Lync.Model.dll
               1 Datei(en),        402.528 Bytes

 Verzeichnis von C:\\AppData\Local\Apps\2.0\ADD8MPR1.KEX\LWE8NDZR.NZB\atte...0000_8d61d827e0c
e478a
01.07.2014  10:14           402.528 Microsoft.Lync.Model.dll
               1 Datei(en),        402.528 Bytes

 Verzeichnis von C:\\AppData\Local\Apps\2.0\ADD8MPR1.KEX\LWE8NDZR.NZB\micr...0000_none_c3cc17
721e9bcbc6
01.07.2014  10:14           402.528 Microsoft.Lync.Model.dll
               1 Datei(en),        402.528 Bytes

 Verzeichnis von C:\\AppData\Local\LyncFellow
22.11.2010  16:15           285.472 Microsoft.Lync.Model.dll
               1 Datei(en),        285.472 Bytes

 Verzeichnis von C:\Windows\Installer\$PatchCache$\Managed\02D425C070400600A8E9C0C494E0E445\6.0.0
21.08.2013  13:35           285.360 Microsoft.Lync.Model.dll
               1 Datei(en),        285.360 Bytes

     Anzahl der angezeigten Dateien:
              18 Datei(en),      5.822.424 Bytes

Sie können Sie aber zumindest sehen, welche Programme heute schon diese API nutzen und dass diese die DLL einfach mitbringen, weil Sie nicht davon ausgehen können, dass das Lync Client SDK schon installiert ist.

Erste Schritte mit der Microsoft.Lync.Model-API

Als Entwickler mit C# oder einer anderen Sprache müssen Sie die DLL natürlich referenzieren. Auch in PowerShell muss die DLL erst eingebunden werden, damit ich dann ein Objekt erstellen und nutzen kann.

# Lync Client SDK importieren
Import-Module `
   -Name (Join-Path -Path ${env:ProgramFiles(x86)} `
   -ChildPath "Microsoft Office\Office15\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll")

# Alternativ geht auch ein Add-Type
#Add-Type `
#   -Path 'C:\Program Files\Microsoft Office\Office15\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll' 



# Den gestarteten Lync Client quasi von der Seite anzapfen
$Client = [Microsoft.Lync.Model.LyncClient]::GetClient()
# Zur kontrolle die eigene SIP-Adresse anzeigen
$Client.uri

# Anzeigen, was ich selbst kann
$Client.Capabilities

Der Client hat durchaus weitere Properties, von denen ich eine Auswahl beschreiben will:

PS C:\> $Client

Capabilities        : UserActivity, Text, GifInk, IsfInk, Audio, Video, Cccp,
                      Calendar, RemoteCallControl, ApplicationSharing
DelegatorClients    : {}
DeviceManager       : Microsoft.Lync.Model.Device.DeviceManager
InSuppressedMode    : False
SignInConfiguration : Microsoft.Lync.Model.SignInConfiguration utilities           : Microsoft.Lync.Model.Utilities
ContactManager      : Microsoft.Lync.Model.ContactManager
ConversationManager : Microsoft.Lync.Model.Conversation.ConversationManager
Self                : Microsoft.Lync.Model.Self
State               : SignedIn uri                 : sip:frank.carius@netatwork.de
RoomManager         : Microsoft.Lync.Model.Room.RoomManager
Settings            : Microsoft.Lync.Model.ClientSettings
InnerObject         : System.__ComObject

Über das "Self"-Property können Sie weitere Komponenten ansprechen, z.B.: ob der Client im "ResiliencyMode" ist

PS C:\> $Client.self

Contact            : Microsoft.Lync.Model.Contact
IsInResiliencyMode : False
Permissions        : {Microsoft.Lync.Model.AccessPermission,
                     Microsoft.Lync.Model.AccessPermission,
                     Microsoft.Lync.Model.AccessPermission,
                     Microsoft.Lync.Model.AccessPermission...}
PhotoDisplayed     : True
TestCallEndpoint   : Microsoft.Lync.Model.ContactEndpoint
InnerObject        : Microsoft.Office.Uc.SelfClass

Kontakt abfragen

Interessant ist natürlich die Abfrage eines anderen Kontakts. Aufbauend auf dem ersten Teil kann ich nun einen Kontakt über die SIP-URL abfragen.

PS C:\> $Client.ContactManager.GetContactByUri("sip:frank.carius@msxfaq.net")

ContactManager           : Microsoft.Lync.Model.ContactManager
CustomGroups             : {Andere Kontakte, Pinned Contacts}
Settings                 : {[Tagged, True], [AccessLevel, 400], [Source, 4],
                           [ExchangeServiceEntryId, 000000007c317bb2cd33d011afc
                           9008029638aba0700505f4fe3174f164184342c578963f891000
                           023d502d60000505f4fe3174f164184342c578963f8910000f33
                           15a5e0000]...} unifiedCommunicationType : Enabled uri                      : sip:frank.carius@msxfaq.net"
InnerObject              : System.__ComObject

Auf den ersten Blick sieht man die "CustomGroups", in der dieser Kontakt in einem anfragenden Client enthalten ist. Zudem kann man sehen, dass, er für UM enabled ist und zur Sicherheit noch mal die SIP-URI.

Schaut man sich die "Settings" an, dann erhält man weitere Details zu dem Kontakt.

$Client.ContactManager.GetContactByUri("sip:frank.carius@msxfaq.net").settings

                                    Key                                   Value
                                    ---                                   -----
                                 Tagged                                   False
                            AccessLevel                                     200
                                 Source                                       4
                 ExchangeServiceEntryId ...9a4c947efd1997fdbc5e0000073142030000
                 DefaultContactEndpoint    Microsoft.Lync.Model.ContactEndpoint

Hier habe ich innerhalb der Firma die Abfrage gestellt und man sieht gut, dass dieser Kontakt nicht "getagged" ist. Der Access-Level definiert, welche Daten er von mir sehen kann. Ich habe das mal für verschiedene Ziele gemacht.

SIP-Adresse Tagged AccessLevel Source

Meine eigene Kontaktinformation

False

nicht vorhanden

0

Kontakt in gleicher Firma (Team)

False

300

4

Kontakt in gleicher Firma (Allgemein)

False

200 

4

Open Federation und nicht in meinen Kontakten

False

100

0

Open Federation und andere Seite hat "Enhanced Privacy" aktiv

False

nicht vorhanden

0

Federation User in meiner "Freunde" und für StatusÄnderungen markiert

True

400

4

Ungültige SIP-Adresse per Federation

False

nicht vorhanden

0

Geblockter Kontakte

False

32000 

0

Die Werte des Access-Level sind schon alte Bekannte: 100=Extern, 200=Standard, 300=Kollegen, 400=Freunde,32000=Geblockt 

Abweichende Berechtigungen

Normalerweise sortiert der Client die Kontakte automatisch in die verschiedenen Berechtigungsgruppen ein. Sie können aber diesen "Privacy-Level" als Anwender natürlich auch ändern. Über das Permission-Property erhalten Sie schnell diese abweichenden Kontakte und deren Access-Level

$Client.self.Permissions

AccessEntries                             AccessLevel InnerObject
-------------                             ----------- -----------
{, sip:chinthakar@zilli...                    Blocked System.__ComObject
{sip:User1@msxfaq.net, ...                    Friends System.__ComObject
{sip:User2@msxfaq.net, ...                  Workgroup System.__ComObject
{, sip:User3@msxfaq.net...                  Colleague System.__ComObject
{, sip:6001@sample.de, ...                   External System.__ComObject

Auch hier sieht man, dass die Liste nicht immer "sauber" ist. Es kann schon leere Einträge geben.

Details eines Kontakt abfragen

Es ist zwar schön, wenn man die Daten eines Kontakts sehen kann, aber interessanter sind die Informationen aus der "Contactcard" und natürlich seine aktuelle Präsenz. Auch das geht sehr einfach:

$Client.ContactManager.GetContactByUri("sip:frank.carius@msxfaq.net").GetContactInformation("ActivityID")
Free

Weitere abrufbare Informationen sind auf ContactInformationType enumeration (https://msdn.microsoft.com/en-us/library/office/microsoft.lync.model.contactinformationtype_di_3_uc_ocs14mreflyncwpf.aspx) aufgelistet oder können durch eine falsche Angabe als Fehler angezeigt werden:

PS C:\> $Client.ContactManager.GetContactByUri("sip:frank.carius@netatwork.de").GetContactInformation("")
Das Argument "contactInformationType" mit dem Wert  "" für "GetContactInformation" kann nicht in den Typ
"Microsoft.Lync.Model.ContactInformationType" konvertiert werden: 
Fehler: "Der Bezeichnername "" kann nicht verarbeitet
werden, da er den folgenden Enumeratornamen zu sehr ähnelt oder mit ihnen
identisch ist: Availability, ActivityId, LocationName, TimeZone, TimeZoneBias,
MeetingSubject, MeetingLocation, Activity, CustomActivity, IdleStartTime,
DisplayName, Reserved1, primäryEmailAddress, EmailAddresses, Title, Company,
Department, Office, HomePageURL, Photo, DefaultNote, DefaultNoteType,
PersonalNote, OutOfficeNote, SourceNetwork, IconURL, IconStream,
ContactEndpoints, Reserved2, Reserved3, NextCalendarStateStartTime, Reserved4,
CapabilityString, Capabilities, ContactType, Description, Reserved5,
FirstName, LastName, Reserved6, Reserved7, Reserved8, Reserved9, Reserved10,
CapabilityDetails, DefaultNotePublishedTime, CurrentCalendarState,
NextCalendarState, AttributionString, InstantMessageAddresses, IsOutOfOffice,
Reserved11, Reserved12, Reserved13, Reserved14, Reserved15, Invalid. Verwenden
Sie einen spezielleren Bezeichnernamen.""

Natürlich kann man auch eine Collection übergeben:

Mit Lync 2013 hat sich hier aber etwas geändert, dass Sie nicht mehr so einfach direkt den Status bekommen. Lync 2013 "optimiert" die abfragen und liefert erst mal nur die Basisinformation. Eine SIP-Anfrage startet erst, wenn Sie den Kontakt über eine eigene ContactSubscription einbinden:

The behavior you are seeing is due to presence subscription optimization to the Lync client so that the subscription is delayed until the necessary contact information is required by the Lync client. Photo is an example für this optimization. Another example is ContactEndpoints. Specifically you must create and maintain your own ContactSubscription für the contacts that you need all the ContactEndpoints
Quelle: http://stackoverflow.com/questions/18516690/lync-inconsistent-behavior-with-contactendpoints

Contact presence subscription changes Contacts that have been obtained from a search operation through the API are not contained in the subscription maintained by the Lync client. To ensure that you get Updated presence, maintain your own ContactSubscription für all contacts. für information about subscribing to contact presence, see How to: Subscribe to enhanced presence content.
Quelle: https://msdn.microsoft.com/library/office/jj933253.aspx  unter „Code behavior changes“

Also muss man noch eine kleine Ehrenrunde drehen.

Kontakt-Endpunkte finden

Interessant fand ich bei der Auflistung der Kontakt-Details das Property "ContactEndpoints". Sollte man damit auch aus der Ferne ermitteln können, mit welchem Client der Anwender "angemeldet" ist ? Der erste Eindruck täuscht aber.

Ich habe hier sehr viele unterschiedliche Endpunkte, aber das sind letztlich nur die Ziele, die ein Anrufer auswählen kann. Es ist aber keine Übersicht, an welchen Endgeräten ich real angemeldet bin.

Die kleine "Abfrageschleife"

Mit dem bisher gesammelten Wissen ist es nun natürlich einfach, eine Liste von SIP-Adressen "MAL SCHNELL" auf ihre Erreichbarkeit zu abzuprüfen. Technisch ist es damit auch möglich, diese Abfrage regelmäßig zu machen und damit zu ermitteln, welcher Benutzer wann welchen Status hat. Das kann für einen Administrator zur Kontrolle der Erreichbarkeit von Endpunkten interessant sein, ist aber aus Datenschutzaspekten zumindest kritisch zu sehen. Vor allem weil es vom abgefragten Anwender selbst nicht erkannt werden kann. Das ist schon etwas anderes, als wenn ich all 5 Minuten in ihr Büro reinschaue, was wie gerade machen. Aber es kann natürlich hilfreich sein, um z.B. die Anzahl der Benutzer in einen Meeting zu erfassen, ehe man Lync Server durchstartet. Hier mal ein Sample ohne Fehlerbehandlung o.ä.

# Laden der Lync Client SDK DLL
Import-Module `
   -Name (Join-Path -Path ${env:ProgramFiles(x86)} `
   -ChildPath "Microsoft Office\Office15\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll")

# Lync Client SDK instanziieren
$Client = [Microsoft.Lync.Model.LyncClient]::GetClient()

$root = [system.directoryservices.activedirectory.forest]::getcurrentforest().rootdomain.name
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"GC://$root")
$objSearcher.PageSize = 1000
$objSearcher.Filter = "(msRTCSIP-PrimaryUserAddress=*)"
$objSearcher.PropertiesToLoad.Add("msrtcsip-primaryUseraddress") | Out-Null

$rtcUserlist = $objSearcher.Findall()

$result=@{}

foreach ($rtcUser in $rtcUserlist) {
   write-host "Processing" $rtcUser.Properties.distinguishedname
   $sipaddress = $rtcUser.Properties.'msrtcsip-primaryUseraddress'[0] 
   $result.item(($Client.ContactManager.GetContactByUri($sipaddress).GetContactInformation("ActivityID"))) +=1
}

$result

Name                           Value
----                           -----
in-a-conference                1
out-of-office                  1
Away                           4
Busy                           3
Inactive                       2
on-the-phone                   1
Free                           4
Offline                        20
                               60

Sehr schön ist hier zu erkennen, wie viele Personen der internen Firma zu dem Zeitpunkt welchen Status hatten. Es gibt aber auch sehr viele Objekte, die gar keinen Status haben. Das sind z.B."Application Contacts", die noch nie ein Präsenzdokument hochgeladen habe, analoge Telefone u.a.

Ich habe bislang noch nicht überprüft, ab wann das Skript in ein "Throttling" läuft, d.h. ich kann mir aber vorstellen, dass allzu exzessive Anfragen an den Server durch diesen vielleicht gedrosselt werden. Der Edge-Server bremst zu viele SIP-Pakete pro Quelle. Basierend auf diesem Code-Schnipsel sind natürlich noch ganz andere Dinge möglich

  • PRTG-Sensor
    Ich überwache viele Dinge mittels PRTG. Ich denke es wird nicht lange dauern, und ich habe einen passenden Custom Sensor gebaut, der diese Präsenzinformation (Summe) auch numerisch erfasst. So könnte man sicher
  • Exchange Message Tracking-Feedback
    Ebenso könnte es interessant sein, anhand der externen Absender und Empfänger eine Liste zu erstellen, wer davon schon per SIP erreichbar ist. Sicherlich interessant um das Potential für eine Zusammenarbeit auszuloten

Wer natürlich serverseitig solche Auswertungen macht, wird dazu sicher nicht den Windows Desktop Client installieren wollen, sondern auf UCWA - Unified Communication Web API oder UCMA - Unified Communication Managed API setzen.

Eigene Präsenz ändern

Über die API kann ich aber auch meine eigene Präsenzinformation ändern. Hier ein Beispiel. Beachten Sie, dass der eigene Status numerisch gesetzt werden muss.

$ContactInfo = New-Object 'System.Collections.Generic.Dictionary[Microsoft.Lync.Model.PublishableContactInformationType, object]'#
# AvailabilityID setzen
#    "Available" {$AvailabilityId = 3000}
#    "Appear Offline" {$AvailabilityId = 18000}
#    "Away" {$AvailabilityId = 15000}
#    "Busy" {$AvailabilityId = 6000}
#    "Do Not Disturb" {$AvailabilityId = 9000}
#    "Be Right Back" {$AvailabilityId = 12000}
#    "Off Work" {$AvailabilityId = 15500}

 
$ContactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::Availability, $AvailabilityId)
$ContactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::PersonalNote, "SIP ist SMTP mit Lichtgeschwindigketi")
$ContactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::LocationName, "Paderborn")

$Publish = $Client.Self.BeginPublishContactInformation($ContactInfo, $null, $null)
$Client.self.EndPublishContactInformation($Publish)

Wenn Sie parallel den Client geöffnet haben, sehen Sie umgehend die StatusÄnderung. Wer mag kann sich nun also ein Skript schreiben, welches z.B. bei jeder Änderung der IP-Adresse den Standort ändert.

IM Senden

Bei all der Arbeit fehlt dann nur noch die Funktion selbst per API eine IM zu senden.

Der folgende Code ist nur ein Start und noch nicht fertig.

[Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft Lync Server 2010\ResKit\RGAgentLive\CommunicatorAPI.dll")
$messenger = new-object "CommunicatorAPI.MessengerClass"
write-host "My SignIn Name:" $messenger.MySigninName
write-host "My ServiceID: " $messenger.MyServiceId
$myself = $messenger.GetContact($messenger.MySigninName, $messenger.MyServiceId)
$messenger.MyStatus MISTATUS_BE_RIGHT_BACK, MISTATUS_ONLINE, MISTATUS_BUSY, MISTATUS_INVISIBLE, MISTATUS_AWAY
$messenger.MyContacts | ft Signinname,status
Abmelden $messenger.Signout()

Meeting Starten

Wer mal schnell ein Meeting braucht, muss dazu nicht erst in Outlook einen Termin planen und einladen. Er kann auch ein "Meet Now" machen. Das geht natürlich auch per Automatisierung:

$Automation = [Microsoft.Lync.Model.LyncClient]::GetAutomation()

# Meeting starten und weitere OBjekte besorgen
$meetNow = $Automation.BeginMeetNow({}, 0)
$conversationWindow = $Automation.EndMeetNow( $meetNow )
$conversation = $conversationWindow.Conversation

Testanruf starten

Folgenden Code habe ich irgendwie nicht zum Laufen bekommen.

$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient(); 
$Conversation = $LyncClient.StartTestCall(); 
Start-Sleep -Seconds 10; 
$Conversation.HangUp();

Weitere Links