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

Control Your PC App With Your Cell-Phone

4.49/5 (20 votes)
8 Apr 2012CPOL6 min read 85.6K   3.4K  
Ultralight Remote Control for Your PC App using your Cell-phone's Browser

Introduction

Control your .NET PC app with your cell-phone. Without loading an app on the cellphone, you can control your PC App from a cell-phone, android, iPhone, iPad—anything with a browser by serving an HTML page with the controls and handling the input. On the way, this article shows most of the code needed to write your own web server too.

Background

There are many ways for a cellphone to control a PC app and I think this one is the easiest. I call it “Ultralight” because it only does a few things...get button presses and text input from the remote device. But if that's all you need for your app, this is straightforward.

Instead of installing an app on the remote control device (there are quite a few available), this control works entirely from the PC side. It opens a “micro-web-server” on the PC which can serve a simple HTML page to the remote device showing the controls. When the remote browser submits user input back to the PC, the control decodes it and raises an event.

Some advantages for this approach

  • It works with any device—iPhone, iPad, Android, even another PC.

  • It is reasonably secure—serving a web page is fairly safe.

  • There is no download/install needed on the remote device.

  • You can customize to display just the controls needed for your app. The UI on the remote device is simply an HTML page and is easy to customize.

  • On the remote, you can create a desktop short-cut which goes directly to the remote-control page so it is as easy as starting a phone-app.

  • As a developer, you don't have to download a bunch of tools and learn the java libraries for Android development or buy a MAC for iPhone development.

Some limitations

  • You can't do drags or other touch/mouse inputs (as written).

  • The user must key in a URL on the remote device—the remote cannot search the network automatically for your app.

  • As written, you cannot change the controls displayed on the remote device because they are served to the remote before the action is decoded by the UI event. As such, the remote also has no way of behaving properly if the app has stopped.

  • The program must have administrator privileges if Windows Account Control is enabled.

  • The firewall may block access. Although I have used this with Norton 360 with no issues on my Vista laptop, on my Windows 7 machine, the app could be controlled from a local browser but not from the remote until I edited the firewall settings to allow World Wide Web (HTTP) services.

Using the code

Download, compile and try the demo program: You can press one of the three buttons to change the window's background color—just to demonstrate some functionality. When the program is started, it also displays the URL you need to enter on your remote device browser. For testing convenience, you can try it out with a browser on your local machine first.

You should see a window like this:

Image 1

Clicking the buttons will change the window background on the PC App.

Then, on your remote device, open a web-browser and enter in the URL shown in the app.

You should see screens like this:

Image 2

Have fun!

To adapt the control to use in your own app: Add the control to the window which will handle the commands coming from the remote and add an event handler to handle the control's RemoteInputReceived event. This handler is called whenever input is received from the remote device. Add your own code to the event handler—usually, the various remote control events will map to controls which already exist in your window.

The demo event handler looks like this:

private void remoteControl11_RemoteControl(object sender, RoutedEventArgs e1)
{
     var e = e1 as RemoteControl1.RemoteControlEventArgs;
     if (e.pageParams.ContainsKey("red"))
                buttonRed_Click(null, null);
     if (e.pageParams.ContainsKey("blue"))
                buttonBlue_Click(null, null);
     if (e.pageParams.ContainsKey("green"))
                buttonGreen_Click(null, null);
     if (e.pageParams.ContainsKey("name") && e.pageParams["name"] != "")
                textBlockHello.Text = "Hello " + e.pageParams["name"] + "!";
     else
                textBlockHello.Text = "";

}

Edit the HTML page (RemoteControl.htm) to include the controls relevant to your app. In theory, you can make the page as complex as you like but in practice, writing device-independent HTML means things should stay simple.

When the window loads (or is initialized) call “StartServer” to start the remote functionality. Call StopServer to end the remote functionality.

Under the Hood

The HttpListener is the key. In a small amount of code, I could create a rudimentary web server which can serve up an HTML page. Here's all the code needed to serve up a page and get a response:

C#
void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    // Create a listener.
    listener = new HttpListener();
    // Add the prefixes.
    listener.Prefixes.Add("http://*:" + portNumber.ToString() + "/");
    try
    {
        listener.Start();
        //read in the html page to serve
        TextReader tr = new StreamReader("RemoteControlPage.htm");
        string pageString = tr.ReadToEnd();
        while (!worker.CancellationPending)
        {
            // Note: The GetContext method blocks while waiting for a request. 
            HttpListenerContext context = listener.GetContext();
            if (context.Request.HttpMethod == "POST")
            {
                //this is a POST, handle the input parameters
                var body = new StreamReader(context.Request.InputStream).ReadToEnd();
                ParsePostParameters(body);
                worker.ReportProgress(1); //raise the event
            }
            // Set up the response object to serve the page
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(pageString);
            context.Response.ContentLength64 = buffer.Length;
            context.Response.OutputStream.Write(buffer, 0, buffer.Length);
            context.Response.Close();
        }
        listener.Stop();
    }
    catch (Exception e1)
    {
        if (!cancelling)
            MessageBox.Show("Remote Listener failed. " + e1.Message);
    }
}

The HttpListener has a few inconveniences for the developer:

  • It is a blocking function so it needs to be put in its own thread. I put it in a BackGroundWorker thread because it was easy. I use the ReportProgess functionality of the BackgGroundWorker whenever a message is received from the remote device, this way, the result ends up back in the UI thread where it can be used to control the other actions.

  • The HttpListener leaves the developer on your own to parse any parameters. I parse them into a Dictionary (static) and pass it back to the UI thread in the event. Determining the user input from the Dictionary is just a matter of asking if it Contains a certain key or getting the value associated with a key. The key is the “name” of the control in the HTML and is case sensitive. Here is the input parser:

C#
private static void ParsePostParameters(string body)
{
    //format:  name1=value1&name2=value2&name3=value3
    string[] stringParams = body.Split('&');
    pageParams.Clear();
    foreach (string s in stringParams)
    {
        int index = s.IndexOf('=');
        if (index > -1)
        {
            string key = s.Substring(0, index);
            string value = s.Substring(index + 1);
            value = System.Uri.UnescapeDataString(value); //removes all the 'secret' character encoding
            pageParams.Add(key, value);
        }
    }
}
  • If User Account Control (UAC) is enabled, HttpListener requires administrator privileges. To cope with this, I added a manifest file to the demo application with the line:
XML
<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />

Points of Interest

Here's an interesting browser quirk: Unlike IE, the android browser on my phone ignores port numbers entered into its address bar. If I key in http://myPC:9999, the browser actually goes to http://myPC:80 instead. That's the reason I use port 80 for the remote. Since I am running a web server on my development machine, I have to shut it down (Command Prompt: “net stop was”) whenever I want to run the remote control program and restart it (Command Prompt: “net start w3svc”) when I'm done.

Here's another browser quirk: If I touch buttons on the web page, there is a noticeable delay before the PC program responds. If I hold the button for a moment, the response time is essentially instant when I release the button. I speculate that there may be a Javascript way to eliminate the delay if it is a problem for your app. I tried using 'onmousedown' on the buttons and this worked when I tried it on IE but not on the Android. Suggestions are welcome.

NOTE: this is the kind of little app/control which is intended to be cross-platform is exceedingly difficult for a lone developer to test because many external factors could make it not work. I would appreciate feedback on the platforms/configurations where it works (or not) to help make it more useful.

History

Initial submission 4/6/12

Fixed some typos 4/8/12

License

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