应用程序自动升级解决方案 (.NET/非.NET都适用)

所以每次做了东西都会有话想说.

原来做 Pacgen (PAC file GENerator) 的时候, 为了实现自动升级, 专门写了个叫 Updative 的命令行工具 (当然还有个给程序用的 DLL), 会比对每一个文件的版本, 并且生成一个目录文件, 这样软件升级的时候就只需要下载更新的文件. 当时做得还是蛮有意思的. 可惜后来想做得更强大, 反倒把逻辑搞混了, 于是不了了之.

最近重写了 Pacgen, 并且因为不再使用 PAC 文件, 而是用 Privoxy 自带的 filter, 再叫 PACgen 也不大说得过去, 便改名 X-Wall, 也更直观. (欲知详情, 请访问 http://x-wall.org/)

所以怎么解决这个自动升级的问题呢? 网上其实有不少现成的类库, 不过我是个怕麻烦的人, 所以懒得去看文档… 我是使用的 Inno Setup 打包的程序. 所以一开始只是给程序提供了简单的版本检查, 下载新的安装包, 并且执行. 之后由用户手动完成. 因为安装包会记住用户之前的设置, 所以实际上这个过程除了多点几下下一步, 也没什么太大的不好.

所以整个流程便是, 启动安装包, 退出程序, 安装后再启动.

但说来我也知道 Inno Setup 有些命令很久了, 就是没联系到一起. 不过还好在有生之年这一天终于是出现了. 改动几行代码, 下载之后启动安装包时加上 /silent 开关, 便大功告成啦.

这里需要 Inno Setup 的相关配置文件添加上安装完成后启动的选项, 并且不能设置为在 silent 模式下禁用.

虽然这样一来, 每次更新都会更新全部文件, 但对于小工具来说, 文件大小还是非常能接受的. 加上我是把东西都放在 Github 上 (详情可以查看我的前一篇文章, 让 Github 成为免费的软件发布/下载/更新的服务器), 速度也不错.

让 Github 成为免费的软件发布/下载/更新的服务器

这年头, 自己动手丰衣足食, 不过真的做出来个什么好家伙, 要想分享给别人, 也得在网上找个地儿. 之前一直是拿博客的空间来放东西, 不过老实说 FTP 还真不够方便, 而且可能很多人还没有这么个空间, 囧.

后来知道有 Github 这个东西, 再后来发现 Github 有 raw (原始文件) 这个东西, 再后来发现 Github 还有 Github Pages 这个东西. 但 Github Pages 则是后话了.

raw 的路径是固定的, 并且非常规范, 同时有 SSL 加密. 比如 X-Wall 的安装文件我提供的下载地址就是 https://raw.github.com/vilic/x-wall/master/release/x-wall-setup.exe, 从前至后依次是 https://raw.github.com/用户/仓库/分支/目录. 人可以通过浏览器下载, 软件当然也可以通过 GET 拿到, 于是这里的下载和更新就解决啦.

顺便就提提 Visual Studio 提供的 ClickOnce 解决方案. ClickOnce 是个非常方便的东西, 特别是对于小工具来讲. 但也有很多局限性, 其中一些可以通过不那么优雅的方式解决 (比如自动启动, 因为如果直接把 exe 添加到启动目录, 是没法读到正确的设置文件的, 还有 Command Line 传递参数等问题), 另一些则比较… 比如每次升级之后, exe 文件的目录也变了 (其实相当于是新下载到了另一个文件夹), 防火墙/托盘图标之类的设置也就没了. 最近做的 X-Wall 也是因为这点最后放弃了 ClickOnce.

对于 ClickOnce 来说, 设置自动更新都是傻瓜化的, 这个蛮好, 所以图方便的话, 还是不错的选择. 而这里要说的是, 我们也可以将 ClickOnce 的应用部署到 Github 上. 不过多年前当作者第一次尝试这么做的时候, 结局是悲惨的. 因为 ClickOnce 有签名, 所以要保证提交的文件和签名是一致的. Git 默认安装提交的时候貌似会把 Windows 风格的换行变成 Linux 风格, 这个就是问题的原因了. 重新运行安装程序 (也许可以直接通过命令改, 但作者是懒人), 选择 “Commit as-is” 的选项.

之后, 把 ClickOnce 的应用发布到本地文件系统上, 也就是你的 Git 仓库所在的文件夹. 其实我们可以新建一个分支来专门放这些东西, 这个还是用 Github Pages 的时候学到的. 这个时候, ClickOnce 的自动更新设置中, 把地址设置成本地发布的这个文件夹提交到 Github 之后 raw 对应的目录即可. 最后提交代码, 大功告成.

最后再提一下 Github Pages. 当然你可以用来做个人/组织/项目的页面, 或许也可以用来做软件的下载/升级服务器. 之前说的 raw 上用户可以下载 exe 等等 binary 的文件, 软件也可以检查并下载更新, 唯有个缺点就是 MIME 类型有时候不那么尽如人意. 比如 js 也好, xml 也好, 都是 plain/text. 但用 Github Pages 理论上来讲就可以避免这个情况了. 总体来说, 如果你不嫌麻烦那么一点点, 并且不需要 SSL 加密连接的话, 用 Github Pages 做这个事情或许会更好.

另外 X-Wall 的主页也是架设在 Github 上的, 欢迎访问! http://x-wall.org/

大学寝室等通用网络共享方式 (视情况需交换机/路由)

刚进大学的时候, 还可用使用Connectify, 不过随着这家伙的普及, 也很快被Dr.COM封杀了. 后来一直使用的是通过无线路由中转, 然后在其中一台电脑上架设代理来实现共享网络. 虽然前后遇到过一些升级, 但经过调整总算是还能正常使用, 下面就介绍下具体的方式和所需要的软件.

首先需要选定一台主机, 用来登录相关账号, 比如我们的是Dr.COM, 需要一款代理软件, 推荐Privoxy. 这里需要说明的是, 可能有些同学联网的客户端会进行代理软件检测, 或许Privoxy不能幸免, 但只要稍作修改即可. 绝大多数检测代理是通过关键字, 比如这里的Privoxy就是Dr.COM的一个关键字. 所以只要改掉这个关键字就好. 如果你懂得一点编程知识, 可以自己下载源码, 在源码中全局替换之后进行编译. 不过Privoxy编译Windows版本还比较麻烦, 所以不推荐这种方式. 这时需要一个可以编辑字节文件的软件, 比如010 Editor. 使用010 Editor, 查找并替换Privoxy主程序中所有的 “Privoxy” 字符串为另一个长度相等的字符串. 比如我改的是 “PcgPrvx”. 当然如果使用其他专门用于编辑这些资源的编辑器, 可以不用一定长度相等, 但这里为了保证没其他问题, 取相同长度.

改好Privoxy程序之后, 需要修改其配置文件, 一般为config.txt, 内容可以全部删除掉, 只写一行, 9257这里是代理端口:

listen-address :9257

当然如果你有耐心看都有哪些设置自然最好了. 更多信息官网上也有提供.

之后就是进行防火墙相关的设置了, 这个就不细说啦. 完成后, 这台电脑就成了一个代理服务器啦. 不过还需要别的电脑能连接到它, 才能真正用起来… 我们学校的情况是, 同一台交换机下的设备无法互相访问, 所以在完成上面的设置之后, 其他交换机上的同学已经可以通过我的IP和代理端口访问网络, 但同一台交换机下的同学, 比如寝室里的, 则不行. 这个时候就需要一台路由器或者自己的交换机啦. 话说买个无线的路由/交换机还是很方便的, 比插网线舒服多了… 将需要共享的设备连接到同一个路由或者交换机下, 然后则可以通过相应的IP和端口访问网络了. 至于两台设备是否可以正常互相访问, 可以互ping IP看看.

还有一个问题, 比如也是我们学校, 不允许使用自己的路由器, Dr.COM会检测, 如果发现则断开网络连接, 这个时候则必须使用交换机. 多数路由器都是可以当做交换机使用的, 具体设置请自行搜索. 🙂

最后再推荐ProxyCap作为共享网络的客户端, 可以把全部程序添加到代理中, 免得麻烦. 否则像QQ等, 还需要更改代理设置, 有些软件甚至根本不支持代理设置… 另外使用ProxyCap之后, 自己的科学上网软件也工作正常, 其实这个才是用它的最大动力啦…

谈前端编程中面向对象和基于事件编程的实践意义

说明下, 本文仅限于我个人的理解与感受, 如果有错误或者不好的地方, 欢迎指正.

可能对于多数前端工程师来说, 面向过程的编程模式依旧是够用的, 其实很多时候, 我自己写的代码也几乎都只是函数没有类. 但面向过程毕竟有它的局限性. 当完成一些庞大的东西, 或者逻辑复杂, 容易产生混乱的代码时, 面向过程的模式就不容易处理了.

当然, 其实说到底, 还是和代码书写者的状况有关系. 我相信, 一个足够优秀的前端工程师, 即使面向过程, 也能写出清晰, 明了, 正确的程序来. 或者说, 在他的这种面向过程的编程下, 已经埋入了很深的面向对象的思想. (另外, 一定要有new才是面向对象吗? 显然不是, 面向对象只是一种思想, 不拘泥于实现的方法.)

面向对象的编程, 很大的一个好处, 就是把程序员从混乱中解放出来, 让思路更清晰, 把注意力集中在一些需要攻克的技术细节, 而非整个系统复杂的内部交互. 当然, 由此带来的东西就更多了, 比如更高的编程效率, 更少的bug.

但, 如何设计一个程序面向对象的结构呢? 这个我想是面向对象的过程中最关键的一步了. 它直接决定了之后具体实现的难易程度, 这应该也是一个高水平的前端工程师需要具备的能力.

接下来举两个例子, 谈谈两种不同的面向对象编程.

1. Web 游戏

Web 游戏的实现过程中, 处理一些具体算法方面的问题, 整体控制也是个问题. 比如说一个简单的操作, 可能会引起复杂的反应, 这些反应可能是各个方面的, 如画面呈现, 内部变化等等. 当然用面向过程的模式也能解决, 但普通的面向过程或许不那么容易简单地解决. 或者即使解决了, 代码的可读性, 或可维护性或许会不那么好.

这个时候, 用几个对象来分别控制游戏的不同部分, 或者会比较容易实现. 比如, 可以分成score, game, operation, 分别表示游戏的得分, 主体和操作控制, 而game又可以继续细分成foreground, stage, background等等. 这样一来, 思路就顿时清晰了.

这里提到的面向对象, 有一个特点, 就是所有的对象都是单独的实例, 不过很多时候就是这样, 一个类出来就只用一次. 这个时候, 我通常这样写JS:

var object = new function () {
    this.someMethod = function () { };
} ();

2. 多级菜单

作为前端工程师, 这个东西应该多少有接触过. 这个例子有很明显的两个对象, 菜单(包括子菜单)和菜单项目. 用面向对象来实现, 我们就只用关心菜单和菜单项目之间的关系, 只要把这两者的关系思考清楚, 就能很容易地避免一些令人伤心的bug, 或者实现难度问题.

function Menu() {
    this.add = function (item) { };
}

function Item() {
    this.childMenu = null;
}

显然, 这里的面向对象, 与之前提到的区别就在于, 我们会用到多个实例, 而且这些实例的数量很多时候是不确定的. 但是只要把握住它们的关系, 就能高效的完成代码, 并且规避一些面向过程容易遇到的bug. 同时, 这种模式可以配合事件编程, 效果更好.

上面提到了面向对象两种看似不同的实践, 但其本质是一样的, 只是一个注重整体的规划, 一个注重功能实现. 配合使用, 我想对整个实现来说, 会有很大的意义. 当然, 或许上面这两个例子还不足以让你感觉到面向对象的优越性, 可能是因为这两个例子还不够复杂, 也没有很大的实现难度. 毕竟合适的才是最好的, 根据需求选择适当的模式, 才是聪明人的选择.

当然, 在实际操作的过程中, 或许并不是那么容易把某些东西抽象出来, 或者可以抽象出来的东西很多, 取舍又是个问题. 这些就需要前端工程师的经验和思考了.

最后再提一下基于事件的编程.

拿一些UI特效为例吧, 比如某个窗口飞入飞出, 我们希望它在飞入的时候做什么, 飞出的时候做什么. 当然随便写写也好, 但可维护性和可扩展性或许就不那么好了. 特别是当这些窗口不止一个两个, 运动也相互独立的时候, 普通的写法就更吃力了. 而且最主要的是, 即使实现了, 也相当容易出bug. 不过最终, 面向对象是核心, 基于事件是翅膀, 没了对象, 事件又能有什么大作为呢?

希望这些想法能对不熟悉或者刚接触面向对象编程的同学有一点参考作用, 也欢迎交流和指正.

Let's "call" around!

记JavaScript的种种神奇.

自己博客更新频率越来越低了, 很大程度上, 是因为技术上的收获不再有那么多, 另外即使有, 也不是特别容易归纳或者表达亦或者常用的技巧, 所以也没必要写到博客上来. 不过, 还是想写一篇, 说说怎么玩JavaScript.

标题是 <Let’s “call” around!>, 所以什么是call? 既然玩的是JavaScript, call自然不能少. 当然, 本文包含但不仅限于call, call在这里代表的, 应该是一类东西.

那, 什么时候, 我们会使用call或者apply呢? 做个不完全归纳吧.

  • OOP.
  • 函数劫持.
  • this绑定.

OOP

其实这个应该算在this绑定里, 但是在OOP中, 又运用得特别多, 于是单做一类. 假设有一个class, MyClass. 那么创建一个实例 var mc = new MyClass(), 在某些时候, 可以做如下替换. var mc = {}; MyClass.call(mc); 当然, 他们并不是等价的, 而且差距还蛮大. 但有些时候, 从功能上讲, 是可以代替一部分的. 不过在这里, 由于建立对象和调用构造函数分成了两部分, 我们就可以在其中增加一些trick. 至于什么trick, 就不多做讨论了.

函数劫持

这个时候用apply可能偏多吧, 毕竟能传递几乎所有的参数, 也方便些. 如之前存在函数fn. 现在要劫持它, 那么就可以 var fnBackup = fn; fn = function () { fnBackup.apply(this, arguments); }; 好吧, 这个仍然牵涉到了this…

this绑定

于是, 就说说单纯的this绑定咯. 对于Web前端开发来说, 在事件处理上, 一定会很熟悉. 当然对于我, 就更熟悉啦, 嘎嘎嘎. 因为写到的很多东西里, 我都会通过this绑定一些方法什么的, 供函数使用, 比如VEJIS的类有这样的写法:

var C = static_class_(function () {
    this.public_({
        xxx: 123
    });
    this.private_({
        yyy: 456
    });
});

匿名函数配合this, 很好用!

OK, 说完了call和apply, 说说callee和caller. callee大家应该很熟悉, 不过caller就… 其实实话, 我也不是很熟, 但是觉得很有意思. 虽然现在还基本没用到过, 以后就说不定咯. 用法请arguments.callee.caller.caller.caller… 自己试试咯.

然后关于正则. 先说match. 在非global情况下, 我习惯这么用.

var result = string.match(regexp) || [];

一个JS里很常见的技巧不是么, 如果我想得到整个匹配的字符串, 那我会这么做. (string.match(regexp) || [”])[0]. 当然, 这个也很简单, 就看有没这个习惯了. 使用这种方式能省不少代码哦.

那说说exec. 这是我最近两年来比较喜欢的方法, 在global模式下非常管用. 我习惯的用法类似:

var groups;
while (groups = regexp.exec(string)) { }

关于instanceof. 这个是JS的一个好东西啊, 这个运算符给了JS这种弱类型语言处理强类型工作的能力. 既然说到它, 就不得不说说继承.

JS中如何实现继承? 其他的方法就不在赘述了, 推荐我比较喜欢的一种:

//更新, 之前的方法有问题, 今天看到TypeScript的做法之后借鉴下.
var Class = function () {};
function __() { this.constructor = Class; }
__.prototype = Base.prototype;
Class.prototype = new __();

var c = new Class();
var o = {};
Base.call(o);
copy(o, c); //这儿就伪代码了, 把o上面的东西拷贝到c上, 根据需要覆盖或不覆盖.

当然… 这样写有点长, 于是, 请自行封装… 使用VEJIS当然也是可以的, 简单多了:

var Class = class(function () { }).inherit_(Base);

这种写法的优势是什么呢? 相对单纯的prototype继承, 它能够更好地保证成员的独立性, 相对单纯的工厂模式, 它能保证instanceof为true. 代价稍大, 但可以忽略.

关于参数的合法性, 有时候我们的代码是给第三方用的, 同一个方法, 或许我们在内部调用的时候, 需要使用某个参数, 但却不希望第三方使用这个参数, 这个时候怎么办? 有人会说, 约定好就好了. 但显然约定只是自我安慰. 我是一个不喜欢暴露变量的人, 不该暴露的东西绝不暴露, 不该给别人用的东西绝对不能给别人用. 于是推荐的方式是, 在自己可控的闭包内, 创建一个类, 如下:

function Value(value) {
    this.value = value;
}

然后再验证参数的时候, 使用instanceof就可以了. 因为Value这个类在闭包内, 只要不传出去 (当然, 也不能把它的实例传出去), 就是安全的.

逻辑运算符的妙用. 这个场景不多见, 也只能是抛砖引玉. 场景是这样的, 通过XHR请求到一些脚本, 脚本内容是调用了一个函数, 现在我需要通过eval来得到这个函数的返回值. 特殊之处在于, 如果前面有注释怎么办? 用正则清除掉? 可行, 但是不大好吧… 于是前后加括号? 万一结尾有分号呢? 于是, 最后我采取的方法如下: var value = global.eval(‘(function () { return false || ‘ + script + ‘ })()’);

困了… 睡觉去.

JavaScript 推荐书写规范

自己写JavaScript也有一些年头了, 前些时候看Douglas的JSLint和他推荐的写法, 深有感触, 也见过不少JSer使用了他的规范, 但, 很多地方, 我却不能认同. 当然, 写法什么的, 最终还是看个人, 哪种写法效率更高, 就用哪种. 下面我就对比Douglas的规范, 说说我推荐的写法.

  1. JavaScript文件及引用
    根据需要将JavaScript代码放入单独的*.js文件中, 这些需要一般可能是指: 代码量较大, 代码被多个页面重用, 为了方便维护等. <script>标签因尽量放在<head>中, 并且标注type=”text/javascript”. 对于普通网页, 放在<body>结束之后, 有需要时甚至是<body>之中也是可以接受的. 但对于JavaScript密集的网页及WebApp, 应尽量避免.
  2. 缩进
    空格和Tab都可以接受, 但最好不要混用.
  3. 换行
    代码书写时要尽量保证其可读性, 自然每一行不应该过于长, 但不推荐无语义的换行. 换行应该能表达一个逻辑片段, 分组等意义.
  4. 注释及空行
    根据代码的需要进行注释, 在一个代码片段开始的前一行使用 // 注释该片段的作用, 对于难以理解的单行代码, 使用 // 注释在该行代码之后. 对于一个完整的, 或者相对重要的功能块, 使用 /* */ 在其上方第二行进行注释, 上方第一行留空. 如果结尾不明显, 容易与之后的代码混淆, 也可以在该功能块结束后再使用 /* */ 进行注释. 对于容易理解的代码, 也应该根据功能或者逻辑进行分组, 中间用空行隔开.
  5. 变量的声明和定义
    声明所有变量, 不过类似于 global.abc = 123; (浏览器中为window)这样的语句也是可以接受的. 随时声明变量能够帮助避免很多作用域的问题, 反之, 类似的问题可能会成为极大的困扰. 从个人来讲, 即使你非常清楚变量的作用域, 也应声明变量, 或者使用类似于global.abc = 123; 这样的形式定义全局变量. 当然, 语义化是永远需要注意的, 这点也适用于下面的函数声明.
    根据逻辑的进行声明变量, 有些变量, 比如在一个闭包中会被多个逻辑使用, 或者从意义上应该是独立与其他逻辑的, 应该放在闭包的最上方, 而部分仅仅会在某个逻辑中使用的变量, 则应该在该逻辑开始, 且该变量使用前声明. 如果该逻辑需要的变量较多, 容易与其他变量冲突, 可以考虑将该逻辑放入一个闭包中. 需要注意的是, 有些语句或许并不适合包含声明变量的代码, 比如if.
    另外, 不推荐使用一个var语句声明大量变量, 将变量分组, 并按意义上的先后顺序进行排序或许更好. 如:

    var id;
    var width, height;
    var left, top;

    当需要在声明的同时定义变量时, 通常我的做法是一个var语句只对应一个变量. 同时, 避免使用不必要的全局变量, 但不要担心局部变量和全局变量或闭包中的变量重名, 如果我们已经很清楚各自作用域的范围, 为了避免重名而给变量名添加各种前后缀, 或者缩写变量名的做法, 只会使代码变得更难理解.

  6.  函数声明和定义
    一般使用function语句进行函数声明, 且声明应放在当前闭包的最下方. 一般情况下, 一个函数主要是处理一个逻辑中某一个从意义上简单, 但代码量较大或者重用率较高的功能. 如果将函数声明在前面, 反而会影响我们直观地获知逻辑结构, 让本末倒置. 另一方面, 如果函数名足够语义化, 对于多数功能, 我们甚至可以略过函数体, 直接通过函数名来完成逻辑的阅读.
    对于声明和定义, 推荐使用这样的格式, 注意哪些地方有空格:

    /* 普通声明 */
    function abc() {
        //代码
    }
    /* 匿名函数赋值 */
    var fn1 = function () { return true; }; //单个语句可以写成一行
    var fn2 = function () {
        if (true)
            return true;
        else
            return false;
    };

    另外, 只有一些非常通用的函数适合作为全局函数(除非当前项目的JavaScript代码量非常少). 其他时候, 需要根据逻辑和意义来确定函数声明的位置. 闭包是JavaScript最美好的东西之一, 一定要善于利用. 它能够让代码具有更明显的结构. 有时需要建立一个立即执行的函数来实现一个闭包, 推荐这样的写法:

    (function () {
        //代码
    })();

  7. 命名
    尽量使用字母(A~Z, a~z)数字(0~9)和下划线(_)命名变量. 一般情况使用类似于myFirstName这样的驼峰状命名规则, 但如果变量的值代表一个类, 则首字母应该大写, 如: MyClass.  一些由于实现必须要求出现在逻辑结构之外的变量, 建议在其前后加双下划线标注. 如:

    var __inc__ = 0;
    function inc() { __inc__++; }

    但多数情况下(当结构相对简单时), 可以使用闭包来避免.
    对于一些有特别意义的全局变量, 比如当一个全局变量能决定是否执行某一个功能块, 以及部分常量, 推荐使用全大写命名, 单词之间使用下划线隔开, 如OPEN_DEBUG.
    另外, 尽量使用英文命名变量, 即使你的英文不够好. 退一万步, 如果使用拼音, 不要过度缩写. 顺便中英文混合虽然可以让人看到很 “快乐”, 但还是不要这样的好 .

  8. 语句
    每一行最多只包含一个语句, 需要分号的地方一定要加上分号. 包括这样的 语句: var fn = function () {}; 因为这实际上是一个赋值语句.
    对于控制结构, 如果只有一条语句, 可以将大括号去掉.
    另外, return是一个特定的语句, 并不是一个函数, 所以一般情况下, 不要在return之后使用(). 当然, 有时你可能会需要()帮你排除换行的歧义. 类似的还有typeof.
    continue有时会很有用, 所以完全没有必要回避. 至于with, 我很少用.
  9. 空格
    合理使用空格能够提高代码的可读性, 具体需要使用的情况如下:
    控制结构中的标示与()之间, ()与后面的语句或者复合语句之间. 如: while (true) {}.
    多数运算符左右应添加空格, 如 var a = 1 + 2; var b = true ? 1 : 0;
    逗号(,), 分号(;)后如果不换行则需要添加一个空格.
  10. 其他建议
    使用 {} 代替 new Object(), [] 代替 new Array();
    在创建一个类型的实例时, 不管构造函数的实参个数是否为0, 都应该加上(), 如new Object()不应写为new Object.
    如果你已经足够熟悉JavaScript了, 那么使用这样的语句也未尝不可:

    var a = true;
    if (a = !a)
        alert(‘hello’);

    同时, ==和!=在多数情况下都是适用的, 所以没必要处处都使用===和!==.
    除此之外, 我觉得一个漂亮的JavaScript代码一定是充分并正确利用了JavaScript语言特性的, 至于这些特性, 就很难一一描述了. 不过, 如果你真的喜欢JavaScript, 请去感受它.

所以只是个人建议而已, 具体怎么落实, 酌情.

ifttt, 这算一个测试吧

ifttt (www.ifttt.com) 刚出来没几天, 但貌似已经非常火爆了, 于是乘此机会, 也向好人要了个邀请码来注册. 那这篇文章算是ifttt的一个最简化简介, 也是我的第一个测试啦.

ifttt是if this then that的缩写, 也就是如果发生了这件事, 那么就做那件事. 它支持很多社交网络或工具, 比如我的WordPress, 刚刚我用它添加了一个任务, 就是如果我的WordPress发了新文章, 那么就在Facebook上添加一个链接. 所以我就发文章来啦.

ifttt最多可以同时开启10个任务, 不过我觉得还是蛮够用了. 嘎嘎.

顺便小广告, 欢迎follow我的twitter, http://twitter.com/vilicvane, 还有就是在Facebook上加我为好友 i(at)vilic.info.

IE 搜索引擎被劫持

今天突然发现IE的搜索引擎又被劫持到了百度联盟… 然后很不爽地发现, 那串url非常眼熟, 又是用utf8xxx的形式, 想把劫持者的联盟账号伪装成编码, 以骗过广大人民… 因为之前遇到过, 就很自然地联想到了Qvod Player… 大家懂的. 这款软件通过添加一款IE插件来达到劫持的目的, 所以只要禁用相关插件就可以了. 之前一次, 好像叫Qvod Extend, 不过这次更猛, 直接挂上了Baidu的名号… 弄得我找了半天才找到.
插件名称是{xxxx-xxx-xxx…}这种形式的, 大家找找看, 禁用掉应该就可以了~

Android不恢复出厂设置删除Google Account

今天遇到这个问题, 很恼火, 于是拿着ES文件浏览器开着root权限到处找, 终于找到了可疑目标: /data/system/accounts.db 果断删除之(需要先挂载为可写). 然后重新启动, 搞定.
注意重启后要先添加一个Google Account哦, 在Settings > Accounts & sync里.

Gravity更换IMEI之邮件样板

Gravity是S60的手机上使用Twitter的客户端, 不过因为Gravity的正版验证方式是和手机的IMEI绑定, 所以当我们更换了手机, 就必须申请修改已经绑定的IMEI.

可能我人品比较好, 没有出现同志们所说的发去邮件后长时间没有回应的情况, 于是分享下. 我们需要发一封邮件给Jan Ole (janole@mobileways.de), 我的是这么写的, 大家可以参照下.

主题

IMEI changing for new cell

正文 

Hi Jan Ole,

I purchased a copy of Gravity for my 6120c. Now I’m using X6, can I change the IMEI to the new cell?

old IMEI xxxxxxxxxxxxxxx
new IMEI xxxxxxxxxxxxxxx
Thanks for your great works!