用手机控制你的电脑应用程序






4.49/5 (19投票s)
使用手机浏览器控制 PC 应用程序的超轻量级远程控制器
引言
用您的手机控制您的.NET PC应用。无需在手机上加载任何应用,您就可以从手机(Android、iPhone、iPad——任何有浏览器的设备)控制您的PC应用,只需提供一个带有控件的HTML页面并处理输入。此外,本文还将展示编写您自己的Web服务器所需的大部分代码。
背景
有很多种方法可以让手机控制PC应用,我认为这种方法是最简单的。我称之为“超轻量级”,因为它只做几件事……接收远程设备的按钮点击和文本输入。但如果您的应用只需要这些,那么这很简单。
与在远程控制设备上安装应用(市面上有很多可用的应用)不同,这个控件完全从PC端工作。它会在PC上打开一个“微型Web服务器”,该服务器可以向远程设备提供一个简单的HTML页面,显示控件。当远程浏览器将用户输入发送回PC时,控件会对其进行解码并引发一个事件。
这种方法的优点
它适用于任何设备——iPhone、iPad、Android,甚至是另一台PC。
它相对安全——提供网页内容是比较安全的。
远程设备无需下载/安装任何东西。
您可以自定义以仅显示您的应用所需的控件。远程设备的UI就是一个简单的HTML页面,易于自定义。
在远程设备上,您可以创建一个桌面快捷方式,直接指向远程控制页面,就像启动手机应用一样方便。
作为开发人员,您不必下载一堆工具来学习Android开发的Java库,也不必购买Mac来开发iPhone应用。
一些限制
您无法进行拖动或其他触摸/鼠标输入(按当前代码编写)。
用户必须在远程设备上手动输入URL——远程设备无法自动搜索网络上的应用程序。
按当前代码编写,您无法更改远程设备上显示的控件,因为它们在UI事件解码操作发生之前就已经提供给远程设备了。因此,如果应用程序已停止,远程设备也无法正确响应。
如果启用了Windows用户账户控制,程序必须拥有管理员权限。
防火墙可能会阻止访问。虽然我在我的Vista笔记本上使用Norton 360时没有遇到任何问题,但在我的Windows 7机器上,该应用可以从*本地*浏览器访问,但无法从远程设备访问,直到我编辑防火墙设置以允许万维网(HTTP)服务。
使用代码
下载、编译并尝试演示程序:您可以按三个按钮之一来更改窗口的背景颜色——这只是为了演示一些功能。程序启动时,它还会显示您需要在远程设备浏览器中输入的URL。为了方便测试,您首先可以在本地计算机上的浏览器中进行尝试。
您应该会看到一个类似这样的窗口
点击按钮会改变PC应用窗口的背景。
然后,在您的远程设备上,打开一个Web浏览器,输入应用程序中显示的URL。
您应该会看到类似这样的屏幕
玩得开心!
要将此控件改编到您自己的应用程序中:将此控件添加到将处理来自远程命令的窗口中,并添加一个事件处理程序来处理控件的RemoteInputReceived事件。每当从远程设备接收到输入时,就会调用此处理程序。在事件处理程序中添加您自己的代码——通常,各种远程控制事件会映射到您窗口中已有的控件。
演示事件处理程序如下所示
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 = ""; }
编辑HTML页面(RemoteControl.htm)以包含您的应用程序相关的控件。理论上,您可以使页面尽可能复杂,但实际上,编写*设备无关*的HTML意味着它应该保持简单。
当窗口加载(或初始化)时,调用“StartServer”来启动远程功能。调用StopServer来结束远程功能。
幕后
HttpListener是关键。通过少量代码,我就可以创建一个简单的Web服务器来提供HTML页面。以下是提供页面和获取响应所需的所有代码
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);
}
}
HttpListener对开发人员有一些不便之处
它是一个阻塞函数,因此需要将其放在自己的线程中。我把它放在BackGroundWorker线程中,因为它很简单。我使用BackgGroundWorker的ReportProgess功能,每当从远程设备接收到消息时,这样结果就会回到UI线程,在那里可以用来控制其他操作。
HttpListener让开发人员需要自行解析任何参数。我将它们解析到一个Dictionary(静态)中,并将其传回UI线程的事件中。从Dictionary中确定用户输入,只需要检查它是否包含特定键,或获取与键关联的值。键是HTML中控件的“名称”,区分大小写。这是输入解析器
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);
}
}
}
- 如果启用了用户账户控制(UAC),HttpListener需要管理员权限。为了解决这个问题,我在演示应用程序中添加了一个清单文件,并包含以下一行
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
关注点
这里有一个有趣的浏览器怪癖:与IE不同,我手机上的Android浏览器会忽略地址栏中输入的端口号。如果我输入http://myPC:9999,浏览器实际上会转到http://myPC:80。这就是为什么我使用端口80进行远程控制。由于我的开发机上运行着Web服务器,当我想要运行远程控制程序时,必须先将其关闭(命令提示符:“net stop was”),完成后再重新启动(命令提示符:“net start w3svc”)。
这是另一个浏览器怪癖:如果我点击网页上的按钮,PC程序响应之前会有明显的延迟。如果我按住按钮一会儿,在我释放按钮时,响应时间基本上是即时的。我推测,如果这对您的应用程序来说是个问题,可能存在一种JavaScript方法来消除延迟。我尝试在按钮上使用'onmousedown',在我尝试IE时是有效的,但在Android上却不行。欢迎提供建议。
注意:这种小型应用/控件旨在跨平台使用,但对于单人开发者来说,进行测试极其困难,因为许多外部因素可能导致其无法正常工作。我将非常感谢您在哪些平台/配置上测试成功(或失败)的反馈,以帮助它变得更有用。
历史
首次提交 2012年4月6日
修正了一些拼写错误 2012年4月8日