Free Online Publication
Windows Server 2008 R2 Remote Desktop Services
From Beginner to Expert Level
Content
The Book
... Table of Content
... Preface
... About This Book
Part I –
A Beginner's Guide to Remote Desktop Services
1 Overview and History
2 Installation
3 Licensing
4 Configuration
5 Client Software
6 Application Installation
7 System Administration
8 Network Planning
9 Printing
10 User Environment
Part II –
An Expert's Guide to Remote Desktop Services
11 Virtualization
12 RDS Internals
13 Remoting Protocol Details
14 Security
15 Registry Settings
16 Server Sizing
17 Resource Management
18 Testing and Quality Assurance
19 RDS Scripting
20 RDS for Developers
Author's Profile
... About
... Benny's Biography
... Presentations 2009, 2008, 2007, 2006, 2005, 2004 and earlier
Awards

 


Microsoft Windows Server 2008 Terminal Services

24. Developing for Terminal Services

Posted by Benny Tritsch on August 24, 2008

[TS Development] [Fundamentals] [Server Side] [Client Side] [Virtual Channels]

Back Next

24.1. Terminal Server Development Fundamentals

Read in this lesson...

  1. Terminal Services Application Programming Interface
  2. Interoperating with Unmanaged Code

 

Most Windows applications don’t require specific adaptation on code level before they are able to run in a terminal server environment. However, there are good reasons why some applications or management tools cannot work as desired without direct access to Terminal Services functionalities through a dedicated set of Application Programming Interface (API) calls. The Terminal Services API gives software developers everything they need to customize existing applications and even develop completely new applications utilizing multi-user capabilities.

If you are an IT professional this is the point where you may ask yourself why you should care about the Terminal Services API. But there are good reasons why this background information can be very helpful for your daily work. You may want to have your own custom management tools and after reading this chapter you will be able to explain developers exactly what you want them to develop – using their terminology. The resulting custom tools and their richer feature sets can go far beyond what standard management tools allow you to do, an important fact if you have the requirement to analyze and potentially modify terminal server settings in a very specific way.

But there are more good reasons for learning about the Terminal Services API. If you run into issues with applications incompatible to the terminal server platform you may want to contact the responsible developers. You will be able to ask for the required application modifications more precisely. You may even suggest how an application can be extended in order to use features specific to Terminal Services, making this platform more powerful. Last but not least, looking at the Terminal Services API will help you to better understand the Terminal Services architecture.

Terminal Services Application Programming Interface

User applications and management tools don’t call Windows system services directly. Instead they use documented public interfaces exposed by one or more subsystem run-time libraries. If you look at the Windows Server 2008 system directory \Windows\System32, you will find a number of files with the extension dll. Many of these Dynamic Link Library files contain public functions that provide a consistent programming interface to the operating system environment. All together these public functions make up the Windows Application Programming Interface, also called the Windows API. Each public function in the Windows API follows C or C++ programming conventions and is documented in the Platform Software Development Kit (Platform SDK) which is shipped with Microsoft Visual C++.

This being said, it is obvious that Windows API was initially designed for use by C/C++ programmers. The same is true for the Terminal Services API which is a subset of the Windows API. In order to use the Terminal Services API in an efficient way it is not only mandatory to have a solid skill level in C/C++ programming, familiarity with client/server architectures is also required.

In contrast to popular opinion, developing for Terminal Services does not only happen on the server side; the client side may also be involved. Applications or tools that use Terminal Services functionalities on the server side require Windows Server 2008, Windows Vista, Windows Server 2003, Windows XP, or Windows 2000. Here they take advantage of the public programming interface exposed by components such as Wtsapi32.dll. On the client side applications can hook into the Remote Desktop Client ActiveX Control Mstscax.dll, which also exposes a public programming interface. Additionally, there is a powerful programming model allowing client-to-server communication through RDP, called Virtual Channels.

NOTE: In the past there was only very few public information available when it comes to developing for Terminal Services. Not even Microsoft published code samples through their MSDN website. This may change in the future after Terminal Services technology is getting more important for many virtualization scenarios.

Terminal Services API Programming in Standard C

Before we start looking at some sample code you need to know about the requirements for Terminal Server API programming, in particular for the server side. You will need Visual Studio with the Platform SDK installed. If you want to start from scratch without using pre-created project templates, create a new C++ Win32 Console Application project. Keep all standard settings in the Win32 Application Wizard with one exception: uncheck the option “Precompiled header”.

After the new project was created, there are additional requirements for programming the Terminal Services API in C. Change the project settings according to the following list:

  • Add Wtsapi32.lib to Configuration Properties | Linker | Command Line | Additional Options
  • Change Configuration Properties | General | Character Set to Use Multi-Byte Character Set
  • Change Configuration Properties | C/C++ | Advanced | Compile As to Compile as C Code (/TC)
  • Change Configuration Properties | C/C++ | Code Generation | Runtime Library to Multi-Threaded Debug (/MTd) or Multi-Threaded (/MT)

Now delete all files you will not need for a C console application. Function headers are declared in Wtsapi32.h, which is stored under %ProgramFiles%\Microsoft Visual Studio 8\VC\PlatformSDK\Include.

