开发 Windows RT 桌面应用(来自 Surface RT)

闲谈

这篇文章是用 Surface RT 写的。先喷一下这设备不爽的地方!

  1. 请看 PPI 对比:
  • Surface RT = sqrt(1366^2+768^2)/10.6 = 147.8

  • XPS 15 = sqrt(1920^2+1080^2)/15.6 = 141.2

居然才比 XPS 15 高了一小点!

  1. 后摄像头成像质量太一般。

再来要说的是,微软的自残行为。UMU 用过 iOS、Android 平板,甚至见过有人用 XP 平板,但相信,论系统本身 Windows RT 是最强大的。不过微软为了战略目标,把 RT 强大的一面给锁起来了。对开发人员来说,这锁表现在以下几点:

  1. 系统本身不允许运行没有微软签名的 EXE;

  2. VS2012 默认无法编译 ARM 程序;

  3. VS2012 自带的 ARM lib 缺失。

开始折腾

下面就是简单介绍一下如何突破这三个封锁:

1. 解锁签名限制

RT Jailbreak Tool By Netham45, Version 1.20

http://forum.xda-developers.com/showthread.php?t=2092158

另外,有很多开源软件已经移植,在开发自己的程序之前,可以先试试,Desktop apps ported to Windows RT:http://forum.xda-developers.com/showthread.php?t=2092348

2. 开启 VS2012 的 ARM 支持

来自 http://stackoverflow.com/questions/11151474/can-arm-desktop-programs-be-built-using-visual-studio-2012 的答案

You can edit the file:

C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\ARM\Microsoft.Cpp.ARM.Common.props

对 VS2013 路径是:

C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\ARM\Platform.Common.props

In thesection add the line:

<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>

before </PropertyGroup>

And that’s all, you can build ARM desktop apps with VS2012.

某些工程需要强制定义 _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 才可以。

3.获取更多的 ARM libs

开源工具应运而生:https://github.com/peterdn/dll2lib

然后,炫耀一下,UMU 已经把自己的一个小作品“天翼宽带智能提速”移植成功。这个程序比较小,一两个小时从解锁到移植开发全部搞定。

最后,如果程序是 .NET 4.x 写的,是可以直接跑在 RT 上的,所以为了省力气,也许应该考虑多用 .NET。

诗盗·震邦

《#诗盗#·震邦》:汶川宝宝芦山没,装逼作秀唱你妹。各种悲剧各种坑,一半红情一半黑。

注解

评新闻《汶川地震宝宝芦山地震去世》和《韩红怒斥赈灾歌曲:唱歌比救援还重要?》,顺便纪念过世的好友陈震邦。

解析 Windows 快捷方式的经验

需求

在 Windows 平台,解析快捷方式(.lnk 文件),获得它指向的目标路径。

分析

有以下相关 API 可以使用:

  • IShellLink 对象
  • MsiGetShortcutTarget

实践

首先,当然是 MSDN 一下 IShellLink 这个 COM 接口,照着写一下,然后测试。郁闷的事情开始了!IShellLink 对象无法正确解析 MSI 创建的新型快捷方式。

所以要用 MsiGetShortcutTarget + MsiGetComponentPath 先尝试获得,失败了才用 IShellLink。

继续测试大量快捷方式后,发现另一个问题:当自己是 x86 程序时,读取的快捷方式如果指向的是 Program Files 下的任何文件或者文件夹,都会被强制解析为 Program Files (x86),好智能啊!!

搜一下,发现使用 IShellLink 对象的前提下,无法解决这个问题。无数老外都郁闷!

于是开始逆向,发现是 IDL 被读出来时就已经做了转换,所以用 IShellLink::GetIDList + SHGetPathFromIDList 也是浪费力气。

最后无奈了,如果发现自己在 WOW64 下运行就直接自己解析快捷方式了……

参考

  1. 【已失效】http://www.vckbase.com/index.php/wv/1132
  2. https://www.codeproject.com/Articles/24001/Workaround-for-IShellLink-GetPath
  3. 【已失效】http://biancheng.dnbcw.info/1000wen/447642.html

程序员心法三则

本文不是介绍奇技淫巧,甚至本质上并不是技术,而是态度,心法。

1.抓住问题的本质,在源头解决问题

简单地说,A 有八哥,B 依赖 A,所以导致依赖 B 的 C 出问题,您会通过修改 B 来解决问题吗?正常人都知道要先解决 A 的八哥,蛋似,稍微复杂、含蓄点的问题就有人迷糊了:

一个浮动小窗体,不希望在任务栏上出现标签。

