首款WP7 PC端截图工具诞生攻坚实录

来源:互联网 发布:网络回拨电话 编辑:程序博客网 时间:2024/04/28 04:50
 

这是世界上首款WindowsPhone 7 PC端实时截图工具,能够实时显示Windows Phone真机中的界面!
演示GIF动画如下:


演示视频如下:

 

安装包下载:http://files.cnblogs.com/rupeng/ScrnMonitor20110620.zip
安装方法:

1、首先把手机连接到电脑上,并且启动Zune,将电脑和手机设置为Connected状态。
2、ItcastWPDevPC20110620.zip 是PC端程序,启动ItcastWPDev.exe,设置监听端口后点击【Start】启动监听。
3、WP7ScreenMonitorAgent.xap是手机端程序。安装后启动“ScreenMonitor”,设置服务器IP和端口后点击【Start】启动手机端监听即可。如果要退出程序进入Tile主页面或者进入别的程序,那么需要按Home键,也就是Windows徽标键来回到Tile主页面,不要按后退键退出程序,否则监听进程会退出。

下面开始正题,这么多年没熬过夜了,这次我连续四天熬夜到3点多终于搞出来了这款工具,遂写下战斗日记。PS:不要熬夜,真的伤身体。
一、有需求才能有产品——“学生说:要有是实时截图工具!所以就有了截图工具。”
最近在备课传智播客.Net培训班的Windows Phone 7(WP7)的课程内容,虽然说通过模拟器基本能够让学生理解Windows Phone手机,不过如果能让学生看到真机那感觉还是不一样的。如何把Windows Phone真机的屏幕展示给讲台下的学生看就成了必须解决的问题,曾经想着用电脑摄像头照手机的屏幕,但是反光问题很难解决,而且手在操作触摸屏的时候就会遮挡屏幕,因此需要一款通过程序来截取真机屏幕显示到PC上的工具。

二、解决后台截屏问题——稳扎稳打
“不要重复造轮子”,我先到网上搜索是否有了类似的工具,网上的资料都是关于截取程序截图的,我想要一个也能截取手机中其他的程序界面的工具。最终找到一款牛人fiinix开发的“CSharp DllImport”,这个工具中提供了后台截屏的功能,开启程序以后进入到要截屏的界面按下拍照键就可以将当前界面的截图保存到MediaLibrary中,就是它了!


不要小看这款工具,熟悉Windows Phone的开发人员看到它绝对有“它打开了通向Windows Phone未知领域的大门”,为什么这么说呢?首先看“后台”功能,众所周知,在Mango之前WP7是不支持多任务的,因此一旦离开程序进入别的程序,那么当前的程序就会被结束掉了。“CSharp DllImport”使用的是网上流传的《修改注册表让WP7支持多任务》的方法来避免离开程序比较程序被结束的问题。“CSharp DllImport”中启动一个无限循环的后台线程,不断检测手机的按键状态,如果用户按下了拍照键,则会自动进行系统截屏。问题又来了,Windows Phone中只能对本程序的界面进行截图,是没有提供全屏截屏的功能,对Windows Mobile、Windows CE的朋友可能会说“那调用GDI来截图不就行了吗?”,不幸的是,Windows Phone中不支持PInvoke机制的,也就是无法编写调用GDI等win32 dll中的函数的代码,更不能调用自己用C/C++写的dll。

“Windows Phone中不支持PInvoke机制”这句话只是给你我这等菜鸟听的,牛人从来不相信。有牛人发现了《在Windows Phone中调用win32 dll的方法》,原理就是微软的Microsoft.Phone.InteropServices.dll中的ComBridge类提供了RegisterComDll方法用来注册一个Com组件到WP7系统中,这样将Native代码写成一个Com组件,然后通过RegisterComDll注册到系统中即可。神奇的大门打开了!!!

Windows Phone底层仍然是Windows CE,可以把Windows Phone看成Windows CE上运行的一个大软件,就像当年的Windows 3.1是DOS上运行的一个软件一样,因此我们就可以用支持Windows CE开发的平台开发出Com组件,可以调用任何的Windows CE的SDK,然后放到Windows Phone中去执行。


