Windows服务与UI交互

前一段在用WAMP的时候,发现一个问题:PHP中的exec函数无法运行本地的GUI程序。而用另一个suite,XAMPP,就没有问题。

网上倒是能搜到不少人有同样的问题,但是大多数都驴头不对马嘴(有的说是safe mode,这个几百年前就从PHP里删除了;有的说是function被disable了,默认的disable明明就是空的)。稍微研究了一下终于搞了清楚,下面简单记录一下。

首先先说说为什么XAMPP能:XAMPP是用你当前的账户来直接运行Apache的。而WAMP则是通过服务。相信有人大概会想:既然账户不同,自然就无法交互?毕竟大多Service的默认账户是LocalSystem。但是把Service的Log On As改成当前用户,依然不行。

其实,一个服务如果想和UI交互,他首先必须是一个Interactive Service。默认的服务是noninteractive的,无法和用户交互。如果想将一个服务改成interactive Service,首先这个服务必须是Log On于LocalSystem的[*];然后,在Service管理中勾选“Allow service to interact with desktop”:

2016-08-03

[*]注:只有LocalSystem能interactive是微软官方的说法:

If the service type specifies SERVICE_INTERACTIVE_PROCESS, the service must run in the LocalSystem account.

来自这里(另参见这里)。不过根据另外一篇文章的说法,通过修改对应服务注册表项中“Type”键的数值从10(二进制,下同)到110,可以对Log on到任意User的Service启用interactive。

这个服务以后应该就是interactive的了。但是等等,到这步还没完:从win8起,默认所有的interactive service都被禁用了,必须要去注册表将以下键值:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows

里的 NoInteractiveServices

改成“0”才行。

在这之后,你还得手动去把相关的支持服务,“Interactive Service Detection”(又名UI0Detect)给打开(如果你忘了修改注册表项,会提示“Error 1: Incorrect Function”)。

到这里,你终于可以成功地启动一个interactive service了——不过我们想要的结果,通过服务打开一个GUI程序,还是无法简单地实现的。因为从Vista起,微软引入了所谓“Session 0 隔离”的设定。所有的服务,都会被运行在Session 0上;而第一个登录用户的所有程序以及UI交互,都在session 1里(第二个是2,以此类推)。而不同session之间有无法跨越的鸿沟。正确的方式应该是,在用户的session里同时保留一个隐藏的application,然后通过诸如pipe之类的方式和你的service通讯(详情可见这里或者上面MSDN的Interactive Services的文章)。也就是说,即使你用上述方法搞好了interactive service,开启某个GUI程序之后那个程序也是在session 0里,用户根本看不到。

当然,对于已有的老旧软件的service,微软保留了一定的legacy兼容。其方法是,建立一个虚拟的“interactive desktop”——实际上在session 0中——然后把他显示给用户,就像虚拟机一样。不过会有烦人的提示,用户体验当然不会好啦:

2016-08-03 (1)