UMU 见过有人使用了 ITaskbarList 对象的 DeleteTab 方法来删掉任务栏上的标签,很高端的做法。蛋似,不够本质,我们要的是不让它出现,而不是出现后擦掉……很早以前,explorer.exe 挂掉后,任务栏通知区域的 QQ 图标就消失了,因为当时 QQ 没有处理任务栏重建的通知消息 TaskbarCreated,重新添加图标。前面说的方法,有同样的问题,explorer.exe 重启后,标签又会出现,还要再删除一次。

正确的主流做法有两个,看情况采用:(1)、WS_EX_TOOLWINDOW;(2)、指定一个隐藏窗体为自己的拥有者。

另一个脱裤子放屁的例子:获得一个文本文件大小,然后 new 一个够大的 char 数组 p,把内容读到 p 上,最后 ::std::string str = p; delete[] p;,这个见太多次,都懒得喷了。::std::string 有 resize 方法,可以直接分配,不需要 new 一个临时数组,再 delete……

判断系统是不是 XP》,也包含了这一哲学,表面上看有好多函数可以获得系统信息,但要明白他们的本质其实有差别,不是都可以混用。

2.要有远见,没有?至少不要不见棺材不落泪!

Y2K 已经过去了,但还有一个 Y2K38,又称 Unix Millennium Bug,历史原因 Unix 时间戳是一个 32 位整数,记录从 1970 年 01 月 01 日开始的秒数,它所能保存的最大时间长度大概是 68.1 年,2038 年 1 月 19 日 03:14:07 之后。

以前硬盘容量小,也不看高清,很多代码都认为文件大小用 32 位表示就够了,结果后来出现很多 ISO、高清电影,都超过 4G……还见过有人采集流量用 32 位整形表示,时间跑久了就溢出了。

远见未必人人都有,退一步说,UMU 敢保证,有很多人即使知道 32 位不够用,还是继续用着,明知道 IPv6 已经出现了很久,还是各种硬编码,认为 IP 地址一定是 IPv4 的地址。态度问题!

3.不要姑息养奸

遇到不合理的情况,UMU 认为应该给力地告诉该知道的人。比如,函数不希望入参是某指,可是调用者偏偏就输入了那个值,怎么办?打印调试信息?不够给力,容易被忽视,应该中断一下,告诉开发者。

配置文件字段被改错,怎么办?如果这个文件是技术人员维护的,应该抛出异常,死给修改配置文件的人看;如果是一般的最终用户,那应该弹出界面,友好提示哪里、怎么错了。

早期,很多程序员为了避免头文件被重复包含,就用了以下代码:

1
2
3
4
#ifndef XXX
#define XXX
// 各种语句
#endif

后来,大家喜欢用 #pragma once,省事,又不容易漏掉最后的 #endif。但是这样做之后会……姑息养奸!除非十分通用的工具类,对严谨的人来说,重复包含是不应该的!所以应该这样:

1
2
3
4
5
#ifdef XXX
#error "您不严谨了!"
#endif
#define XXX
// 各种语句

有重复包含立刻告警,而且都是集中在开头,不存在漏掉 #endif 的问题。

态度问题!这里只是举几个简单的例子~

卸载 LSP 并重启系统依然有服务加载它

发现问题,2012-12-11 18:16:00

快游(网游加速器)包含一个 LSP,属于加速核心组件,在测试 LSP 期间,发现一个奇怪的现象:反注册它,并 netsh winsock reset 加重启好几次……依然有程序加载它。用 Process Explorer 查看是:IpOverUsbSvc.exe 和 daemonu.exe。把 LSP 的 DLL 文件删掉,再重启,可以消灭这个奇怪的现象。但后来想重现这个怪现象时,却无法重现。

重现和解决问题,2012-12-25 15:38:00

问题自然重现,继续研究。这两个进程对应的服务名是:IpOverUsbSvc 和 nvUpdatusService。手动重启这两个服务后,即不再加载 LSP。推理:这两个服务很可能每次重启机器时都没有正常关闭,系统提供了某种机制让他们在下一次重启后快速恢复了运行现场(保留了有 LSP 注册时的环境)。

分析问题,2013-03-26 23:24:30

时隔三个月,偶然看到介绍"混合式关机"的文章,恍然大悟,原来是这货引发的八哥!

在安装 Win8 后,很多人都体验到了其开关机惊人的速度,尤其是开机速度,相比 Win7 之下,它提升的不止是一点半点。在某些超极本和配备了 SSD 的机器上,其开关机速度可以在数秒以内。例如 Surface Pro,其实测系统引导速度为2秒,从启动到自动登录到开始屏幕只要6秒。