使用ComBridge调用VC编写的Dll的几个关键点,这块开发要涉及到Com组件的开发以及Interop的知识,这块知识不是这篇文章能覆盖的,因此如果您不熟悉Com这个.Net的老祖宗,那么可以不用看,也不影响你对于整体架构的理解。如果你需要熟练下面的每项技术,欢迎你来传智播客.Net培训班来学,我手把手的教你。
1)在VC中创建一个Com项目。注意如果要运行在HTC等Arm CPU的真机上时,需要构建ARM平台的二进制代码,如果要在Windows Phone7模拟器上运行的时候则需要构建为X86的平台,因为XDE是X86的CPU。
把Com组件生成的dll每次修改Com组件并且编译通过后都要拷到WindowsPhone项目中。为方便调试,建议将Com项目的输出路径指向Windows Phone项目,这样避免了把生成的dll拷来拷去的麻烦。

2)添加WMAppManifest.xml 到Windows Phone项目根目录下内容为:
<?xml version="1.0" encoding="utf-8" ?>
<Interop>
</Interop>

3)修改Windows Phone项目的WMAppManifest.xml文件,在Capabilities节点下增加
<Capability Name="ID_CAP_INTEROPSERVICES" />
也就是给程序INTEROP的能力。

4)在使用组件之前(比如程序启动的时候、类的Static初始化中)调用RegisterComDll方法用来注册Com组件
例子:ComBridge.RegisterComDll("Network.dll", new Guid("0111B877-9EE5-45ba-800E-CF324B5552C7"));
Network.dll 为Com组件的dll文件名,"0111B877-9EE5-45ba-800E-CF324B5552C7"为组件实现类的Guid。