Interoperating with Unmanaged Code

As explained previously, the Terminal Services API was designed for use by C/C++ programmers. Today, however, Microsoft encourages developers to use the Microsoft .NET Framework which provides an advanced programming model and associated programming languages such as C# or VB.NET. Unfortunately, neither the client side nor the server side of the Terminal Services API is exposed through a standard class in the Microsoft .NET library. But there is a solution to this challenge.

Code executing under the control of the .NET common language runtime is called managed code. Conversely, code that runs outside the runtime is called unmanaged code. Terminal Services API functions, COM components, and ActiveX interfaces are examples of unmanaged code. Their runtime environment seems to be completely separated from the managed code runtime environment due to different object models and architectures. However, the Microsoft .NET Framework supports interaction with COM components, COM+ services, external type libraries, and many operating system services. Data types, method signatures, and error-handling mechanisms vary between managed and unmanaged object models. To simplify interoperation between .NET Framework components and unmanaged code and to ease the migration path, the .NET runtime hides from both clients and servers the differences in these object models.

Platform Invoke

Platform invoke (pinvoke) is a service that enables managed code to call unmanaged functions implemented in dynamic link libraries (DLLs), such as those in the Terminal Services API. It locates and invokes an exported function and marshals its arguments (integers, strings, arrays, structures, and so on) across the interoperation boundary as needed. Platform invoke enables you to control a significant portion of the operating system by calling functions in the Windows API, the Terminal Services API, and numerous other APIs and DLLs. Platform invoke relies on metadata to locate exported functions and marshal their arguments at run time.

The identity of a DLL function consists of the following elements: Function name (or ordinal) and name of the DLL file in which the implementation can be found. For example, specifying the MessageBox function in the User32.dll identifies the function (MessageBox) and its location (User32.dll, User32, or user32). The Windows API can contain two versions of each function that handles characters and strings: A 1-byte character ANSI version and a 2-byte character Unicode version. When unspecified, the character set, represented by the CharSet field, defaults to ANSI. Some functions can have more than two versions. MessageBoxA is the ANSI entry point for the MessageBox function; MessageBoxW is the Unicode version.

Within a class, you define a static method for each DLL function you want to call. The definition can include additional information, such as the character set or the calling convention used in passing method arguments; by omitting this information, you select the default settings. Once wrapped, you can call methods on the function as you call methods on any other static function. Platform invoke handles the underlying exported function automatically.

Before you can access an unmanaged DLL function from managed code, you need to know the name of the function and the name of the DLL that exports it. With this information, you can begin to write the managed definition for an unmanaged function implemented in a DLL. Further, you can adjust the way that platform invoke creates the function and marshals data to and from the function.

 

using System.Runtime.InteropServices;
 
[DllImport("user32.dll")]
public static extern int MessageBox(int hWnd, String text, String caption, uint type);

 

You can rename an unmanaged function to whatever you like within your code as long as you map the new name to the original entry point in the DLL. An entry point identifies the location of a function in a DLL. Within a managed project, the original name or ordinal entry point of a target function identifies that function across the interoperation boundary. Further, you can map the entry point to a different name, effectively renaming the function. You can use the DllImportAttribute.EntryPoint field to specify a DLL function by name or ordinal. If the name of the function in your method definition is the same as the entry point in the DLL, you do not have to explicitly identify the function with the EntryPoint field. Otherwise, use the following form to indicate a name or ordinal:

 

[DllImport("dllname", EntryPoint="Functionname")]

 

The following C# example demonstrates how to replace MessageBoxA with MsgBox in your code by using the EntryPoint field.

 

using System.Runtime.InteropServices;
 
public class Win32
{
  [DllImport("user32.dll", EntryPoint="MessageBoxA")]
  public static extern int MsgBox(int hWnd, String text, String caption, uint type);
}

 

The next C# example shows how to call the MsgBox function from managed code:

 

using System;
 
namespace WTSConsole
{
  class ConsoleClass
  {
    [STAThread]
    static void Main(string[] args)
    {
      if (args.Length == 0)
      {
        Console.WriteLine("\n   Usage: WTSConsole Arguments\n");
      }
      else
      {
        Console.WriteLine("\n   Correct Usage!");
        Console.WriteLine("\n   Argument 1 = {0}\n", args[0]);
        Win32.MsgBox(0, args[0], "Platform Invoke Sample", 0);
      }
    }
  }
}

 

Many unmanaged functions expect you to pass, as a parameter to the function, members of structures that are defined in managed code. When passing structures or classes to unmanaged code using platform invoke, you must provide additional information to preserve the original layout and alignment. Central to the platform invoke concepts is an important difference between structure and class types. Structures are value types and classes are reference types — classes always provide at least one level of memory indirection (a pointer to a value). This difference is important because unmanaged functions often demand indirection.

 

Back Next

 

Read in this chapter...
24 Developing for Terminal Services
24.1 Terminal Server Development Fundamentals
24.2 Server-Side Terminal Services Programming
24.3 Client-Side Terminal Services Programming
24.4 Extending Terminal Services Through Virtual Channels