August 29th, 2010我的黑苹果

我的这个yy半年之久的黑苹果计划终于在这个月初实现了.
Read the rest of this entry »

April 14th, 2010猥琐病毒的解决方案

这两天朋友电脑出现了一奇怪了症状: C盘空间貌似在不断减少, 疑似U盘有毒, 于是拿来看了下.

然后就发现了这个猥琐的病毒, 它的工作原理貌似很简单: 把自身的图标设置成与文件夹图标一模一样, 所以如果你没注意你是看不出来它是程序还是一个文件夹的, 另外它把U盘根目录下所有的文件夹隐藏(设置系统+隐藏属性, 所以即使你打开显示隐藏的文件与目录也看不到这些被隐藏的文件夹), 同时它创建一个与该文件夹同名的.exe文件.  而当毫不知情的用户点击运行这个病毒的时候它会打开与之对应的文件夹, 同时创建一个名为update.exe进程. 然后便向用户主目录下appdata(Win7下)的某个目录里狂写东西. 至于其他的坏事暂时还没发现.

鉴于这个病毒相对简单, 所以自己写了一个批处理来干掉它, 方法也很简单就是枚举磁盘根目录下所有的文件夹, 看是不是有同名的.exe存在, 如果有则删除并去除此文件夹的系统和隐藏属性. (在Win7下试用通过)

@echo off
setlocal 

:BEGIN
set ROOT=
set /P ROOT=输入要清除的盘符(输入0退出): %=%
if %ROOT%==0 goto END

echo 正在清除驱动器 %ROOT%:
rem kill the update.exe process
taskkill /IM update.exe 2>NUL
for /F "usebackq delims==" %%i in (`dir /AD /B %ROOT%:`) do if exist "%ROOT%:\%%i.exe" if not exist "%ROOT%:\%%i.exe\NUL" (
echo 删除 %ROOT%:\%%i.exe 并去除目录%ROOT%:\%%i的系统与隐藏属性
del "%ROOT%:\%%i.exe"
attrib -H -S "%ROOT%:\%%i"
)

goto BEGIN

:END

November 24th, 2009progress on login

这里是指在linux下, 在我们的环境中由于在登录系统的时候需要比较久的时候(5-15秒), 在那里看着不动的屏幕总归会有些不爽, 于是便写了一个小小的进度条来.
首先呢, 我们先完成一个画一个转圈的直线的程序来(其实就是轮流打印”-\|-/”这几个字符, 它每转完一圈后会打印一个加号.) — 当然每次打印要有一个间隔, 不然会很快, 这个时间间隔我们能过程序参数来传递.

#! /bin/bash
count=0
#c is the array for the characters we'll print
c[0]='-\b'
c[1]='\\\b'
c[2]='|\b'
c[3]='/\b'
c[4]='-\b'
c[5]='+'

while true
do
     let "count = $count %6"
     echo -n -e "${c[$count]}"
     sleep $1  #sleep sometime to continue....
     let "count = $count + 1"
done

在上面我们使用了echo 的两个参数, -n 是让它不打印一个多余的换行, -e让它解释转义字符, 而\b则是退格符.
好的, 下面就保存这个脚本并让它可执行(chmod +x ), 这里我把它命名为~/bin/timedot, 能过执行 ./timedot 0.2 来看一下效果吧. (请确定你的sleep程序可以接受小数形式的参数).

嗯, 看上去不错, 好的, 让我们把它加入到启动脚本里, 基本思路是把它作为一个后台程序运行.

~/bin/timedot 0.2&
timedotPID=$! #$! 是bash的内置变量, 它保存最后一个后台进行的pid.
...... #把你的很慢的代码放在这里.

kill $timedotPID  #杀掉打印进度条的进程.

好了, 试一下, 嗯, 似乎工作的还不错. 不过似乎有一个问题: 在每次启动的时候你都会看到一个类似下面的消息:

[1234]-  Terminated                 ~/bin/timedot 0.2&

