Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

Visual Studio debug console for iPhone

5.00/5 (8 votes)
21 Aug 2012CPOL6 min read 48.1K   496  
An architectural proposal to integrate mobile and .Net applications

Image 1    Image 2

Introduction

The following article has two purposes:

  • From the architectural perspective: To show an approach to integrate Windows applications with smarthphone and tablet applications through HTML5 WebSockets.
  • From the software engineering perspective: To show a concrete and useful case of a Visual Studio remote debugging console on iPhones, extensible to Android devices.
It is not a goal of this article to provide a final and polished solution, the source code and applications provided are just for demonstrating the concept. Although the .net application uses the WPF library, it can be used with any UI library.

Background

Few days ago I got an iPhone 4. I was hoping to find a way to use it as a debugging console for my .net applications: No luck. So, I started to research about the simplest solution to perform this, using the following criteria:

  • Avoid to learn a totally new language, like Objective-C
  • Avoid to distribute the application through Apple's AppStore (and save a hundred bucks!)
  • Use a firewall-friendly communication technique
  • Use a simple library, prefereably just one DLL
The research process was not too long, HTML5 seems to be the best choice for writing an application without requiring the AppStore process, iOS supports nicely the HTML5 Application Cache feature and the HTML5 WebSockets complied with the firewall-friendly requirement and was implemented natively in iOS' Safari browser.

Architecture 

In the Windows side, the core functionality of the tool is a TraceListener-derived class. This class is responsible to trap the System.Diagnostics.Debug.Write() and .WriteLine() calls and sent it to the remote device by using an HTML5 WebSocket server. On the other hand, the iPhone implements a javascript application using a WebSocket client. The high-level design looks like this: 

Image 3

Implementation

Windows side

In the Windows side, WebSockets is still not supported natively by .Net (but already announced for the .Net Framework 4.5). After evaluating a half dozen of C# WebSocket libraries, I went for Alchemy (see  http://alchemywebsockets.net ) because of its simplicity and monolitic DLL implementation.
Another important feature of Alchemy is its support for several versions of the WebSocket protocol. Since it is not a mature standard, there are many versions of it, depending on the browser brand, version and platform. The supported versions are hixie-76 (hybi00), hybi-10, hybi-17 and RFC6455.
As mentioned before, the main class in the windows side is a TraceListener-derived class, named WebSocketTraceListener. The implementation is really short, as follows:
C#
namespace System.Diagnostics
{
    public class WebSocketTraceListener : TraceListener
    {
        private Alchemy.WebSocketServer  server = new Alchemy.WebSocketServer();
        private Alchemy.Classes.UserContext connection = null;

        public void Initialize(System.Net.IPAddress ip, int port)
        {
            try
            {
                server.ListenAddress = ip;
                server.Port = port;
                server.OnConnect = OnConnect;
                server.OnDisconnect = OnDisconnect;
                server.Start();

                Debug.WriteLine("WebSocket Trace Listener: An error ocurred when the application tried to start");
                Debug.Listeners.Add(this);
            }
            catch (Exception)
            {
                Debug.WriteLine("WebSocket Trace Listener: Started correctly.");
            }
        }
        public override void Write(string message)
        {
            if (this.connection != null)
                this.connection.Send(message);
        }
        public override void WriteLine(string message)
        {
            if (this.connection != null)
                this.connection.Send(message);
        }

        private void OnConnect(Alchemy.Classes.UserContext context)
        {
            if (this.connection != null)
                this.server.Restart();
            this.connection = context;
        }
        private void OnDisconnect(Alchemy.Classes.UserContext context)
        {
            this.connection = null;
        }
    }
}

This class can be embedded directly into your application; there is no need to keep it in a separated DLL. There are two dependencies, though: The Alchemy library (Alchemy.dll, included in the solution), and System.Web.dll, which requires the project to be configured with the regular .Net Framework (and not the .Net Framework Client Profile) in order to allow you to attach that DLL. Finally, the listener class can be used by adding few lines of code: 

C#
var listener = new WebSocketTraceListener();
listener.Initialize(IPAddress.Parse("192.168.0.101"), 81);
...
Debug.WriteLine("This is a debug line");

