Introduction
Don’t you just love technology sometimes? We live in the digital age, and I for one am quite excited about it. It is amazing what apps are out there to improve our otherwise dull lives. I cannot imagine my life without TeamViewer. Giving technical support has never been easier, especially when you are working in a large company with various branches all over the country. It has never been easier. Today, I will introduce you to the TeamViewer API and the various platforms you can make use of to program a TeamViewer enabled app.
What Is TeamViewer?
If you haven’t figured it out yet, based on the introduction of this article, TeamViewer enables you to provide technical support to any PC that is not near you. But how? Well, once you have installed TeamViewer, you cannot simply open it and have a magical skeleton key that opens up all backdoors to all PCs. You are given an ID, just like the PC you are connecting to also has been given an ID. You have to supply the ID of the PC you are connected to, as well as that PC’s given password.
Once you have supplied the given ID and password to connect to the desired PC, you have control over that PC. Note, it is not full control; it’s just enough so that you can try to replicate the given problem or chat with the person on the PC. Let me tell you this: Make use of TeamViewer because not everyone is as tech savvy as you. People you have to give support to speak a different language, so try to understand the basics of their problem.
Getting Started with the TeamViewer API
Just like any other mainstream product, you cannot just jump in haphazardly. Similar to previous apps I have demonstrated (Facebook, Instagram), there is a simple method of making sure your intentions are valid with your app. Keep one thing in mind: Popular brands do not like the fact that their name could possibly be connected with malicious apps, hence destroying their brand’s reputation.
The following screenshots demonstrate all the steps you need to take to make use of the TeamViewer API.
Navigate to the following URL to start the process: https://integrate.teamviewer.com/en/develop/api/get-started/.
The previous link details each step that you have to follow, as seen in the following screenshots:

Figure 1: Create App

Figure 2: Create Account

Figure 3: Create App Menu

Figure 4: Create Application

Figure 5: Registration Completed

Figure 6: Token