之所以会有这行消息是因为你在后台运行一个程序的时候, bash会关注所有属于它的后台进程的状态, 并在后台进程结束后通知前台程序某个程序已经死掉啦(或者是已经完成了), 不过毫无疑问我们不希望看到关于timedot的通知消息(太丑陋了), 于是我们可以通过disown 命令来让bash放弃对timedot的管理.

好, 在这里似乎一切都很完美了, 不过假如你在一次登录的时候, 实在忍受不了长时间的等待了, 于是你按下了ctrl+c来中止当前的初始化脚本, 然后你就会发现了另外一个问题… 你会发现在你打字的时候总是会有”|/|-+”这几个字符蹦出来 —- 而这是因为你的ctrl+c虽然中止了你的初始化脚本, 但它却没有中此我们的这个后台进行的进度条进程. 好吧, 让我们修好它:

~/bin/timedot 0.2&
timedotPID=$! #$! 是bash的内置变量, 它保存最后一个后台进行的pid.
my_control_c()
{
     kill $timedotPID #杀死后台进程<br />
     trap SIGINT  #重置SIGINT,
}
trap my_control_c SIGINT   #我们设置一个control+c的钩子, 当用户按下control+c的时候会调用我们的my_control_c函数
...... #把你的很慢的代码放在这里.

kill $timedotPID  #杀掉打印进度条的进程.
trap SIGINT #重置SIGINT为默认值.

这里使用了trap命令来设置ctrl+c的钩子, 这样每次要中止初始化脚本的时候就会调用我们自己的函数, 而它会杀掉后台进度条进程, 当然不要忘记重置钩子, 我们不需要再以后用到它了.

刚搞了个黑莓玩, 在写短信时发现缺少了一个在用wm时常用的功能: 往短信中插入联系人. 这个有啥用呢, 想一想当有朋友问题你要另外一个人的号码时, 你就会知道它的用处了, 简单的通过一个菜单就可以插入联系人的手机号, 邮箱…… 而在黑莓里是没有这个功能的, 只能先切换到桌面然后打开联系人界面然后再找到你要的联系人, 然后再把电话复制出来, 再然后就是切换到短信程序并粘贴电话号码, 这显然是一个比较烦琐的步骤. 再假如你想要同时插入邮件/手机号, 则还要来回复制两遍. 所以我们就会看到一些常用的扩展来辅助插入手机号. 于是闲来无事自己研究了一下具体实现 (已经看到了一些具体的实现, 只是好奇, 所以研究了一下)

让我们分析一下这个问题, 要实现这个功能, 应该可以把功能划分为以下三个步骤:

  1. 往编辑短信的菜单中添加一个菜单项
  2. 当用户点击这个菜单的时候显示一个选择联系人的界面
  3. 当用户选择联系人之后把联系人的电话插入到短信中去.

下面我们分别看这三个步骤的实现, 步骤一很简单, BB提供了相关的api, 在它的文档中就可以找到, 具体请看这里. 首先我们需要实现一个派生自MenuItem类,

class InsertContactMenuItem extends ApplicationMenuItem {
   InsertContactMenuItem() {
                     super(2000000);  // This sets the item just before the "Add To:" menuitem          }

   public String toString() {             return "Insert Contact";
   }

  public Object run(Object context) {              ....

  }

上面就是一个简单的程序菜单项, 它在初始化的时候通过调用父类的构造函数来调整它在菜单项中的顺序(这个没有试过, 具体不知道工作不工作…), toString方法返回的字符串决定了它显示在菜单中的文字, 至于run方法则会在点击菜单项时会调用到, 它接受一个context参数, 系统会传入一个上下文相关的对像.

有了Menu类之后就可以把它注册到短信编辑界面里去了:

   private static void registerMenuItem() {
        System.out.println("*** Registering Insert contact menuitem");
        InsertContactMenuItem menuItem = new InsertContactMenuItem();
        ApplicationMenuItemRepository amir = ApplicationMenuItemRepository.getInstance();
        amir.addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_EDIT, menuItem);
        }

上面的逻辑很简单, 首先取得系统ApplicationMenuItemRepository的实例, 并向注册InsertContactMenuItem到SMS_EDIT中去, 好了, 到了这里, 第一步基本完成了. 完成这一步, 你就可以在模拟器里看到相应的菜单项了, 如下图:

menu

下面开始第二步, 这里需要在用户点击菜单之后弹出一个选择联系人的界面, 这个可以通过BlackBerryContactList.choose()方法实现:

