一、开始
不得不说,老汉是个不太安分的人,经常会做一些系统/平台不太推荐的事情。前一段时间在测试一个不使用 Symbian SDK 推荐的框架的最简化的 GUI 程序。在 EXE 里创建一个 RWindow 来显示内容并接收事件,我曾经在很久之前就写过,这次的需求略有不同,要能够在其中正常使用 CCoeControl 派生出来的控件(网上有一篇这方面的资料传播最为广泛:http://www.pushl.com/developers/exectrl.html。很不幸的是,其中有些只能用于 S60 第二版,例如涉及到的 CCoeAppUiSimple 这个类)。经过一些挫折后,总算是在模拟器里可以工作了,于是就想到真机上试试。
二、图标失效事件
要想让手机在应用程序中把程序显示出来,熟悉的开发人员都知道,需要有一个 xxx_reg.rss 资源文件。老汉在这里犯了一个很大的错误,过程是这样的。我的工程在最开始的时候,风格更接近于第二版的程序,几乎在所有的文件名种都没有 UID3 信息,中途为了更加符合三版的惯例,就修改了一次,把 UID3 加上了。最后把 SISX 部署到手机上之后发生了一个很奇怪的现象:在系统的九宫格里点击图标,不能启动程序,但是用调试器远程调试却又正常!这个问题困扰了我三四天,最终才发现竟然是忘记把 xxx_reg.rss 中 APP_REGISTRATION_INFO 结构里的 app_file 域中的可执行文件名相应地增加 UID3 而导致的。这个域很有意思,它必须与可执行文件的文件名匹配,但绝对不能写上扩展名!正是因为后面的这个限制,导致我在修改工程时误认为这个域仅仅是个描述性的文字,从而保留未动(没有附加上 UID3)!
三、显示名称事件
经过上面的修正,图标终于可以正常把我的程序启动起来了,不过另一件事情使得我的程序看起来很丑陋。图标下的文字是可执行文件名,而且毫不惊讶地,带着那个难看的 UID3!接下来的改进使我困惑:如果你想定制伴随图标显示的文字,正规的做法不是把这一信息也在 xxx_reg.rss 里指定,而是在 xxx.rss 资源文件里(一个 LOCALISABLE_APP_INFO 结构)!在 xxx_reg.rss 仅有一个链接性的信息,用 APP_REGISTRATION_INFO 结构中的两个域来描述,一个是 xxx.rss 编译后的资源文件名的 localisable_resource_file,一个是在该资源文件中包含了这一显示文字信息的结构的 ID 的 localisable_resource_id。难道我为了一个加起来不到 100 行代码的程序,竟然非要写两个 rss 文件出来?
经过仔细考察,我决定让 localisable_resource_file 指向 xxx_reg.rss 对应生成的资源文件自身。这下来了一个问题:正常的情况下,xxx.rss 在 xxx_reg.rss 之前被编译,会生成一个 rsg 文件,其中包含有 xxx.rss 中的所有资源结构所对应的 ID。这个 rsg 文件被 xxx_reg.rss 所引用,从而才可以给 localisable_resource_id 指定一个正确的值。但是现在不行了。如果我把 LOCALISABLE_APP_INFO 结构挪到 xxx_reg.rss 中来的话,那么我将无从知道资源编译器会把它的 ID 列为多少,也就无法给 localisable_resource_id 赋值。
当然,这样的困恼难不住老汉。我最终的做法是,先随便给 localisable_resource_id 写了一个数值,然后编译 xxx_reg.rss,然后找到它所生成的对应的 rsg 文件(也生成在 SDK 的 include 目录里),打开来看 LOCALISABLE_APP_INFO 的 ID 究竟成了几,然后把这个正确的数字更新到 localisable_resource_id 里,再次编译。这样就达到了一个资源文件即可满足系统所需信息的目的。
四、资源 ID 的戏法
在上面的过程中,有一件事情引起了我的注意。那些通常被我们定义时以 r_ 开头的名字命名的资源,与我们使用时所引用的对应的大写名称(其实是一个取值为数字的宏定义),究竟资源编译器是怎么生成对应关系的?
我对上面步骤所产生的 xxx_reg.rsg 文件进行了观察,看到 r_localisable_app_info(类型为 LOCALISABLE_APP_INFO)所对应的 ID(即 R_LOCALISABLE_APP_INFO)的值被定义为了 2。这不由得让我初步断定,这个值是根据 rss 文件中的 RESOURCE 定义顺序自增生成的,而且不管这个资源是否被命名了。在上例中,未命名的 APP_REGISTRATION_INFO 资源应该是占用了 1 这个 ID。带着这个结论去查看其他工程里的 rsg,得到了一个确认,一个疑惑。确认的是,ID 确实是按照前述顺序规则排列,疑惑是,这个顺序值仅占一个十六进制 DWORD 的后三位,前五位则各个 rsg 相异,不过在同一个 rsg 中总是相同的。这让我想起文档中提到的 rss 中 NAME 语句的定义不能冲突的问题,几乎可以认定这个前缀数值和 NAME 的定义值有关,但是一时找不到规律。
我写了一个批处理,来自动生成 NAME 名为 AAAA 到 AAAZ 的资源文件,并自动编译生成了对应的 rsg,可以看出前缀数值与名字一样,是一起增长的,然而准确规律依然未曾显现。不得已去 Symbian SDK 中搜索,找到了 ID 的顺序排列以及前缀数值由 NAME 变换而来的官方说法,不过并未提及变换算法。真正有价值的收获在于一个单词,offset,这是 SDK 文档中对我们前面的“前缀数值”的称谓。以此单词作为关键字到 google 上搜索,终于找到了答案,下面是经过我少些修改的变换算法代码,此工作至此告一段落。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
long IdFromName(const char* name) { /// REF: https://svn.symbianos.org/rcomp/trunk/src/rcomp.y // space 0 // A 1 // B 2 // ... // Z 26 // // ABCD corresponds to the number 4321 which becomes ( (4*27 + 3) * 27 + 2) * 27 + 1. int length = 0; if(name) length = strlen(name); if(length > 4 || length <= 0) return -1; long new_id = 0; for(long i=0; i<length; i++) { new_id *= 27; if(isalpha(name[i])) new_id += toupper(name[i]) - 'A' + 1; } long curr_id = new_id << 12; printf("Name=%s, Id offset=0x%08X(%d)\n", name, curr_id); return curr_id; } |
为了日后便于追索,把上面提到的批处理的内容也列出来(symres.bat):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
@echo off setlocal ENABLEDELAYEDEXPANSION if exist rcomp.exe goto begin echo rcomp.exe missed. goto quit :begin set uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ set lowercase=abcdefghijklmnopqrstuvwxyz set prefixu=AAB set prefixl=aaB set counter=0 :combine set suffixu=%uppercase:~0,1% set suffixl=%lowercase:~0,1% if "%suffixu%"=="" goto quit echo %suffixu% rem pause set uppercase=%uppercase:~1% set lowercase=%lowercase:~1% rem Do our real work here set stringu=%prefixu%%suffixu% set stringl=%prefixl%%suffixl% echo NAME %stringu% > r.rss echo STRUCT TBUF >> r.rss echo { >> r.rss echo LTEXT buf; >> r.rss echo } >> r.rss rem echo . >> r.rss echo RESOURCE TBUF %stringl% >> r.rss echo { >> r.rss echo buf = ""; >> r.rss echo } >> r.rss rcomp.exe -hr.rsg -sr.rss type r.rsg >> list.rsg if not "%suffixu%"=="Z" goto combine :quit if exist r.rss del r.rss if exist r.rsg del r.rsg |