究竟是什么技术提升了 Win8 的开关机速度呢?如果要用最简单的一句话概括,那应该是"系统会话休眠",或者更简单的,“混合式关机”。

在 以往的 Windows OS 中,典型的关机顺序为:

  1. 单击"关机"。

  2. Windows 广播运行应用程序关机信息,让应用程序可以保存数据和设置。应用程序也可以要求一些额外的时间以结束其当前工作。

  3. Windows 为每个登录用户关闭用户会话。

  4. Windows 向服务发送关机信息,通知已开始关机,接着关闭服务。如果服务未响应,系统将强制关闭。

  5. Windows 向设备广播信息,示意设备进行关闭。

  6. Windows 关闭系统会话(也称为"会话 0")。

  7. Windows 刷新系统驱动器待决数据,以确保完全保存。

  8. Windows 通过 ACPI 界面向系统发送信号以给计算机断电。

看着以上的典型关机步骤,你是不是也有想到一些步骤对应的屏幕上的 UI 表现呢~

再来看看 Windows 8 采用的混合式关机主要步骤:

  1. 单击"关机"。

  2. Windows 广播运行应用程序关机信息,让应用程序可以保存数据和设置。应用程序也可以要求取得一些额外的时间以结束其当前工作。

  3. Windows 为每个登录用户关闭用户会话。

  4. 系统会话休眠,并掉电。

可见,Windows 8 只关闭用户会话而不像以前那样完全关闭计算机。此时, Windows 不再等待并结束系统服务和关闭会话 0,而是让其进入休眠。这种关闭用户会话+休眠系统服务和系统会话的做法,被称为"混合式关机",也就不难理解了。

或者说得通俗点,就好比你之前打扫卫生时,需要先组装专业的拖布,组装好了以后,才能开始打扫卫生。而现在,你可以拿起拖布直接开始打扫,因为你上次打扫完之后,并没有将拖布这一工具像以往那样拆卸下来收好。

开机在结构上是关机的逆过程,所以有了混合式关机,开机自然也就快了。但是,除了 RAID 卡外,一般硬盘的读取速度会比写入速度略快,加上关机的时候,系统会通知并等待应用程序退出,所以从感官上,开机过程会比关机过程显得要快一些。

枚举物理网卡

其实目的是获取靠谱的 MAC 地址,但这个任务真蛋疼!不信您看看搜索出来的乐射……

神马 GetAdaptersInfo、GetIfEntry、GetAdaptersAddresses、NetWkstaTransportEnum,还有读取注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards。这些都会枚举到虚拟网卡,给您举个例子“VirtualBox Host-Only Ethernet Adapter”,读取神马 NetCfgInstanceId、MediaSubType,都不靠谱,没有平台移植性!

用 Setup API 枚举 Interface,匹配 PCI 和 USB 类型是比较靠谱的。

蛋似,虚拟机的网卡也是虚拟的,为了方便在虚拟机测试,您要注意放开一些特例……很抱歉,领导说代码要保密,自己搜吧,关键字:SetupDiGetDeviceInterfaceDetail、OID_802_3_PERMANENT_ADDRESS。

给个蛋碎的例子:\\.\pci#ven_10ec&dev_8168&subsys_050e1028&rev_06#4&224db6dd&0&00e5#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{4cc0ea76-88b7-40e1-8b4b-6339f8dd49bf} 可以简称为 \\.\{4cc0ea76-88b7-40e1-8b4b-6339f8dd49bf} 或者 \\.\Global\{4cc0ea76-88b7-40e1-8b4b-6339f8dd49bf}

协议加速的三个时代

这里要说的是基于反向代理+缓存的加速。位于客户端和服务端之间的加速系统,理解某些协议,并对这些协议进行处理,使客户端不需要做任何改动的前提下,其请求被重定向到更“近”的“代理服务器”上。

普通时代

主要处理公开的协议,最明显的例子是 HTTP 下载,加速系统理解 HTTP 协议,当收到 GET 请求时,伪造 302 等跳转回应,使客户端改向加速系统提供的“代理服务器”下载。

当一个域名只用于 HTTP 服务时,还可以对这个域名做文章……DNS 协议也是公开的。

文艺时代

各种下载器、视频应用开始采用私有协议……所以,逆向就必不可少了,这个时代加速服务提供商必须有一定开发实力,不能再用一些开源代码整合就了事。

二逼时代

某些下载器的公司出于各种原因不希望别人假扮自己的“服务器”,于是做出了艰难的决定,下载协议使用非对称算法加密,这样一来,即使那些逆向高手把整个下载器客户端都逆向了,那也只是做出一个客户端,无法扮演服务端,于是乎……加速服务公司都 2B 了,要私钥吗?分点钱吧!