Notice that the IP address belongs to the computer where the debugged application runs, and the port number should be one that is not blocked by your current OS and Hardware firewall settings; 81 is a good one. The IP address will be usually local, as it is expected to be connected from the mobile device inside a LAN context. In order to debug an application which is running in Internet, more settings may be required in your router.

A mockup WPF application has been created in order to send some debug strings according to the user interaction:

Image 4

Mobile device side

In the mobile device, the source code is simple as well, by using a native WebSocket client object, as in the following template:

JavaScript
var socket;

function Initialize(address) {
    socket = new WebSocket(address);
    socket.onopen = function (event) {
       // Prepare the UI here
    };
    socket.onclose = function(event) {
       // Reset the UI here
    };
    socket.onerror = function (event) {
       // Display the error (event.data)
    };
    socket.onmessage = function (event) {
       // Display the debug message (event.data)
    };
}
function Stop() {
    if (socket !== undefined) {
        socket.close();
    }
}

The address must indicate the ws:// protocol and shall match the IP and port specified in the Windows side.

Regarding the concrete tool, it has a minimalistic user interface. The first row allows to specify the socket address, start and stop the connection. The incoming lines will appear from top to bottom and can de scrolled up and down as usual:

Image 5

When a connection is succesful, the Connect button will become disabled and the Stop button will turn enabled. After stopping successfully, both buttons will swap their states.

Image 6

Deployment

The unique file required to be deployed is DebugConsole.htm, which contains all the HTML, CSS and Javascript code. It can be placed in any server. As this is an HTML5 application using WebSocket, the application will run as a standalone application after it is loaded through a browser. In order to convert this Internet page into a cached application, there are several tricks and tips required.

The first step to prepare a cached application is to create a manifest file. The minimum content would be something like:

CACHE MANIFEST
CACHE:
DebugConsole.htm

The web page is linked by specifying the manifest file into the html tag as: <html manifest="manifest_file">. This will keep the page cached into the local machine until the manifest is changed at server or the local cache is manually cleared by the user.

The big tip here is that the manifest must be served with MIME type text/cache-manifest. There are many authors that suggest to configure the server to allow this new MIME type for the specific file, but sometimes we don't have that degree of control. Another way to accomplish the same task, is to manually manipulate the headers while creating the manifest file dinamically. The sample source code shows a way to do it with PHP, but it can be done with ASP as well:

PHP
<?php ob_start(); header('Content-type: text/cache-manifest'); 
echo 'CACHE MANIFEST
CACHE:
DebugConsole.htm';
ob_end_flush(); ?>

A final trick can avoid hours of pain: When alterating the headers at runtime, it is extremely important to not to output any single character before the header is changed. That would throw a server exception. Be sure to remove any extra white spaces at the end of the scripted file.

iPhone specifics

There are few extra steps to make this a true iPhone application. We need a couple of resources: a 57x57 icon and a 320x460 splash screen. Both image files can be png or jpeg. After creating the required resource files, they shall be added to the manifest:

PHP
<?php ob_start(); header('Content-type: text/cache-manifest'); 
echo 'CACHE MANIFEST
CACHE:
Debug_Icon.png
Debug_Start.png
DebugConsole.htm';
ob_end_flush(); ?>

In the HTML file, some META and LINK records are needed to tell iOS how to set some behaviors and deal with the resources added:

HTML
<head>
    <title>VS Debug</title>
    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />

    <link rel="apple-touch-icon" href="Debug_Icon.png"/>
    <link rel="apple-touch-startup-image" href="Debug_Start.png"  media="screen and (min-device-width: 200px) and (max-device-width: 320) and (orientation:portrait)" />

The final steps can be seen in the following screen shots. In summary, you have to browse to the desired page by using Safari and then add the page to the Home Screen. The iOS will scan the manifest and take the icon and splash screen for future use.

Image 7 

And that's it! You are now able to develop more complex iPhone/HTML5 Apps connected to other Window applications. 

Live Demo 

I have temporarily published a live demo of the debug application at the address:  http://www.jaimeolivares.com/debug/DebugConsole.htm, but it could be removed or relocated at any time. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)