VNC-Client Library for C#

GitHub Workflow Status GitHub Nuget

This project provides a very performant, fully managed C#-Library that implements the RFB protocol for easy integration of VNC-Client features into own applications. It’s also cross-platform and runs every where .NET Core (or .NET 5 and so on…) runs.

API Documentation

This library is currently in alpha state, but has proven to be quite stable and is ready for daily use. A stable release will follow, as soon as it’s feature-complete.

Sample Application

Screenshot of the sample application

Source Code: AvaloniaVncClient

Main Design Goals

Features

Most of these features are demonstrated by the included sample applications.

NuGet-Packages

Core library: MarcusW.VncClient

This is library contains the main protocol implementation and is completely platform-agnostic thanks to some abstractions using C# interfaces. It has no external dependencies.

Adapter libraries: MarcusW.VncClient.Avalonia, more may follow…

These libraries provide platform specific implementations for the mentioned interfaces and provide e.g. user controls that can just be dropped into an UI application to make use of the VNC library very easily. These libraries depend on the core VNC library as well as the corresponding UI library.

You can find the latest packages for every commit that’s pushed to master on GitHub Packages.

Support me!

Developing a VNC client implementation is hell a lot of work. Months of my spare time went into this. So if you use this library in a commercial context, please consider giving something back - and of course let me know about where you used it 😀.

Usage

Creating Connections

Let’s prepare some objects which you can later use to start as many new connections as you want:

// Create and populate a default logger factory for logging to Avalonia logging sinks.
var loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new AvaloniaLoggerProvider());

// Create a vnc client object. This can be used to easily start new connections.
var vncClient = new VncClient(loggerFactory);

// Create a new authentication handler to handle authentication requests from the server
var authenticationHandler = new DemoAuthenticationHandler(); // see below

The provided DemoAuthenticationHandler gets called by the protocol implementation when the server requires newly established connections to be secured with e.g. the VNC Authentication security type. In this case the server requests a password input from the authentication handler which in turn could then show an input-dialog to the user.

public class DemoAuthenticationHandler : IAuthenticationHandler
{
    /// <inhertitdoc />
    public async Task<TInput> ProvideAuthenticationInputAsync<TInput>(RfbConnection connection, ISecurityType securityType, IAuthenticationInputRequest<TInput> request)
        where TInput : class, IAuthenticationInput
    {
        if (typeof(TInput) == typeof(PasswordAuthenticationInput))
        {
            string password = "verysecure!!"; // Retrieve the password somehow

            return (TInput)Convert.ChangeType(new PasswordAuthenticationInput(password), typeof(TInput));
        }

        throw new InvalidOperationException("The authentication input request is not supported by this authentication handler.");
    }
}

Now you can use the vncClient and authenticationHandler objects to connect to a VNC server by doing the following in the code:

// Configure the connect parameters
var parameters = new ConnectParameters {
    TransportParameters = new TcpTransportParameters {
        Host = "hostname or ip address",
        Port = 5900
    },
    AuthenticationHandler = authenticationHandler
    // There are many more parameters to explore...
};

// Start a new connection and save the returned connection object
RfbConnection = await vncClient.ConnectAsync(parameters, cancellationToken).ConfigureAwait(true);

You will receive an instance of the RfbConnection class which represents the active connection, exposes properties for the connection status and has methods to interact with it.

But probably, you also want to make the connection somehow visible on your screen. There are multiple ways to achieve this, depending on the application framework you use:

Avalonia User Control

In case of the Avalonia UI framework, there is a ready-to-use user control which can be added to applications to view connections and interact with them.

Extending an Avalonia application with VNC support is very easy, just add the NuGet-Package MarcusW.VncClient.Avalonia to your project and add a <VncView />-control to your application window. This is the place where the VNC connection will be shown.

<vnc:VncView Connection="{Binding RfbConnection}" />

The connection that is shown inside this view can be specified by setting the value of the Connection-property, either in code-behind or through a view model binding. As soon as you set a VncView to show a connection object, you will see the remote session on your screen and should be able to interact with it.

This works, because the view will register itself as the RenderTarget and OutputHandler of the connection object.

Manual Integration

If you don’t want to use the prebuilt VncView control or there is none available for your used application framework, you can also manually implement the required interfaces:

After you have implemented these interfaces, you can make the connection use them by setting the RfbConnection.RenderTarget and RfbConnection.OutputHandler properties. You can also detatch the connection form your render target by setting RfbConnection.RenderTarget to null. When doing this, the client implementation will render it’s frames to nowhere, which is a useful feature to keep a connection alive in the background without wasting CPU resources on rendering the received frames.

There are also two connection parameters called InitialRenderTarget and InitialOutputHandler which allow you to attach render target and output handler right from the beginning.

To send messages for keyboard or mouse input, you can call the EnqueueMessage or SendMessageAsync method on the connection object. Again, please take a look at the Avalonia-specific implementation for details on this.

TurboJPEG Requirement

For maximum performance, this VNC client supports the Tight frame encoding type. But what makes this encoding type so efficient is mainly the use of a extremely optimized JPEG decoder called “TurboJPEG” (or libjpeg-turbo). Because of this, Tight is only available, if your application or the used operating system ships with some version of this native library. If it doesn’t, this encoding type will be automatically disabled and the sample application will show a warning. Your application should probably do this, too.

In future, maybe, I’ll extend my CI builds to ship precompiled TurboJPEG-binaries for the most common operating systems directly in the NuGet package. Please vote on this issue if you want this. 😀

Contributing

Contributions are always welcome! Wanna fix a bug or add support for a new protocol extension? Go ahead!

But before you spend a lot of time working on a Pull Request, please create an Issue first to communicate, what you are working on, to avoid duplicated work and maybe discuss some details about the way you’re implementing the fix/feature.

Here is a very great community-maintained protocol specification that I used and which is probably also useful to you: rfbproto

🎉 Happy coding! 🎉