Figure 7: Documentation
Once you have been given a token, you are able to develop an app that integrates with TeamViewer. A word to the wise: Please make sure you download the documentation. It is a step-by-step guide in creating apps that makes use of the TeamViewer API. The documentation can be downloaded from here.
Examples
You can find a list of examples on how to create a basic TeamViewer app from here.
Using the TeamViewer API
First off, your app needs to be authenticated. This confirms (through your app) that you do in fact have the appropriate tokens to make use of the API. Here is a small example on how to get authenticated with the TeamViewer API:
Imports System
Imports System.Net
Imports System.Security.Cryptography
Imports System.Text
Imports CreateSession.CustomException
Imports CreateSession.DataType
Imports CreateSession.Ressources
Imports CreateSession.Views
Imports Newtonsoft.Json
Namespace CreateSession.Helper
Friend Delegate Sub OauthProcessCompleteHandler()
Friend Class OAuth
#Region "internal fields"
' TODO: Insert your client id here:
Friend Const ClientId As String = ""
' TODO: Insert your client secret here:
Friend Const ClientSecret As String = ""
' TODO: Insert your redirect uri here:
Friend Const RedirectUri As String = ""
#End Region ' internal fields
#Region "private fields"
Private Shared s_currentinstance As OAuth
Private ReadOnly _additionalEntropy() As Byte = _
Encoding.ASCII.GetBytes("Just a sample")
Private _webView4Oauth As Webview4Oauth
#End Region ' private fields
#Region "constructors"
Private Sub New()
Tokens = New Token With {.TokenType = TokenType.AppToken}
ReadTokens()
End Sub
#End Region ' constructors
#Region "events"
Friend Event OauthProcessComplete As OauthProcessCompleteHandler
#End Region ' events
#Region "properties"
Friend Shared ReadOnly Property CurrentCurrentinstance() As OAuth
Get
If s_currentinstance Is Nothing Then
s_currentinstance = New OAuth()
End If
Return s_currentinstance
End Get
End Property
Private privateTokens As Token
Friend Property Tokens() As Token
Get
Return privateTokens
End Get
Private Set(ByVal value As Token)
privateTokens = value
End Set
End Property
Friend ReadOnly Property TokensAvailable() As Boolean
Get
Return (Tokens IsNot Nothing AndAlso Not _
String.IsNullOrEmpty(Tokens.AccessToken) AndAlso Not _
String.IsNullOrEmpty(Tokens.RefreshToken))
End Get
End Property
#End Region ' properties
#Region "internal methods"
Friend Shared Function ValidateToken(ByVal tokenType As TokenType, _
ByVal accessToken As String) As String
If tokenType = CreateSession.Ressources.TokenType.SkriptToken AndAlso _
Not String.IsNullOrEmpty(accessToken) Then
Return accessToken
End If
If tokenType = CreateSession.Ressources.TokenType.AppToken AndAlso _
CurrentCurrentinstance.TokensAvailable Then
If CurrentCurrentinstance.AccessTokenIsExpired() Then
CurrentCurrentinstance.RefreshAccesToken()
End If
Return CurrentCurrentinstance.Tokens.AccessToken
End If
Throw New AuthorizationException(Resources.Error_AccessTokenRequired)
End Function
''' <summary>
''' Pings the API to validate the access tokens
''' </summary>
''' <returns></returns>
Friend Function AccessTokenIsExpired() As Boolean
Dim rp = New RestProperties With {.Url = TvApiUrls.UrlPing, .Method = _
WebRequestMethods.Http.Get, .AccessToken = Tokens.AccessToken}
Dim jsonResultString As String = (New RestConnection(rp)).SendToApi()
If String.Empty <> jsonResultString Then
Dim pingResult = New With {Key .token_valid = False}
pingResult = JsonConvert.DeserializeAnonymousType _
(jsonResultString, pingResult)
Return Not pingResult.token_valid
End If
Return True
End Function
''' <summary>
''' Sets the url and the postdata to get a new access tokens
''' from the api
''' </summary>
''' <param name="code">Authorisierungcode zur Erstellung
''' des neuen tokens</param>
Friend Sub GetNewAccessToken(ByVal code As String)
If code = String.Empty Then
Return
End If
Dim postData = New StringBuilder()
postData.Append("grant_type=authorization_code")
postData.Append("&code=" & code)
postData.Append("&redirect_uri=" & RedirectUri)
postData.Append("&client_id=" & ClientId)
postData.Append("&client_secret=" & ClientSecret)
RetrieveAndSetTokensFromApi(postData.ToString())
End Sub
''' <summary>
''' Opens the webview to authorize the app
''' </summary>
Friend Sub OpenWebviewForNewAccessToken()
_webView4Oauth = New Webview4Oauth()
AddHandler _webView4Oauth.WebViewFinished, _
AddressOf GetNewAccessToken
_webView4Oauth.ShowDialog()
End Sub
''' <summary>
''' Reads the tokens from the encrypted binary files
''' and decrypts them
''' </summary>
Friend Sub ReadTokens()
If My.Settings.Default.AccessToken IsNot Nothing Then
Try
Dim encryptedAccessToken() As Byte = _
My.Settings.Default.AccessToken
Dim decryptedAccessToken() As Byte = _
ProtectedData.Unprotect(encryptedAccessToken, _
_additionalEntropy, DataProtectionScope.CurrentUser)
Tokens.AccessToken = _
Encoding.Default.GetString(decryptedAccessToken)
Dim encryptedRefreshToken() As Byte = _
My.Settings.Default.RefreshToken
Dim decryptedRefreshToken() As Byte = _
ProtectedData.Unprotect(encryptedRefreshToken, _
_additionalEntropy, DataProtectionScope.CurrentUser)
Tokens.RefreshToken = _
Encoding.Default.GetString(decryptedRefreshToken)
Catch e1 As CryptographicException
Tools.StandardErrorMessage("Error on decrypting
the tokens-files!" & ControlChars.Lf & _
"Files or tokens seem to be defective ..." _
& ControlChars.Lf & ControlChars.Lf & _
"New Authorization is acquired!", _
"Decryption error")
End Try
End If
End Sub
''' <summary>
''' Sets the url and the postdata to get a new access tokens
''' from the api after the old one is expired
''' </summary>
Friend Sub RefreshAccesToken()
If Tokens.RefreshToken = String.Empty Then
Return
End If
Dim postData = New StringBuilder()
postData.Append("grant_type=refresh_token")
postData.Append("&refresh_token=" & Tokens.RefreshToken)
postData.Append("&client_id=" & ClientId)
postData.Append("&client_secret=" & ClientSecret)
RetrieveAndSetTokensFromApi(postData.ToString())
End Sub
''' <summary>
''' Calling the API for the new tokens and save them
''' </summary>
''' <param name="postData"></param>
Friend Sub RetrieveAndSetTokensFromApi(ByVal postData As String)
Dim restProperties = New RestProperties With {.Url = TvApiUrls.UrlToken, _
.Method = WebRequestMethods.Http.Post, .ContentType = _
HttpContentTypes.ApplicationXWwwFormUrlEncoded, .PostData = postData}
Dim jsonResultString As String = _
(New RestConnection(restProperties)).SendToApi()
If String.Empty <> jsonResultString Then
Tokens = JsonConvert.DeserializeObject(Of Token)(jsonResultString)
SaveTokens()
' Check if someone is listening to the OauthProcessComplete-Event
' Event to inform the main thread that the OAuth-Process is complete
' and that he can use the new tokens to create a new session via
' the API
RaiseEvent OauthProcessComplete()
End If
End Sub
''' <summary>
''' Ecnrypts the tokens and saves them to binary files
''' </summary>
Friend Sub SaveTokens()
Try
Dim encryptedAccessToken() As Byte = _
ProtectedData.Protect(Tools.StringToByteArray(Tokens.AccessToken), _
_additionalEntropy, DataProtectionScope.CurrentUser)
My.Settings.Default.AccessToken = encryptedAccessToken
Dim encryptedRefreshToken() As Byte = _
ProtectedData.Protect(Tools.StringToByteArray(Tokens.RefreshToken), _
_additionalEntropy, DataProtectionScope.CurrentUser)
My.Settings.Default.RefreshToken = encryptedRefreshToken
My.Settings.Default.Save()
Catch e As Exception
Tools.StandardErrorMessage(e.Message)
End Try
End Sub
#End Region ' internal methods
End Class
End Namespace
This Namespace handles the Authentication. It simply checks to see if you have a valid token, then tries to connect to the corresponding URL as specified through the Webview4Oauth class (it was referenced earlier when we connected to the Authorization page).
Everything Okay?
Namespace CreateSession.Views
Friend Delegate Sub WebViewFinishedHandler(ByVal code As String)
Partial Friend Class Webview4Oauth
Inherits Form
Friend Event WebViewFinished As WebViewFinishedHandler
''' <summary>
''' Constructor -> navigates to the defined url on startup
''' </summary>
Friend Sub New()
InitializeComponent()
Dim url = New StringBuilder("https://webapi.teamviewer.com")
url.Append("/api/v1/oauth2/authorize")
url.Append("?")
url.Append("response_type=code")
url.Append("&client_id=" & OAuth.ClientId)
url.Append("&redirect_uri=" & OAuth.RedirectUri)
url.Append("&display=popup")
oauthWebBrowser.Navigate(url.ToString())
oauthWebBrowser.Select()
oauthWebBrowser.Focus()
End Sub
''' <summary>
''' Url of the webview has changed
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub oauthWebBrowser_Navigated(ByVal sender As Object, _
ByVal e As WebBrowserNavigatedEventArgs) _
Handles oauthWebBrowser.Navigated
' Checks the changed url corresponds to the final url with the code
If oauthWebBrowser.Url.Query.Contains("code=") Then
' Extract the code from the link
Dim urlParameters = _
HttpUtility.ParseQueryString(oauthWebBrowser.Url.Query)
Dim code As String = urlParameters("code")
' Inform asynchronous thread that the new code is available
' and deliver it
If code <> String.Empty Then
RaiseEvent WebViewFinished(code)
Else
MessageBox.Show("Code for the new access-token could not be _
retrieved!", "Error", MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End If
Close()
End If
End Sub
End Class
End Namespace
Namespace CreateSession.Ressources
Friend Module TvApiUrls
Public Const UrlCreateSession As String = _
"https://webapi.teamviewer.com/api/v1/sessions"
Public Const UrlGetGroupIds As String = _
"https://webapi.teamviewer.com/api/v1/groups"
Public Const UrlPing As String = _
"https://webapi.teamviewer.com/api/v1/ping"
Public Const UrlToken As String = _
"https://webapi.teamviewer.com/api/v1/oauth2/token"
End Module
End Namespace
This class ensures that we are indeed validated and that the resulting URL can be found and be accessed.
Getting the Application Info
Namespace CreateSession.Helper
Friend Class ApplicationInfo
''' <summary>
''' get assembly title
''' </summary>
Friend Shared ReadOnly Property AssemblyTitle() As String
Get
Dim attributes() As Object = _
System.Reflection.Assembly.GetExecutingAssembly()._
GetCustomAttributes(GetType(AssemblyTitleAttribute), False)
If attributes.Length > 0 Then
Dim titleAttribute As AssemblyTitleAttribute = _
DirectCast(attributes(0), AssemblyTitleAttribute)
If titleAttribute.Title <> String.Empty Then
Return titleAttribute.Title
End If
End If
Return Path.GetFileNameWithoutExtension(System.Reflection.Assembly _
.GetExecutingAssembly().CodeBase)
End Get
End Property
''' <summary>
''' get assembly version
''' </summary>
Friend Shared ReadOnly Property AssemblyVersion() As String
Get
Return System.Reflection.Assembly.GetExecutingAssembly() _
.GetName().Version.ToString()
End Get
End Property
''' <summary>
''' get assembly description
''' </summary>
Friend Shared ReadOnly Property AssemblyDescription() As String
Get
Dim attributes() As Object = _
System.Reflection.Assembly.GetExecutingAssembly() _
.GetCustomAttributes(GetType(AssemblyDescriptionAttribute), False)
If attributes.Length = 0 Then
Return String.Empty
End If
Return DirectCast(attributes(0), AssemblyDescriptionAttribute).Description
End Get
End Property
''' <summary>
''' get assembly product
''' </summary>
Friend Shared ReadOnly Property AssemblyProduct() As String
Get
Dim attributes() As Object = _
System.Reflection.Assembly.GetExecutingAssembly() _
.GetCustomAttributes(GetType(AssemblyProductAttribute), False)
If attributes.Length = 0 Then
Return String.Empty
End If
Return DirectCast(attributes(0), AssemblyProductAttribute).Product
End Get
End Property
''' <summary>
''' get assembly copyright
''' </summary>
Friend Shared ReadOnly Property AssemblyCopyright() As String
Get
Dim attributes() As Object = _
System.Reflection.Assembly.GetExecutingAssembly() _
.GetCustomAttributes(GetType(AssemblyCopyrightAttribute), False)
If attributes.Length = 0 Then
Return String.Empty
End If
Return DirectCast(attributes(0), AssemblyCopyrightAttribute).Copyright
End Get
End Property
''' <summary>
''' get assembly company
''' </summary>
Friend Shared ReadOnly Property AssemblyCompany() As String
Get
Dim attributes() As Object = _
System.Reflection.Assembly.GetExecutingAssembly() _
.GetCustomAttributes(GetType(AssemblyCompanyAttribute), False)
If attributes.Length = 0 Then
Return String.Empty
End If
Return DirectCast(attributes(0), AssemblyCompanyAttribute).Company
End Get
End Property
Friend Shared Function RetrieveLinkerTimestamp() As Date
Dim filePath = System.Reflection.Assembly.GetCallingAssembly().Location
Const cPeHeaderOffset As Integer = 6
Const cLinkerTimestampOffset As Integer = 8
Dim b = New Byte(2047){}
Dim s As Stream = Nothing
Try
s = New FileStream(filePath, FileMode.Open, FileAccess.Read)
s.Read(b, 0, 2048)
Finally
If s IsNot Nothing Then
s.Close()
End If
End Try
Dim i = BitConverter.ToInt32(b, cPeHeaderOffset)
Dim secondsSince1970 = _
BitConverter.ToInt32(b, i + cLinkerTimestampOffset)
Dim dt = New Date(1970, 1, 1, 0, 0, 0)
dt = dt.AddSeconds(secondsSince1970)
dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours)
Return dt
End Function
End Class
End Namespace
API Functionalities
Namespace CreateSession.Helper
Friend Class ApiFunctions
Friend Shared Function GetGroupsOfAccount(ByVal tokenType As TokenType, _
Optional ByVal accessToken As String = Nothing) As Group()
' First setting up a new RestProperties and define the attributes
' needed for "GET /api/v1/groups (list all available groups)"
Dim rp4GroupIDs = New RestProperties With {.Url = _
TvApiUrls.UrlGetGroupIds, .Method = WebRequestMethods.Http.Get, _
.AccessToken = OAuth.ValidateToken(tokenType, accessToken)}
' Set up a new API-Connection
Dim rc4GroupIDs = New RestConnection(rp4GroupIDs)
' Send the newly created RestProperties to the API and retrieve
' the JSON string
Dim resultJsonString = rc4GroupIDs.SendToApi()
If String.Empty <> resultJsonString Then
' Extracting the group names from the JSON-string we got
' from the API
Dim groups = _
JsonConvert.DeserializeObject(Of Groups)(resultJsonString)
Return groups.GroupArray
End If
Return Nothing
End Function
Friend Shared Sub CreateInstantSupportSession(ByRef _
instantSupportInfo As InstantSupportInfo, ByVal tokenType _
As TokenType, Optional ByVal accessToken As String = Nothing)
Dim restProperties = _
New RestProperties With {.Url = TvApiUrls.UrlCreateSession, _
.Method = WebRequestMethods.Http.Post, .PostData = _
JsonConvert.SerializeObject(instantSupportInfo), _
.AccessToken = OAuth.ValidateToken(tokenType, accessToken)}
' Create a new RestConnection with the completed RestProperties
Dim rc = New RestConnection(restProperties)
' Send data to the API and retrieve the JSON data
Dim resultJsonString = rc.SendToApi()
' Extract the session data from the JSON string
If String.Empty <> resultJsonString Then
instantSupportInfo = _
JsonConvert.DeserializeObject(Of InstantSupportInfo)(resultJsonString)
instantSupportInfo.UnformatedJsonString = resultJsonString
Else
instantSupportInfo = Nothing
End If
End Sub
End Class
End Namespace
Conclusion
As you can see, once you know where to find the particular APIs and documentation needed, developing apps with third-party DLLs is quite easy. Until next time, happy coding!.