(虚拟桌面里面就不截图了,不知道为啥我一进去整个电脑就死机完全无反应…可以参见这里

什么,你问我最后问题怎么解决的?显然是换用XAMPP啦!

其他参考文献:

  1. MSDN Blog: Troubleshooting Interactive Services Detection
  2. MSDN Blog: What is Interactive Services Detection and Why is it Blinking at Me?
  3. Stack Overflow: windows service (allow service to interact with desktop)
  4. White paper: Impact of Session 0 Isolation on Services and Drivers in Windows
  5. Session Isolation from Microsoft [PPT]
  6. Code project:  Subverting Vista UAC in Both 32 and 64 bit Architectures
  7. Channel9: Session 0 Changes and Vista Compatibility for Services running as Interactive with Desktop
  8. MSDN: Application Compatibility: Session 0 Isolation
  9. StackExchange: Why can’t Windows services have a GUI?

 

图像缩小之后文件体积增大之谜

昨天帮师姐处理数据时,发现一个很奇怪的现象:有一组长宽各6000 px、相互比较相似的图片在裁剪掉3/4面积的白边(还剩约3000×3000左右)、外加缩图到1000 px宽之后,对原始图和处理过的图分别打包,居然是前者(原图)的总体积比较小,而且小很多。

Laplacian_Gaussian_restored at t=1
当事图片范例(已经缩图)

 

稍微有点信息熵以及压缩原理概念的人应该已经看得出这件事儿的诡异之处,不过这里还是啰嗦几句。这整个过程有三个步骤:

  1. 裁剪白边
  2. 缩图
  3. 7z压缩

无损图像本身当然是尺寸越大体积越大,不过白边的信息量极低,约等于没有。所以,在7z压缩过程中,这部分“虚”的体积会被压缩掉。所以裁剪掉它并不会减少多少极限压缩后的体积是可以预料到的。事实上,将这一步骤去掉(即对仅裁剪的图像7z压缩和裁剪后又缩图图像7z压缩两者进行对比),可以看到这一步骤几乎没有任何影响。

7z的压缩原理(LZMA)基本综合了常见的无损压缩的方法,应该已经没有多少余量可言,可以认为基本已经达到或接近了原始数据的压缩极限值。另外,除了对单个图片本身的压缩以外,不同文件间的差别也会对predictor的效率造成影响。不过由于本次的图片在缩放前后都是同一组(仅缩放)图片,所以这上面的压缩率应该基本保持一致(具体来说,会保持一个很高的文件间压缩率,因为都是相似图像)。

那么问题显然就只能出在缩图上了。换句话说,缩图(而且是长度上3->1的缩小)反而增加了图像的熵。

我第一个想法是原图的TIFF其实是JPEG压缩。TIFF格式可选很多压缩算法,虽然一般用无损(或者无压缩),但是也支持有损的JPEG。如果我将一组事实上为JPEG的图片转换为无损格式(BMP/PNG)之后再压缩,其体积当然会增大,因为我把之前JPEG算法中无意引入的许多噪声也当成了信息本身来对待。但是感觉上,这个增加的噪声应该不足以抵消甚至超过1/3(长度上)的缩图才对。

事实上,在用imagemagick的identify utility查看图像的信息后,可以看到

D:\>magick identify -verbose abc.tiff
Image: abc.tiff
Format: TIFF (Tagged Image File Format)
Mime type: image/tiff
Class: DirectClass
Geometry: 6251×6196+0+0
Resolution: 1000×1000
Print size: 6.251×6.196
Units: PixelsPerInch
Type: Grayscale
Base type: TrueColor
Endianess: LSB
Colorspace: sRGB
Depth: 8-bit
Channel depth:
Gray: 8-bit
Channel statistics:
Pixels: 38731196
<略>
Compression: RLE

其使用的是RLE(游程编码,根据TIFF的标准应该具体是PackBits这个实现),也就是无损压缩。这也很容易证明:将原始图像直接转换成BMP后再压缩,和缩图后的BMP再压缩比,依然体积要领先。

这里插播一段:一般而言,对于压缩过的图像再压缩,基本不会再有什么改善,因为已经逼近极限。例如,对于图像,你先用PNG压缩之后再用7z,体积几乎不会有丝毫变化。而且,在常见的算法中PNG应该是体积最小的(除去新千年兴起的webP之类的新算法),所以对单张图片基本就直接转换成PNG就好。但是有个例外就是文件间压缩:在固实包足够大的前提下,多个相似图像压缩最好先保持图像是无压缩的raw的状态(BMP),然后直接上7z,这样一般效果会比先PNG再7z压缩好不少——因为PNG压缩后的“脱水”数据反而不利于文件间压缩的predictor工作。

这里拿一组eroge CG(同一个Event的16张差分)做了个实验:

格式 图像大小 相对BMP大小 7z极限压缩后大小 压缩率 相对BMP压缩包大小
BMP 44,237,664 100% 4,945,976 11% 100%
PNG 16,713,122 38% 15,193,005 90% 307%
TIFF (PackBits) 43,265,681 98% 5,203,355 12% 105%
TIFF (ZIP) 24,878,172 56% 15,940,445 64% 322%
TIFF (LZW) 36,758,162 83% 16,832,042 45% 340%

确实支持之前的说法。不过这次的图像有点古怪,BMP+打包的体积居然不是最小的,反而是TIFF (PackBits)+打包的体积最小,而且小得多:3.53MB vs 1.40MB。具体原因?留在最后揭秘。

回到正题,那么,为什么缩图之后图像的信息量反而增大呢?这得回到图像本身来。仔细观察图像:

QQ截图20160720224543
图像的一部分,1:1比例

你会发现,有很大的“像素”点。事实上,这个图像是从一张小图用硬边缘法(Nearest neighbour)放大至9倍(长度上)之后形成的。因此,其有效信息量其实远没有他的尺寸那么多。当你缩图的时候,反而会在粗大的“像素”点的交界处产生一些新的“平均”(各种意义上的)像素点出来。Pattern的复杂度增加,因此熵也就增加了。而且,当你的缩图算法越“精细”,这种副作用就越强烈。当然,如果完整地缩小回1/9,新增加的像素倒不是问题,因为反正“老的”也干掉了,总体熵并不会有太大变化;但是在缩小到非整数倍时,这一副作用会非常明显。

上述效应的影响其实还和这图的颜色有蛮大关系。本图颜色很单调,本质上是一灰度图(虽然是用的RGB格式),而且只有离散地少数几种颜色:

original
原始图片的直方图

然而在缩图之后,其调色板大小急剧上升:

new
Lanczos缩图后的直方图

这显然完全不利于压缩。StackOverflow有个类似的问题谈到了这一点。

应该也很容易看到,如果用简单的线性采样(也就是Nearest neighbour)来缩小的话,可以完美地规避这一副作用(仅对这一特例而言。对于正常图像来说用NN缩图只会产生锯齿成狗的结果)。

做了个简单的测试,同样是BMP的前提下,使用4种不同的缩图算法缩图至50%再打包的结果如下:

缩图算法* 压缩后大小
Nearest Neighbour 2.28MB
Lanczos 11.1MB
B-spline 10.3MB
Bilinear 4.09MB
Cubic 10.3MB

[*]:缩图使用xnview。对于某些算法的具体参数不详。

这也符合我们的猜测。

长话短说,当一张图片真实信息量较低(有大量重复pattern,颜色单一,etc.)时,缩图可能反而会由于像素间平均的缘故使图像信息量增加,进而增加脱水后的体积。

哦对了,还有上面的思考题呢。为什么TIFF (Packbits)的压缩包最新小?个人的猜想是这种硬放大外加颜色少的图像,可以说是游程编码最理想的情况之一了:试想当你的文本文件是aaaaabbbbbccccdddddeeeee…时,在游程编码下可以变成5a5b5c5d5e…甚至5(abcde..)!更完美的是,经过了这样游程编码的的每张图像之间的相似性(可预测性)完全没有被破坏!所以在接下来的7z压缩中依然可以火力全开。其结果就是压缩率非常高的压缩包。

补记

成文之后,才发现其实文中那个有古怪的图哪怕直接单张压缩成PNG都会出现原图体积小于缩图的问题……也就是说根本没必要引入关于7z的部分,增加复杂度。不过既然写都写了就不改了。既然可以直接用PNG,那就顺手生成了几个例子测试。

图像范例是一张一般的ACG图,1000px宽。

测试1(重复patteren):先将图像用NN放大800%倍,保存PNG(1)。然后用Lanczos缩图至70%,保存PNG(2)。

结果:1=4.16MB,2=10.35MB,符合猜想

测试2(颜色调色板增加):先将图像用PS的色调分离分离成为25种颜色,保存PNG(1)。然后用Lanczos缩图至70%,保存PNG(2)。

结果:1=979.06KB,2=1.14MB,符合猜想

Google地图中国版现状

不是什么新闻,纯粹没事儿干记录一下而已。另外本文只涉及Google地图自身,不涉及(墙内)如何访问Google地图。

众所周知由于种种原因Google Maps在中国是有偏移的,其直接结果就是地图和卫星图对不上,在两者之间切换时(或者看Hybrid时)非常难受。好在Google在退出中国之后,还保有一个谷歌地图(下简称Ditu,和Maps区分),没有偏移的问题。

错误正确

(上Maps,下Ditu)

Ditu现在能用的地址是:

http://www.google.cn/maps

注意几点:

功能上大致和Maps差不多。UI已经更新到最新版(这个最“新”版也有年头了),但是不支持Maps最新的Eerth View,而只有Satellite View。当然对于中国来说这两个没有实质区别(反正也没有有3D建模的城市),只是看国外的城市的时候会不太方便(小贴士:Eeath View下可以按着shift无极旋转/倾斜)。

但是仔细测试就会发现,不是所有功能都正常。首当其冲就是搜索功能,Google故意切断了搜索功能:搜索任意关键词并确认后会直接显示

服务器错误。请稍后重试。

但是看日志就会发现并没有任何错误,只是Google服务器端切断了。

但是搞笑的地方在于Google instant search依然工作正常,也就是说在你输入搜索关键词的同时会在下拉菜单显示匹配的结果,你可以点击里面的结果来定位:

QQ截图20160712222432

不过很显然就不能干类似“搜索附近所有饭店”这类搜索了。

导航功能似乎也正常:

北京市丰台区北京西站至北京首都国际机场 - Google地图 2016-07-12 22-27-04

具体数据是否正确我没仔细测试,本来个人用Maps/Ditu的主要的目的也就是看卫星图玩。

其他的功能,街景啊之类的也正常(Again,中国国内并没有街景),就不贴图了。甚至添加商家的功能点了都会出表格。

另外有个很奇怪的地方,在Firefox中定位(右侧加减号上面那个)功能正常,会正常请求授权然后就会正常定位;但是在Chrome里点了之后直接就提示

Google 地图无权使用您的位置信息。

根本没有索权的提示,不知道为啥(Maps里正常)。感觉只是技术问题。

哦对了,很显然和Google账号的关联是没有的,右上角没有你个人头像等,汉堡包菜单里相关的个人选项诸如“您的地点”自然也欠奉。

至于地图数据,应该也是和Maps同步的,随便看了几个地方都是2016年的。

我在这里访问用的就是Google山寨城的服务器IP(74段),不确定国内访问时是否一致(或是否能直接访问)。

PotPlayer播放Livestreamer奇怪的错误排查

由于Twitch.tv的flash player占用资源太多(大多数时候并不是一个问题,但是在同时玩大型游戏的时候会有明显的FPS下降),偶尔会用livestreamer来通过本地播放器播放。

结果前一段,用PotPlayer播放时总是会报错,无法正常播放:

QQ截图20160619202742

官网搜了下,是说PotPlayer应使用--player-continuous-http参数。试了下倒是好使了,但是我清晰地记得原来并不需要这个参数也能正常播放。

更奇怪的是,经过一番研究,我发现这个bug还和在哪里运行命令行有关系:在桌面运行livestreamer twitch.tv/esl_dota2 best就有问题,在D盘根目录下就没有;换到H盘的根目录,则又出现了bug。

在就要我即将去github报错之前,我突然灵机一动重置了一下PotPlayer的配置,果然立刻就恢复了正常。经过一个对ini文件简单的AB测试,很快就发现病根在“相似文件策略”这个选项上。默认是打开相似文件,不过我前一段为了播放整个文件夹方便改成了“同时打开所有文件”(这样就可以无脑点“下一个”换片,无论文件名是否相似)。结果大概是由于PotPlayer的处理不是那么强壮,在打开livestreamer提供的流媒体的时候,如果该文件夹内有别的视频文件(也就是为什么这个bug会和命令行路径有关),就会出现错误。

改回“同时打开相似文件”就立刻修复了。

换主板小记

闲着没事儿干写的流水账一篇。

从快3年前装机起一直服役的技嘉GA-B85M-D3H,因几个月前因失手将一杯水倒进机箱,无法完全正常工作。虽绝大部分功能在晾干后完好如初,但是每次关机后会自动重启,必须关掉电源上的硬开关才行。稍微在Amazon浏览了下价格后发现同款主板的价格居然不降反升,实在无法说服自己花更贵的价格买3年前的产品,这事儿一直也就搁置了下来。

不过每晚睡觉前要手动去后面关电源,确实不便,更烦的是半夜挂下载无法再指望靠软件自动关机。于是瞄上了缩水版的GA-B85M-DS3H-A。奇葩的是连这缩水版的价格,都高于我当年的原版。靠Camelcamelcamel上挂的提醒,可算逮到一次降价到50刀的机会买了回来。

在拖延症了2天之后终于开装。之前通过网上配置已经大概清楚缩水了哪些:

  • 少了一条PCI-Ex16插槽(x4速度)
  • 音频芯片微缩,由6个降级为3个音频接口,阉了S/PDIF输出(官网居然还把音频拿来当卖点也是不懂…)
  • 少了个TPM接口

总而言之都是一些用不到的功能。比较关心的SATA接口依然是SATA3*4+SATA2*2的配置,基本别的就无所谓了。

不过真的到货一对比才发现细节方面也缩了不少:首先长度缩短了一些,布局更紧凑了;这理论上其实算是个好事,因为这机箱在D3H最外面一侧对应的螺丝孔下面居然无洞装铜柱,导致整个主板右侧是悬空的,每次插上面的线都觉得很玄乎;不过事实上来说,因为过于紧凑,有一个SATA3扣直接被显卡压住,线的插头处稍微粗大一点就会顶到显卡风扇……还好我把2条线交换了一下之后正好碰不到。CPU左侧的散热器直接没了(虽然我感觉那块只有几个电容和方块状物,不知道哪里值得装散热器)。南桥的散热器上面少了个印有技嘉logo的盖子(果然低端太丢脸了不值得印Logo上去么)。其他用料也以肉眼可见的程度缩水。

安装过程总体没什么问题,如我所料唯一的难点是CPU风扇:上次涂硅脂的时候已经搞断了一个脚,没想到只有三个脚的情况下还是怎么塞都有一个进老不去,折腾了5分钟。硅脂偷懒没重涂(而且被我抹得风扇底面到处都是),后来测试下温度还可以接受。哦在拆主板后面板那个铝片的时候还把手给划了……一开始没发现险些血溅CPU。

顺便把上次挂掉的3T硬盘拆了,感觉空间还是略紧张于是换了个闲置的1T的当下载盘。

装完开机测试,系统自动装了网卡+声卡驱动,不过还是自己去官网手动下了声卡的驱动更新了下。关机测试,正常无问题。