首款WindowsPhone 7 PC端实时截图工具诞生攻坚实录(二)
接上一篇:《首款WindowsPhone 7 PC端实时截图工具诞生攻坚实录(一)》
上节内容回顾:我开发了一款对真机进行截屏的软件,核心技术点就是在WindowsPhone中调用Com组件。
三、图片传出去
我们已经解决了截屏和后台运行的问题,下面怎么把截屏下来的内容显示到PC上呢?肯定使用网络了。简单测试以后非常开心,只要把手机连接到电脑上,并且启动Zune,将电脑和手机设置为Connected状态,这样手机就可以连接电脑上的网络服务了,还可以通过电脑来访问外网,无需Wifi。
因为Mango之前的WP7只支持通过WebClient访问Http网络,无法编写普通Socket程序(除非用上面的方法调用Dll),因此服务器端我用一个Http服务器接收客户端提交上来的截图图片流然后显示出来即可,服务器端我用了一个开源的嵌入式http服务器“C# WebServer”(本地下载包),这样避免部署IIS,直接启动WinForm程序就能运行服务器,手机端通过WebClient来提交截屏的数据。服务器端和客户端的实现都是普通的.Net操作,不再详细讲。
编写手机端程序,定时截取图片传到服务器,服务器显示正常。偶耶!
依照我多年的开发经验,我感觉这么顺的就实现了绝对不正常,果然发现了问题。在截图程序中进行的操作都能截屏显示出来,但是离开程序后截屏过程竟然停止了,画面一直停在离开前的画面上。我勒个去,难道"修改注册表让WP7支持多任务的方法"只是一个传说?但是一调试就傻了,定时截屏的任务依然在忠实的执行着,也就是截屏程序确实是在后台执行的。这是为什么呢?
熟悉Silverlight、Windows Phone的朋友知道,SL/WP7中的WebClient操作全部是异步的,没有普通.net中DownloadData()、UploadData()等阻塞方法,例如:
WebClient wc = new WebClient();
wc.OpenWriteCompleted+=new OpenWriteCompletedEventHandler(wc_OpenWriteCompleted);
wc.OpenWriteAsync(Uri);
OpenWriteAsync执行完毕后上传操作并没有立即开始,而是wc_OpenWriteCompleted这个异步方法中执行。经过调试发现当离开手机程序以后,虽然后台线程还在运行,但是wc_OpenWriteCompleted方法不会再被触发。我猜测Silverlight中也是通过类似于Win32中的WM_TIMER事件实现的消息泵来进行任务的所谓“异步执行”的,当离开程序以后UI线程就暂停,所以异步操作就执行不了了。经过一通的反编译、查资料,发现确实如此,Dispatcher是靠DispatcherTimer来实现的。
没办法了吗?No!向灰太狼先生学习“我一定会回来的”!不要以为这样就能拦住我!消息泵停了我能给你来一个“心脏起搏器”强迫消息泵继续运转,经过反编译、反射发现Dispatcher有一个私有的Dispatch方法用来驱动消息泵,我不断的调用Dispatch方法不就可以强迫消息泵运转了吗?有人问“私有方法不是不能调用吗?”,一切限制都是给别人设置的,坚强的灰太狼总能想到解决方案。“通过反射调用私有成员”这已经是一个非常常见的一个高级技术了,可以解决很多常规手段解决不了的问题。
编写类似于下面的代码来调用私有方法吧!
var m = d.GetType().GetMethod("Dispatch", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
mInvoke(d, new object[] {});
不幸的是调用过程发生异常,经过查询资料得知一个不幸的消息,基于安全考虑,Windows Phone中不能通过反射调用私有成员。
看来通过WebClient发送截图的思路失败了,幸好能够通过前面说的方法调用Native的代码,我开启VC++调用socket api编写了提交Http二进制数据的一个Com组件,没想到自认为TCP搞的还算不错的我竟然在这个“小环节”上载了跟头,连续熬了两个晚上调试Http发送图片流都没有调试成功,总是发送前几帧能显示后面的数据就全部乱掉了,没过多久还报了10061之类经典的Socket错误码。而把同样的代码放到Windows XP下编译运行就没问题。在无数次“我的Socket代码的错?”、“Windows CE Socket有特殊的地方?”、“凌晨三点了,快点睡吧”、“差一点就好了,再熬一会”的挣扎中,我最终放弃了。改为调用高度封装wininet中的HttpOpenRequest等方法来实现,很快就运行通过了。至今没有搞清楚到底是我的代码的问题还是Windows CE的Socket有特殊的地方,如果是我代码的原因那么可能就是没有处理好大数据量的缓冲区、“Http 1.1/ 100 continue” 等细节问题。还好通过HttpOpenRequest这种方法实现了,期待网络编程牛人的指导。