  PIM pim = PIM.getInstance();
  BlackBerryContactList list = (BlackBerryContactList) pim.openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY);
  PIMItem pimItem = list.choose ();
  String phoneNum = pimItem.getString  (Contact.TEL, 0);

这里首取得系统PIM的实例, 然后联系人列表并调用它的choose ()方法, 运行的结果应该如下图所示, 出现一个选择联系人的窗口:

choosecontact 到了这里, 最后一步就是要把联系人信息插入到短信输入窗口里了, 在这里我走了一段弯路:

在前面提到在系统调用run (Object context)方法时会传入一个上下文相关的对象, 在看4.5.0os的文档里提到这里会传入一个TextMessage对象, 具体参见这里, 当时我是想尝试直接通过修改这个对象来往消息里插入文本, 不过可惜的是行不能: 因为在这里传入的TextMessage对象是只读的, 尝试调用它的set方法会引发一个异常. 而且在老版本的系统里根本就没有传入一个对象(8800 4.2.0模拟器里显示传入的对象为空). 所以在经历了若干次失败后只好转求它法.

在阅读相关的代码的时候, 终于发现了一个UiApplication类, 通过它可以得到当前在最前面的屏幕, 并且我们可以遍历这个屏幕上的所有元素, 貌似有点希望! 让我们尝试遍历一当前屏幕并打印出这些元素, 看看它们有些什么!

  Screen screen = UiApplication.getUiApplication().getActiveScreen();
  for (int i = 0; i < screen.getFieldCount(); i++)
   {
        Field field = screen.getField(i);
        printFields (field);
   }
      void printFields (Field field)
         {
             try
             {
                    System.out.println("*** Field class: " + field.getClass().getName());

                         if (field instanceof  TextField) {
                             TextField textField = (TextField) field;
                             System.out.println("*** Found a text field");
                             System.out.println("*** text field label: "
                                     + textField.getLabel());

                                 System.out.println("*** Found text field");
                                 System.out.println ("** value: " + textField.getText ());
                         }
                         else if (field instanceof  Manager)
                         {
                             Manager mgrField = ( Manager) field;
                               System.out.println ("** Subfields..");
                                for (int i = 0; i < mgrField.getFieldCount(); i++) {
                                        Field subFields = mgrField.getField(i);
                                        printFields (subFields);
                        }
                    }
                }
                catch (Exception e)
                {
                    System.out.println ("printFields excetion:" + e.getMessage ());
                }
        }

对于这段代码, 前几行是得到当前活动的屏幕,  然后调用函数printFields函数, 在这个函数里我们基本是把一个field相关信息打印了出来: 它的标签的文字, 它的文字. 由于在screen里可以嵌套包含一些屏幕元素的管理对象, 所以我们需要一个递归来打印. 下面是得到的结果, 请注意使用蓝颜色标记的文字,

BB 8800 4.2.1.74的Debug输出

JVM: bklt @8044: timer

JVM: bklt @8044: idle 0

JVM: bklt @8044: setTimer 22

*** Field class: net.rim.device.api.ui.container.VerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.internal.phone.model.ReadOnlyPhoneNumberField

** Subfields..

*** Field class: net.rim.device.apps.internal.phone.model.SmartPhoneNumberLabelField

*** Field class: net.rim.device.api.ui.component.SeparatorField

*** Field class: net.rim.device.apps.internal.sms.ui.SMSEditField

*** Found a text field

*** text field label: null

*** Found Using field

** value: Afdsf

BB  9500 的Debug输出:

FRIDG: could not find background_popup__Landscape.png

*** Field class: net.rim.device.api.ui.container.VerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.api.ui.LeftRightFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.container.HorizontalFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.component.LabelField