5)在Windows Phone中声明对应的实现类和接口,使用ComImport标记类和接口,例如:
    [ComImport, Guid("0111B877-9EE5-45ba-800E-CF324B5552C7"),
    ClassInterface(ClassInterfaceType.None)]
    internal class NetworkClass
    {
    }
   
    [ComImport, Guid("25CC9D0F-CA11-4DED-8019-BC7C20CEE5D3"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface INetSockets
    {
        [PreserveSig]
        int HttpPost(string remoteServer, int remotePort, string path, IntPtr buffer, int len);
    }
    然后INetSockets socks = new NetworkClass() as INetSockets;就可以拿到组件的实例然后调用Com组件中的方法了!
   
   
    “CSharp DllImport”中使用的是CreateCompatibleDC、BitBlt等GDI方法来进行屏幕截取的,没什么特别的,不再详述。
    现在已经有很多基于这个机制的WP7程序了,比如注册表编辑器RegistryEditor、TouchXPlorer、Yaaf.Wp7.AdvancedExplorer等。
    注意使用这种方法调用Native代码是无法通过Marketplace审核的,所以这样的工具只能在越狱的机器上直接部署Xap的方式安装。
    现在Windows Phone还没有系统级别的中文输入法,我猜测也许可以将Windows CE上的中文输入法通过这样的方法移植到Windows Phone上,这样可以解决Mango之前大家用不了输入法的问题,有高人如果想做这项好事,我非常非常以及非常愿意提供帮助。

三、图片传出去
   我们已经解决了截屏和后台运行的问题,下面怎么把截屏下来的内容显示到PC上呢?肯定使用网络了。简单测试以后非常开心,只要把手机连接到电脑上,并且启动Zune,将电脑和手机设置为Connected状态,这样手机就可以连接电脑上的网络服务了,还可以通过电脑来访问外网,无需Wifi。
   因为Mango之前的WP7只支持通过WebClient访问Http网络,无法编写普通Socket程序(除非用上面的方法调用Dll),因此服务器端我用一个Http服务器接收客户端提交上来的截图图片流然后显示出来即可,服务器端我用了一个开源的嵌入式http服务器“C# WebServer”本地下载包),这样避免部署IIS,直接启动WinForm程序就能运行服务器,手机端通过WebClient来提交截屏的数据。服务器端和客户端的实现都是普通的.Net操作,不再详细讲,感兴趣的可以参考传智播客的.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方法不就可以强迫消息泵运转了吗?有人问“私有方法不是不能调用吗?”,一切限制都是给别人设置的,坚强的灰太狼总能想到解决方案。“通过反射调用私有成员”这已经是一个非常常见的一个高级技术了,可以解决很多常规手段解决不了的问题。当然如果你不了解如何实现“通过反射调用私有成员”这又想学习的朋友可以关注传智播客的.Net培训,其中“.Net 高级技术”的课程就讲到了这些。
   编写类似于下面的代码来调用私有方法吧!
   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这种方法实现了,期待网络编程牛人的指导。

  前两天看xda上的一篇帖子,是牛人fiinix对我的WP7实时截图工具做了优化《WP7 Screen recorder (Based on "The DllImport Project")》 ,把1帧每秒的截屏提高到了3帧每秒,画面流畅了不少。

fiinix主要的优化点:
1、没有采用jpeg进行压缩,这样可以节省压缩的时间;
2、采用普通Socket代替Http协议来提交数据,提高性能。我采用的是Http协议,由于Http协议每次都要建立连接、传输数据、断开连接
3、把截图、发送等放到单独的线程中执行,线程间通过信号量进行通信,提高性能。
这个帖子是fiinix的优化说明:http://forum.xda-developers.com/showthread.php?t=1093169&page=8

        fiinix是一个瑞典佬,看到fiinix的帖子中写的“Zomg, the Chinese could not program good fps,So i super optimized the shit code of theirs.”,一个“the Chinese”听得出他对中国人的不屑,“shit code of theirs”→“他们的那些狗屎代码”,我代表了一次中国人。估计他看到我的作品的时候,心里肯定在想“那帮老土的中国佬先于我做出这个工具来了?”,然后就开始找我作品暂未优化的部分心里自我安慰的说“这帮狗屎中国佬的代码效率还是太低了,还是我们伟大的西方国民牛逼”。呵呵,鬼佬,不用醋意大发,在windows phone开发方面我这个中国佬和你们一起站在了世界开发的最前沿,这不是个案,中国的牛X的人有的是,和他们比我只是一个小鸟,希望以后你们不要再用“the Chinese”统称中国人,以后再碰到中国人和你交流技术,请喊他们的中文名字,请我叫“yang zhongke”,就像我叫你fiinix一样!

       还是聊技术吧,fiinix的优化还是效果不错的,本来我还是打算优化的,有人做了我就不做了,抛砖引玉成功,以后就“用玉”了,哈哈。

      我还有一个优化想法,我说出来,有人能做出来最好了。我想起来有一项VNC技术可以用来进行远程控制,VNC的效率非常高,原理也很简单,也就是监听鼠标、键盘等事件,高效率的每次只传输变化的部分。我还找了一个开源的Windows CE版的VNC服务器端在Windows CE上编译、运行通过了。但是移植到Windows Phone上总是报GDI相关的错,由于调试也比较麻烦(只能调用Win32的MessageBox方法来进行调试),个人业余时间也不多,后面还有其他的备课任务,因此我就放弃了。但是有一个问题要解决,那就是网络的问题。因为运行在手机上的是VNC的服务器,如果只是通过USB连接电脑,电脑上无法建立到手机服务器上网络连接,因为这时候手机和电脑完全不是一个网段,只能手机连接电脑的Socket服务器(我不是网络方面的专家,不知这样表述是不是正确),必须他们同时连到wifi才是在一个网段,这也是我放弃Windows Phone版VNCServer的原因。期待有牛人能够继续下去。我只做了一个优化,就是每次手机向服务器发截屏之前检查这次的截屏是否和上次一致(很土但是效率还挺高的流逐位比较的方法),如果一致的话就不发送,这样能降低截屏监控客户端给手机CPU的压力。
  

五、感想
    经过这个工具的开发我有如下的感想:
    1)任何事情都终将解决。“Mango之前无法多任务”、“WP7中无法调用Native代码”等这些难题都被高人解决了。在传智播客.net培训班上讲课的时候经常有学生问“老师,能不能实现***功能”,我一般开头第一句就是“只要有钱有时间没有实现不了的,开发人员是万能的”,所以如果你学习、工作中遇到了难题,在时间、成本可控的前提下,可以攻坚一下,对于技术控来说,搞定一个难题是可以狠狠得瑟一把的事情。
    2)学习任何的技术都要尽可能的研究它的本质,不能仅仅局限于“会用就行”。比如只有熟悉了Silverlight中SynchronizationContext、Dispatcher、DispatcherTimer等之间的内在联系以及Silverlight的异步调度机制,处理难题的时候才有思路。前几天有人在博客园上写了篇文章“学ASP.net不要只会拖控件”,我非常赞同,如果只会拖控件,而不理解Http协议、HTML等这些东西,很容易就被WebForm控件绕晕,遇到一个难题就只能去各个论坛求爷爷告奶,而如果知道Http协议、HTML等这些东西,那么完全可以自己分析出解决问题的办法。所以传智播客讲ASP.Net的时候不是一上来教学生拖控件,而是先讲通过不用控件的方法开发网站,学生理解了Http协议、HTML等这些东西以后再学看似复杂的WebForm控件的时候学生都催“老师,这些控件没啥意思,我们都能自己琢磨出来,讲点别的吧”,呵呵,我很欣慰。
    3)艺多不压身。任何行业都是如此。我上大学的时候通过研究过Com、为了录《C语言也能干大事》的视频我研究了C/C++,没想到这次开发截图工具就用上了这些技术,如果我不懂C/C++、Com,那么编写Com组件那块就是不可能完成的任务了。有人说“学那么多技术有什么用?又不来钱!”,说这种话的人根本没有学到啥技术,很多人工作两三年还是三四千块钱,而我那些学得比较好的学生刚毕业就拿到7、8K(如果你没有别的更好的发大财的道儿或者还没有赚到比7、8K这个“基本温饱工资”更多的话,就建议你不要说“我认识某某人不是搞技术人家照样身价千万”、“7、8K也算高薪?”这样不疼不痒的话了
    4)不要瞧不起自己,在条件允许的情况下尝试一下自己的想法,不要认为“比我聪明的人有的是,他们没解决我怎么可能解决?”,成功不是光靠聪明就可以解决的,世界上能把想法付诸行动的人不多。我当初想:“没人做一个PC端截图工具,是不是技术上做不出来呀?但是我想可以!”,我想可以,所以我就尝试了一把,就做出来世界上第一款Windows Phone的PC端截图工具,气死了鬼佬。争取你也能找到这种Happy的感觉。
   
    最后请我再总结这篇文章中的核心点:
    1)Windows Phone底层是Windows CE。
    2)Windows Phone7中可以通过调用ComBridge调用本地代码,这样如果想调用C/C++代码或者调用PInvoke只要编写Com组件即可。使用这种方法编写的程序无法通过Marketplace审核。最新消息“Mango中将不可以通过Interop调用本地代码”,希望早日有牛人解决这个问题。
    3)程序在后台运行的时候消息泵是暂停的,所以WebClient、DispatcherTimer等都会暂停。
   
    暂时买不了真机的朋友也可以安装WP7开发工具来通过仿真器来体验一下,当然和真机感觉没得比了。不过微软出的Windows Phone 7仿真器必须运行在Win7下,如果现在只能用Windows XP,那么推荐你下载“传智播客WP7(Windows Phone 7)模拟器”。想学习WP7开发的朋友可以买一款WP7真机,现在真机非常便宜,1500元左右就能上手一款,绝对抄底的最佳时机,而且用真机开发有一个好处,就是可以在Windows XP下进行开发,只要修改WPDT的一个配置文件就可以在Windows XP上安装WPDT了,具体搜索“Windows XP 安装Windows Phone SDK”。  

原创粉丝点击