*** Field class: net.rim.device.apps.internal.sms.ui.MessageComposeComboField

** Subfields..

*** Field class: net.rim.device.apps.api.addressbook.AddressBookComboField$EmailComposeEditable

*** Found a text field

*** text field label: To:

*** Found text field

** value: 12313

*** Field class: net.rim.device.apps.api.ui.LeftRightFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.container.HorizontalFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.component.LabelField

*** Field class: net.rim.device.apps.internal.sms.ui.MessageComposeComboField

** Subfields..

*** Field class: net.rim.device.apps.api.addressbook.AddressBookComboField$EmailComposeEditable

*** Found a text field

*** text field label: To:

*** Found text field

** value:

*** Field class: net.rim.device.apps.internal.smscompose.message.SMSEditorScreen$BodyVerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.internal.smscompose.ui.SMSEditField

*** Found a text field

*** text field label: null

*** Found text field

** value: Wet

我们可以看到实际在这两个系统里的短信编辑输入框实际都是一个net.rim.device.apps.internal.smscompose.ui.SMSEditField 类, 看上去似乎我们可以通过判断一个Field对象是不是一个此类的实例来找到短信的实际输入框了, 十分遗憾的是请注意前面的那一连串类名里的一个.internal.: 对不起, 这是一个内部类, 外界开发人员是不能使用这个类的.不过我们还是可以利用这一点的, 在这里直接判断它的类名里是不是包含SMSEditField就可以达到预定效果.

      Screen screen = UiApplication.getUiApplication().getActiveScreen();
                    for (int i = 0; i < screen.getFieldCount(); i++)
                    {
                        Field field = screen.getField(i);
                        printFields (field, phoneNum);
                    }

      void printFields (Field field, String str)
         {
             try
             {
                           System.out.println("*** Field class: "
                                 + field.getClass().getName());

                         if (field instanceof  TextField) {
                             TextField textField = (TextField) field;
                             System.out.println("*** Found a text field");
                             System.out.println("*** text field label: "
                                     + textField.getLabel());

                                 System.out.println("*** Found text field");
                                 System.out.println ("** value: " + textField.getText ());
                                 if (textField.getClass ().getName ().endsWith(".SMSEditField")
                                 {
                                     System.out.println ("insertting text to...");
                                    textField.insert (str);
                                }

                         }
                         else if (field instanceof  Manager)
                         {
                             Manager mgrField = ( Manager) field;
                               System.out.println ("** Subfields..");
                                for (int i = 0; i < mgrField.getFieldCount(); i++) {
                                        Field subFields = mgrField.getField(i);
                                        printFields (subFields, str);
                        }
                    }
                }
                catch (Exception e)
                {
                    System.out.println ("printFields excetion:" + e.getMessage ());
                }
        }
        }

修改完代码后, 编译, 运行, 启动程序, 然后打开短信输入窗口, 按照前面的步骤选择联系人, 确认! 电话被成功的插入到短信编辑窗口里了!:)

resultsms

很好, 这里我们基本大功告成了! 不过其实还有很事情要做: 怎么把程序弄成开机启动的, 然后应该在选择联系人之后我们应该弹出一个对话框让用户选择需要插入的项(移动电话, 家庭电话, email 等). 不过做为一个自己瞎折腾型的项目, 到这里咱们就告一段落了:)

最后再告诉你一个不幸的消息, 即便你有了这个程序你也不能把它安装到你的手机里的, 这是因为黑莓开发的api中有一些是需要官方的验证的, 而这个验证在当前是需要钱滴(20刀). 不过即使没有申请官方的验证也是可以在模拟器里支持你的程序的, 这就是我写这篇文章的环境:)

具体的代码在这里:InsertContact.java

June 9th, 2009IE的怨念

– 因为写网页发现很多不兼容的郁闷的地方……

1. 数组声明

var a = {a: 1, b:2, c:3, }

注意最后一个”,”, 这样写在ie里是会报语法错误的……

2. 上面的例子, 如果没有初始化列表的最后一个逗号在ie中是可以的, 但是下面的呢?

var params = {class : “main”, a : 1};

在网页里写下这样的代码, 然后拿到ie里运行, 貌似没看出啥问题(我用的IE7), 可是你会发现你的代码为啥总没执行呢? 于是一行一行的删除代码, 最后n长时间过后总算发现是这行代码的问题, 可是为啥呢? 仔细看看, 貌似这里特别的地方就是在初始化列表里的class是一个关键字 — 是的, 就是它了, 对于有这种关键字的初始化列表是要加上双引号的, var paramrs = {“class” : “main”,  a : 1};, 这个问题很恼人 — 你支持{a : 1};为啥就不支持{class : 1};总感觉这里的实现像是半个残废.

3. css兼容性的问题, 太多了…… 导致看到好好的东西到了ie下就是不中啊, display:table-cell 在ie8之前的版本里不支持, 我哭.

4. 再另外像inject <tr> 到<table>里不工作, 必须得先把<tr> inject<tbody>里才行, 用的是mootools, 但估计也是IE的问题导致的, 反正是有了怨念, 就把这个也算到它的头上吧.

用javascript写程序, 想不到使用的感觉不错, 对js萌生出不少好感来, 有了好感后便是想着能在所有的地方用它, 于是便yy能在写本地程序的时候也用上js — 如果写本地程序可以用像写网页的方式写下来, 那该多完美哪 (当时yy的是一个程序可以以native/网页的形式运行的东西~~~). 记得当时找了一些, 貌似没有太多收获, 今天偶然又google了一把, 居然找到了一篇有趣的blog介绍gtk Seed, 果然有点意思.

Seed是个什么东西, 它的主页上有了介绍, 它是一个库, 解释器以及可以在运行时和webkit javascript核心引擎协作的东西.一句话它提供了你创建本地javascript程序的能力. 这个项目比较新的, 2008年11月8号才发布第一个版本. 这里有篇blog给了一些用它实现的小程序, 看上去还不错, 它封装的本地gtk api应该基本够用了, 没有仔细研究, 但至少文件/窗口消息啥的都有了. 另外貌似这里还用到了Gobject Introspection, 貌似很牛X的, 这个东西的目标之一应该是使用系统语言(像C之流)写的Gtk对象的窗口对象可以不经过任何额外的封装操作都都可以在其他语言中使用(像python/javascript) — 这样还是比较方便的. seed主页可以找到很多例子程序, 看了几个, 貌似很方便. 抽时间了搞下来折腾一下, 不知道有没有提供windows下的版本…

另外与Seed类似的还有另外一个项目:GJS, 这两个引擎的目标都很相似, 貌似都是为了提供本地化的开发能力, 区别就是Seed采用了webkit的js 核心, 而GJS则是使用的mozilla firefox的js引擎(Spidermonkey ). 使用spidermokey引擎的好处之一据说就是可以使用很多webkit中尚不支持的javascript5中引入的新语言特性. 不过看了一些文章, 似乎Seed和GJS的老大们还有过一些讨论目的是使得这两个引擎提供良好的兼容性, 其中Seed的老大似乎还同意修改Seed中Enum/import的实现以保持和GJS的一致.  不过相比较而言似乎Seed项目相对比较活跃一些, 更新比较频繁, 今年已经发而了五六个版本. 而GJS则相对比较沉闷了一些了, 似乎这半年没啥动静, 有点怀疑是不是要黄了……

另外既然讲到本地化的javascript开发, 就不得不提到adobe 的AIR了, 这个应该是和我原来yy的比较相似, 它支持html+js的本地程序, 看了一些文档, 提供了一些本地化的api(文件啊, 剪贴析啊之类的), 不过相对于前两者来讲应该是对于开发的限制应该多了不少. 但是这个酷在确实做到了web+native的双向执行啊, 另外就是它的开发工具相对全一些, 但是要收费滴.再另外就是Air的linux版本是incomplete的, 所以有些功能可能还是不支持地. 当然前面的两个有没有windows/mac的版本还不知道呢:)


© 2007 pangwa's Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress