生成工程
现在到了着手生成自己的工程的时候了。在这个工程中,有一个按钮上面写有“Hello”。我们可以像建立Starter程序那样生成这个工程,删去S tarter程序原有的代码和资源而用自己的新的代码和资源将之代替。你可以通过改变工程的设置使之自动生成。很多设置比较有趣,它们有直接的答案,况且在S tarter应用程序中已经设置好了。所以,这并不存在很多困难:
1. 运行Code Warrior集成开发环境;
2. 选择File | New;
3. 在Project选项卡中选择Palm OS 3.1;
4. 按下Set按钮选择目录;
5. 单击OK;
6. 给工程起名叫Hello;
注意:
你可以把有用的文献放在顶级目录或一个特殊的文献目录,以便自己和别人能很容易的找到。我在保留应用程序以备后用时,就经常放在一个叫i deas的目录中。当想添加这样的文件时,可选择“Project | Add Files”。注意应把文件类型设为All Files以便能看见文献文件。
7. 单击AppSource树使之打开;
8. 右击Starter.c并选Delete。这是原来Starter的代码,你应该用你自己的代码将之代替;
9. 单击AppResources树使之打开;
10. 右击Starter.rsrc并选Delete。这是包含原来Starter程序中有关窗体和控件的规划信息。你也应该用你自己的资源文件将之代替;
11. 进入Windows浏览器,找到新建的工程文件夹,它应该叫Hello。在这个文件夹里面,还有一个文件夹叫Src。打开此文件夹,里面是原来S tarter程序中的源文件,全选并删除。在下一部分中,你将为Hello程序建立新的源文件,它们就保存在这里。
生成资源
Palm OS资源在本质上和为Mac和Windows生成的资源是相同的,但更加简单。由于资源是来自于RezEdit的资源格式,所以它更接近Mac的资源。P alm OS程序的界面叫做窗体。为产生这个简单的“Hello”程序,你应该建立一个上面有一个按钮的单一窗体。
步骤如下:
1. 运行Palm上构造器,这是Metrowerks的资源编辑器。在Code Warrior中也会发现相同的菜单;
2. 选择File | New Project File。这样将产生一个空的资源文件。现在的工程属性取缺省就行了,在下面的章节会详细讲到它们还能做什么;
3. 改变Application Icon Name为工程名称“Hello”;
4. 在Resource Type and Name列表箱里选中Forms行;
5. 选择Edit New Form Resource或者按CTRL-K创建一个新的窗体。新窗体将出现在Forms行并被命名为“untitled”;
6. 单击name,出现编辑框后,取名字为“Hello”;
7. 双击窗体打开,在下面的章节中将详细的讲解这些属性的意义并论述它们如何使用;
8. 选择Window | Catalog或按CTRL-Y打开控件工具箱;
9. 拖动一个按钮至窗体的中心。现在按钮的属性将代替窗体的属性出现在窗体对话框的左边。这些属性缺省就可以了,以后将详细讨论它们。你如果十分渴望改变按钮的位置的话,你可以改变L eft Origin 和Top Origin属性;
10. 单击Label属性,将其名称由OK改为Hello;
11. 单击右侧的面板,重新得到窗体属性;
12. 选择Layout | Hide Object Ids。这将使你确实看到写有“Hello”的按钮。如果“Hello”看起来字体比较大,这是因为你遇到了我在安装部分讲到的古怪字体问题。为解决这个问题,关闭构造器,打开W indows文件夹中的Fonts文件夹,然后重新打开构造器。
13. 选择Fiel | Save保存工程资源。将其保存在Hello工程文件夹的Src子文件夹下面。并命名为hello.rsrc。
14. 当你完成你的新工程后,下一步该涉及到总工程。资源工程是总工程的一部分,总工程将包括所有有关用户界面的图形描述的各个方面,例如:大小、布局及窗体和控件的外观等;
15. 再次打开Code Warrior集成开发环境,选择File | Open Recent | hello.mcp;
16. 选择Project | Add Files。置文件类型为“All Files”以便能看见的资源文件。找到hello.rsrc打开;
17. 这时资源文件会出现在工程的顶部。为简洁起见,把它拖到应用程序(AppResource)资源组的下边。为此,你可以按住鼠标把文件图标拖到应用程序(A ppResource)资源组的下部,当出现在资源组下面出现一直线时,松开鼠标;
18. 到此为止,你已成功的创建和添加了新的资源。
生成代码
为使你的程序能够运行,必须添加C代码,步骤如下:
1. 建立可以写入代码的新文件,你可以选择File | New Text File创建之;
2. 选择File | Save As保存文件为hello.c;
3. 选择Project窗口,选中Project | Add Files。找到hello.c添加之;
4. 如果它不在AppSource文件夹,可把它拖到该文件夹中。当你拖动文件的时候,你会发现一条直线,它允许你把文件插入;将下面的代码输入到hello.c文件中。在下一部分将逐行进行详细的讲解。
代码分析
源代码由两个函数组成。它创建了一个窗体和可以退出应用程序的按钮。当然,在程序为解决实际问题而变的更复杂时,我们必须增加更深层的程序结构。
下面是代码的逐行解释。
深入(IN DEPTH)注意到Pilot.h包含在<>中而hello_res.h包含在引号中了吗?这就是说,编译器将在Palm OS所指定的系统文件夹中寻找Pilot.h,而对于hello_res.h,则先在Hello\Src中寻找,若找不到再到所有的文件夹中寻找。
当你创建hello.rsrc时,hello_res.h随之也建立了。hello_res.h中包含了窗体和控件的ID标识符常量。
注意:在运行之前,应把hello_res.h添加到你的工程中,它允许你方便的查看和编辑。添加文件时,首先选择Project | Add Files。找到hello_res.h将之拖到AppSource文件夹中。
以上就是处理窗体事件和控件事件的回调函数的原型,所谓回调函数就是在应用程序中的一个系统调用,它一般用来响应系统或用户自定义的事件,譬如你用输入笔点击窗体中的按钮所产生的事件。
函数PilotMain()
PilotMain()就是包含了被称为启动代码的函数。变量cmd 提供了激活应用程序的入口。接下来我将告诉你开始运行启动代码后,其它参数将起到什么样的作用。
如下定义了指向窗体的指针。
如下定义了指向当前事件的指针。EventType是一个数据结构,包含了当前事件的所有信息。
小技巧:
一般我们会先查看一下PilotMain()函数头部是否存在一些识别Palm OS版本的代码,以确保程序在发现操作系统存在任何不兼容的现象后,安全而体面的退出。为了达到这个简单的目的,我先把这部分代码分离出来如下。需要事先说明的是,现在提供的这个应用程序确实能够运行在任何P alm 设备上,因为它只用到了很少的系统资源,但是在运行其它复杂的程序时就存在一些不可预料的问题了。让我们从下面的代码看起。
通过函数Ftret()的调用可以得到ROM中Palm OS的版本。从而得知当前运行的Palm OS版本是否达到了你应用程序的最低运行配置。一般Palm OS 1.0版本的处理方法是不停的重新加载应用程序(如果上一次加载不成功的话),直到你主动切换到其它的应用程序。这样就有可能使你陷入一种近乎死循环的尴尬局面,所以,在这样的情况发生前你应该先调用A ppLaunchWithCommand()函数,使你得以从容跳出当前的应用。
下面是环境检查代码,你可以藉此查验当前的运行环境是否是一个正常的运行环境(normal cicumstances)。到下一章,我们还将看到其它的运行环境。
下面是一些关于Palm OS内嵌窗体管理器的函数调用。首先,调用FrmInitForm()函数,使我们的窗体进入备用状态。通过使用FrmSetEventHandler()函数为窗体绑定事件处理函数(e vent handling function)。然后,使用FrmSetActiveForm()函数来激活窗体。最后,调用FrmDrawForm()显示窗体。
下面是事件循环的开始部分。
深入: 什么是事件循环?事件循环是用来处理应用与用户,及应用之间交互的一种程序循环,譬如处理输入笔运动、按钮被按下等用户事件。许多现代图形用户界面(G UI),如Mac OS、Windows等都是事件驱动的操作系统。事件是一种操作系统和你的应用程序精确交互的手段,从而可以得知用户正在对应用程序做一些什么样的操作。一般,如P alm OS这样简单的操作系统,通常按发生的顺序来保存事件,用户可以请求事件和处理事件。
下面这个函数从ROM中调用事件处理器(Event Manager),并把老的事件放到队列中,以操作系统接收的先后顺序来处理事件。
下面的函数调用了系统管理器用来处理系统事件。
下面这个函数不但调用了回调函数处理事件,而且还执行了很多其它的任务。譬如,使用penUpEvent 和penDownEvent 两个原始事件来产生新的事件,这就像ctlSelectEvent事件,我们使用在窗体事件处理函数中去决定按钮是否被按下了。这就是我们使用系统回调函数处理事件而不是把所有事件都放到一个巨大的事件循环中去的原因。
一旦另一个应用程序被用户选择,就交由下面的代码来处理程序退出前的一切善后工作。 在这个例子程序中并没有什么需要清除的,所以我们就可以通过结束循环来退出执行中的应用程序。这些代码往往出现在循环的底部,因为我们在退出时总是需要往窗体事件处理器(或者其它的处理函数)中添点什么代码的,所以让这个退出事件接收循环贯穿整个程序结构是很有好处的。
helloHandleEvent() 函数
还记得吗?我们调用FrmSetEventHandler()函数的时候就是通过传递helloHandleEvent()函数的入口地址给Palm OS来实现的。这个函数通过调用FrmDispatchEvent()函数来处理窗体事件。
这个事件发生在窗体上的”Hello”按钮被按下后,在例子中,我同时让系统发出了警报音。
我的编程风格是通过超过500,000行以上代码,16年的程序生涯养成的。然而,看了我的程序并不意味着就学到了我的风格。我认为,不应该全部照搬书中的内容,而是要采用自己的方法来编程,并且不为尝试自己独特的编程风格而感到惭愧不安。用自己的方法写自己的样列程序,往往能使人更好的学习和理解那些内在的原理,所以我是非常赞同程序员编写自己风格的代码的。
调试:使它工作起来
在这一部分你就可以开始编译和链接这个项目工程了。首先选择运行Project | Make。如果你像我那样干的话,就可能会搞出一些BUG的(当然了并不是所有的方面都是完美的嘛)。祝你们好运吧。
我们已经取得不少的进步了。你现在可以通过Compare files操作比较快速的除掉一些编译时间错误了。如果你在创建工程的时候意外的删除了库文件,那么就会产生连接错误。你最好的处理办法就是尽早做好这些文件的备份工作。
发现运行时间错误的最好方法就是逐行的调试程序。你可以在Code Warrior IDE集成环境中执行下面的一些调试步骤:
1.在PC上选择Project | Debug 或者 点击F5来编译和连接工程,然后调出调试器(debugger)。
2.在Palm上,把设备先置位到Console模式。最简单的方法就是利用find框,删除find输入框中原有的文字并输入快捷符号。快捷符号很像连写的小写字母“l ”,你在输入区上画一个连写的“l”以产生快捷符号。然后写一个句号(在输入区轻击两下)和数字“2”。在你这样做后,你会发现Pa lm设备会发出声音并且所画的内容将消失。图2-2显示了把Palm设备设置为Console模式所要画的图形;
3.在PC上,点击OK用来下载应用程序到Palm设备。再点击OK,来去掉Palm上出现的Code Warrior lite警告框,现在你就应该停在Code Warrior 调试器中,PilotMain()函数的第一行了。
4.在PC上,点击调试器窗口顶端看起来象一个向右箭头的按钮。这将使你以单步调试的方式运行程序,进入函数后,箭头每次都将停留在下一步将要执行的语句上。点击另一个像向上箭头的按钮,将使你跳出任何一个执行中的函数。
5.在PC上,按下调试器窗口顶端的“X”按钮可以重置Palm设备。如果你的Palm设备已经关掉,那就再打开,然后按下“X”按钮,则设备将被置于一种良好的C o-nsole模式关闭的状态。当然,也可以通过使用图钉或其它尖的东西按下Palm背面的Reset按钮,来达到同样的目的。
值得庆幸的是,我早期的一些源代码评论将对你有用,特别是像你这样正努力推测程序中每条代码含义的时候。当然,如果你的窗体根本不显示的话,那么你就得首先确保你已经正确连接了当前资源文件(* .rsrc),以及通过在FrmDrawForm()函数入口设置断点和单步跟踪来确保它是否被调用了。
你也可以通过点击每行左边的页空白处来设置断点。如果设置了断点,一个小红点将出现在该行左边的页空白处。去掉断点只要点击该行左边页空白处显示的小红点就可以了。
当你的Hello示例程序被完全调试好之后,你将会发现:
1 在Palm的应用程序区将出现“Hello”的图标
2 一旦程序被加载,将显示它的窗体和“Hello”按钮
3 当“Hello”按钮被按下,程序将退出
4 当按下Palm上的4个真正的按钮或任何一个输入板(graffiti pad)边上的“Silkscreen bottom”,程序都将退出
祝贺你。你已经写完了你的第一个Palm OS 应用程序了。这是一个很好的程序,可以用来测试你当前使用的代码中是否存在一些不易发现的不安全的设置。
当你运行你的程序时发生了什么?
现在你已经拥有一个能工作的Palm OS程序了,让我们设置一个断点,再一次单步调试,以便于更加深入的了解它是如何工作的。
1.在PC上,开始运行调试器。选择Project | Debug 或 按下 F5 开始编译和连接工程以及弹出调试器。
2.在Palm上,设置你的设备到Console状态。使用find框,删除find输入框中原有的文字并输入快捷符号。快捷符号很像连写的小写字母“l ”,你在输入区上画一个连写的“l”以产生快捷符号。然后写一个句号(在输入区轻击两下)和数字“2”。在你这样做后,你会发现Pa lm设备会发出声音并且所画的内容将消失。
3.在PC上,单击OK后,下载应用程序到Palm。现在你需要在PilotMain()函数的第一行设置断点。
4.在PC上,从调试器窗口中找到,如下代码:
/* Parse the event */
if( event->eType == ctlSelectEvent )
SndPlaySystemSound( sndAlarm );
5.在PC上,在if语句前设置断点。
6.通过反复的按下执行一行代码的按钮,来单步逐行调试整个程序。注意到,FrmDispatchEvent()函数的调用经常停留在窗体事件处理器上(m yHandleEvent()),但由于发生的并不是ctlSelectEvent,所以SndPlaySystemSound()就没有运行。跳过SndPlaySystemSound()函数后,程序又进入了G etEvent()状态,等待其它事件的发生了。
7.点击Palm上的“Hello”按钮,注意到,GetEvent()函数响应了,窗体事件处理器被反复调用了许多次,这次终于发生了ctlSelectEve nt,于是SndPlaySystemSound()就被调用了。你现在已经亲眼目睹了你的代码分配和处理一些事件的全过程了。
8.在PC上,按下调试器窗口顶端的“X”按钮可以重置Palm设备。如果你的Palm设备已经关掉,那就再打开,然后按下“X”按钮,则设备将被置于一种良好的C o-nsole模式关闭的状态。当然,也可以通过使用图钉或其它尖的东西按下Palm背面的Reset按钮,来达到同样的目的。
把你的工程存放到固定的目录中
在后面的文章中,我们将使用做好的应用程序作为整个程序外观的基础。在Code Warrior中,提供了一种叫做“Stationary”的建立详细工程项目的模板,现在就请把当前项目做成一个模板。在你的 Code Warrior下的Stationary子目录中保存一个应用程序模板的拷贝,这样当你下一次选择New Project选项的时候它就会出现。你也可以按照以下步骤在Windows资源管理器中复制应用程序模板:
1.打开Windows资源管理器,找到你的Hello工程的目录。
2.选中整个Hello工程目录。
3.按下CTRL-C复制目录
4.在Code Warrior目录下找到Stationary子目录,进入。
5.按下CTRL-V复制Hello目录。
现在到了着手生成自己的工程的时候了。在这个工程中,有一个按钮上面写有“Hello”。我们可以像建立Starter程序那样生成这个工程,删去S tarter程序原有的代码和资源而用自己的新的代码和资源将之代替。你可以通过改变工程的设置使之自动生成。很多设置比较有趣,它们有直接的答案,况且在S tarter应用程序中已经设置好了。所以,这并不存在很多困难:
1. 运行Code Warrior集成开发环境;
2. 选择File | New;
3. 在Project选项卡中选择Palm OS 3.1;
4. 按下Set按钮选择目录;
5. 单击OK;
6. 给工程起名叫Hello;
注意:
你可以把有用的文献放在顶级目录或一个特殊的文献目录,以便自己和别人能很容易的找到。我在保留应用程序以备后用时,就经常放在一个叫i deas的目录中。当想添加这样的文件时,可选择“Project | Add Files”。注意应把文件类型设为All Files以便能看见文献文件。
7. 单击AppSource树使之打开;
8. 右击Starter.c并选Delete。这是原来Starter的代码,你应该用你自己的代码将之代替;
9. 单击AppResources树使之打开;
10. 右击Starter.rsrc并选Delete。这是包含原来Starter程序中有关窗体和控件的规划信息。你也应该用你自己的资源文件将之代替;
11. 进入Windows浏览器,找到新建的工程文件夹,它应该叫Hello。在这个文件夹里面,还有一个文件夹叫Src。打开此文件夹,里面是原来S tarter程序中的源文件,全选并删除。在下一部分中,你将为Hello程序建立新的源文件,它们就保存在这里。
生成资源
Palm OS资源在本质上和为Mac和Windows生成的资源是相同的,但更加简单。由于资源是来自于RezEdit的资源格式,所以它更接近Mac的资源。P alm OS程序的界面叫做窗体。为产生这个简单的“Hello”程序,你应该建立一个上面有一个按钮的单一窗体。
步骤如下:
1. 运行Palm上构造器,这是Metrowerks的资源编辑器。在Code Warrior中也会发现相同的菜单;
2. 选择File | New Project File。这样将产生一个空的资源文件。现在的工程属性取缺省就行了,在下面的章节会详细讲到它们还能做什么;
3. 改变Application Icon Name为工程名称“Hello”;
4. 在Resource Type and Name列表箱里选中Forms行;
5. 选择Edit New Form Resource或者按CTRL-K创建一个新的窗体。新窗体将出现在Forms行并被命名为“untitled”;
6. 单击name,出现编辑框后,取名字为“Hello”;
7. 双击窗体打开,在下面的章节中将详细的讲解这些属性的意义并论述它们如何使用;
8. 选择Window | Catalog或按CTRL-Y打开控件工具箱;
9. 拖动一个按钮至窗体的中心。现在按钮的属性将代替窗体的属性出现在窗体对话框的左边。这些属性缺省就可以了,以后将详细讨论它们。你如果十分渴望改变按钮的位置的话,你可以改变L eft Origin 和Top Origin属性;
10. 单击Label属性,将其名称由OK改为Hello;
11. 单击右侧的面板,重新得到窗体属性;
12. 选择Layout | Hide Object Ids。这将使你确实看到写有“Hello”的按钮。如果“Hello”看起来字体比较大,这是因为你遇到了我在安装部分讲到的古怪字体问题。为解决这个问题,关闭构造器,打开W indows文件夹中的Fonts文件夹,然后重新打开构造器。
13. 选择Fiel | Save保存工程资源。将其保存在Hello工程文件夹的Src子文件夹下面。并命名为hello.rsrc。
14. 当你完成你的新工程后,下一步该涉及到总工程。资源工程是总工程的一部分,总工程将包括所有有关用户界面的图形描述的各个方面,例如:大小、布局及窗体和控件的外观等;
15. 再次打开Code Warrior集成开发环境,选择File | Open Recent | hello.mcp;
16. 选择Project | Add Files。置文件类型为“All Files”以便能看见的资源文件。找到hello.rsrc打开;
17. 这时资源文件会出现在工程的顶部。为简洁起见,把它拖到应用程序(AppResource)资源组的下边。为此,你可以按住鼠标把文件图标拖到应用程序(A ppResource)资源组的下部,当出现在资源组下面出现一直线时,松开鼠标;
18. 到此为止,你已成功的创建和添加了新的资源。
生成代码
为使你的程序能够运行,必须添加C代码,步骤如下:
1. 建立可以写入代码的新文件,你可以选择File | New Text File创建之;
2. 选择File | Save As保存文件为hello.c;
3. 选择Project窗口,选中Project | Add Files。找到hello.c添加之;
4. 如果它不在AppSource文件夹,可把它拖到该文件夹中。当你拖动文件的时候,你会发现一条直线,它允许你把文件插入;将下面的代码输入到hello.c文件中。在下一部分将逐行进行详细的讲解。
| /* The super-include for Palm OS */ #include /* Our resource file */ #include "hello_res.h" /* A prototype for our form handler function */ static Boolean myHandleEvent( EventType* event ); /* The main entry point */ DWord PilotMain( Word cmd, Ptr, Word ) { FormPtr form; /* A pointer to our form structure */ EventType event; /* Our event structure */ /* If this is not a normal launch, don't launch */ if( cmd != sysAppLaunchCmdNormalLaunch ) return( 0 ); /* Initialize our form */ form = FrmInitForm( HelloForm ); FrmSetEventHandler( form, myHandleEvent ); FrmSetActiveForm( form ); FrmDrawForm( form ); /* Our event loop */ do { /* Get the next event */ EvtGetEvent( &event, -1 ); /* Handle system events */ if( SysHandleEvent( &event ) ) continue; /* Handle form events */ FrmDispatchEvent( &event ); /* If it's a stop event, exit */ } while( event.eType != appStopEvent ); /* We're done */ return( 0 ); } /* Our form handler function */ static Boolean myHandleEvent( EventType* event ) { /* Parse the event */ if( event->eType == ctlSelectEvent ) SndPlaySystemSound( sndAlarm ); /* We're done */ return( false ); } |
源代码由两个函数组成。它创建了一个窗体和可以退出应用程序的按钮。当然,在程序为解决实际问题而变的更复杂时,我们必须增加更深层的程序结构。
下面是代码的逐行解释。
| /* The super-include for Palm OS */ #include Pilot.h包括了所有Palm OS相关的头文件。 /* Our resource file */ #include "hello_res.h" |
深入(IN DEPTH)注意到Pilot.h包含在<>中而hello_res.h包含在引号中了吗?这就是说,编译器将在Palm OS所指定的系统文件夹中寻找Pilot.h,而对于hello_res.h,则先在Hello\Src中寻找,若找不到再到所有的文件夹中寻找。
当你创建hello.rsrc时,hello_res.h随之也建立了。hello_res.h中包含了窗体和控件的ID标识符常量。
注意:在运行之前,应把hello_res.h添加到你的工程中,它允许你方便的查看和编辑。添加文件时,首先选择Project | Add Files。找到hello_res.h将之拖到AppSource文件夹中。
| /* A prototype for our form handler function */ static Boolean myHandleEvent( EventType* event ); |
以上就是处理窗体事件和控件事件的回调函数的原型,所谓回调函数就是在应用程序中的一个系统调用,它一般用来响应系统或用户自定义的事件,譬如你用输入笔点击窗体中的按钮所产生的事件。
函数PilotMain()
| /* The main entry point */ DWord PilotMain( Word cmd, Ptr, Word ) { |
PilotMain()就是包含了被称为启动代码的函数。变量cmd 提供了激活应用程序的入口。接下来我将告诉你开始运行启动代码后,其它参数将起到什么样的作用。
如下定义了指向窗体的指针。
| FormPtr form; /* A pointer to our form structure */ |
如下定义了指向当前事件的指针。EventType是一个数据结构,包含了当前事件的所有信息。
| EventType event; /* Our event structure */ |
小技巧:
一般我们会先查看一下PilotMain()函数头部是否存在一些识别Palm OS版本的代码,以确保程序在发现操作系统存在任何不兼容的现象后,安全而体面的退出。为了达到这个简单的目的,我先把这部分代码分离出来如下。需要事先说明的是,现在提供的这个应用程序确实能够运行在任何P alm 设备上,因为它只用到了很少的系统资源,但是在运行其它复杂的程序时就存在一些不可预料的问题了。让我们从下面的代码看起。
| Dword ROMVersion; //Get the ROM version ROMVersion = 0; FtrGet ( sysFtrCreator ,sysFtrNumROMVersion, &ROMVersion ); //Alert and bail if the ROM version is too low if ( ROMVersion < ROM_VERSION_MIN ) { // Do something here that reports an error //Palm OS 1.0 will continuously re-launch this app //unless we switch to another safe o-ne if ( ROMVersion < ROM_VERSION_2 ) { AppLaunchWithCommand( sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL ); } return ( 0 ); } |
通过函数Ftret()的调用可以得到ROM中Palm OS的版本。从而得知当前运行的Palm OS版本是否达到了你应用程序的最低运行配置。一般Palm OS 1.0版本的处理方法是不停的重新加载应用程序(如果上一次加载不成功的话),直到你主动切换到其它的应用程序。这样就有可能使你陷入一种近乎死循环的尴尬局面,所以,在这样的情况发生前你应该先调用A ppLaunchWithCommand()函数,使你得以从容跳出当前的应用。
下面是环境检查代码,你可以藉此查验当前的运行环境是否是一个正常的运行环境(normal cicumstances)。到下一章,我们还将看到其它的运行环境。
| /* If this is not a normal launch, don't launch */ if( cmd != sysAppLaunchCmdNormalLaunch ) return( 0 ); |
下面是一些关于Palm OS内嵌窗体管理器的函数调用。首先,调用FrmInitForm()函数,使我们的窗体进入备用状态。通过使用FrmSetEventHandler()函数为窗体绑定事件处理函数(e vent handling function)。然后,使用FrmSetActiveForm()函数来激活窗体。最后,调用FrmDrawForm()显示窗体。
| /* Initialize our form */ form = FrmInitForm( HelloForm ); FrmSetEventHandler( form, myHandleEvent ); FrmSetActiveForm( form ); FrmDrawForm( form ); |
下面是事件循环的开始部分。
| /* Our event loop */ do { |
深入: 什么是事件循环?事件循环是用来处理应用与用户,及应用之间交互的一种程序循环,譬如处理输入笔运动、按钮被按下等用户事件。许多现代图形用户界面(G UI),如Mac OS、Windows等都是事件驱动的操作系统。事件是一种操作系统和你的应用程序精确交互的手段,从而可以得知用户正在对应用程序做一些什么样的操作。一般,如P alm OS这样简单的操作系统,通常按发生的顺序来保存事件,用户可以请求事件和处理事件。
下面这个函数从ROM中调用事件处理器(Event Manager),并把老的事件放到队列中,以操作系统接收的先后顺序来处理事件。
| /* Get the next event */ EvtGetEvent( &event, -1 ); |
下面的函数调用了系统管理器用来处理系统事件。
| /* Handle system events */ if( SysHandleEvent( &event ) ) continue; |
下面这个函数不但调用了回调函数处理事件,而且还执行了很多其它的任务。譬如,使用penUpEvent 和penDownEvent 两个原始事件来产生新的事件,这就像ctlSelectEvent事件,我们使用在窗体事件处理函数中去决定按钮是否被按下了。这就是我们使用系统回调函数处理事件而不是把所有事件都放到一个巨大的事件循环中去的原因。
| /* Handle form events */ FrmDispatchEvent( &event ); |
一旦另一个应用程序被用户选择,就交由下面的代码来处理程序退出前的一切善后工作。 在这个例子程序中并没有什么需要清除的,所以我们就可以通过结束循环来退出执行中的应用程序。这些代码往往出现在循环的底部,因为我们在退出时总是需要往窗体事件处理器(或者其它的处理函数)中添点什么代码的,所以让这个退出事件接收循环贯穿整个程序结构是很有好处的。
| /* If it's a stop event, exit */ } while( event.eType != appStopEvent ); /* We're done */ return( 0 ); } |
helloHandleEvent() 函数
还记得吗?我们调用FrmSetEventHandler()函数的时候就是通过传递helloHandleEvent()函数的入口地址给Palm OS来实现的。这个函数通过调用FrmDispatchEvent()函数来处理窗体事件。
| /* Our form handler function */ static Boolean myHandleEvent( EventType* event ) { |
这个事件发生在窗体上的”Hello”按钮被按下后,在例子中,我同时让系统发出了警报音。
| /* Parse the event */ if( event->eType == ctlSelectEvent ) SndPlaySystemSound( sndAlarm ); /* We're done */ return( false ); } |
我的编程风格是通过超过500,000行以上代码,16年的程序生涯养成的。然而,看了我的程序并不意味着就学到了我的风格。我认为,不应该全部照搬书中的内容,而是要采用自己的方法来编程,并且不为尝试自己独特的编程风格而感到惭愧不安。用自己的方法写自己的样列程序,往往能使人更好的学习和理解那些内在的原理,所以我是非常赞同程序员编写自己风格的代码的。
调试:使它工作起来
在这一部分你就可以开始编译和链接这个项目工程了。首先选择运行Project | Make。如果你像我那样干的话,就可能会搞出一些BUG的(当然了并不是所有的方面都是完美的嘛)。祝你们好运吧。
我们已经取得不少的进步了。你现在可以通过Compare files操作比较快速的除掉一些编译时间错误了。如果你在创建工程的时候意外的删除了库文件,那么就会产生连接错误。你最好的处理办法就是尽早做好这些文件的备份工作。
发现运行时间错误的最好方法就是逐行的调试程序。你可以在Code Warrior IDE集成环境中执行下面的一些调试步骤:
1.在PC上选择Project | Debug 或者 点击F5来编译和连接工程,然后调出调试器(debugger)。
2.在Palm上,把设备先置位到Console模式。最简单的方法就是利用find框,删除find输入框中原有的文字并输入快捷符号。快捷符号很像连写的小写字母“l ”,你在输入区上画一个连写的“l”以产生快捷符号。然后写一个句号(在输入区轻击两下)和数字“2”。在你这样做后,你会发现Pa lm设备会发出声音并且所画的内容将消失。图2-2显示了把Palm设备设置为Console模式所要画的图形;
3.在PC上,点击OK用来下载应用程序到Palm设备。再点击OK,来去掉Palm上出现的Code Warrior lite警告框,现在你就应该停在Code Warrior 调试器中,PilotMain()函数的第一行了。
4.在PC上,点击调试器窗口顶端看起来象一个向右箭头的按钮。这将使你以单步调试的方式运行程序,进入函数后,箭头每次都将停留在下一步将要执行的语句上。点击另一个像向上箭头的按钮,将使你跳出任何一个执行中的函数。
5.在PC上,按下调试器窗口顶端的“X”按钮可以重置Palm设备。如果你的Palm设备已经关掉,那就再打开,然后按下“X”按钮,则设备将被置于一种良好的C o-nsole模式关闭的状态。当然,也可以通过使用图钉或其它尖的东西按下Palm背面的Reset按钮,来达到同样的目的。
值得庆幸的是,我早期的一些源代码评论将对你有用,特别是像你这样正努力推测程序中每条代码含义的时候。当然,如果你的窗体根本不显示的话,那么你就得首先确保你已经正确连接了当前资源文件(* .rsrc),以及通过在FrmDrawForm()函数入口设置断点和单步跟踪来确保它是否被调用了。
你也可以通过点击每行左边的页空白处来设置断点。如果设置了断点,一个小红点将出现在该行左边的页空白处。去掉断点只要点击该行左边页空白处显示的小红点就可以了。
当你的Hello示例程序被完全调试好之后,你将会发现:
1 在Palm的应用程序区将出现“Hello”的图标
2 一旦程序被加载,将显示它的窗体和“Hello”按钮
3 当“Hello”按钮被按下,程序将退出
4 当按下Palm上的4个真正的按钮或任何一个输入板(graffiti pad)边上的“Silkscreen bottom”,程序都将退出
祝贺你。你已经写完了你的第一个Palm OS 应用程序了。这是一个很好的程序,可以用来测试你当前使用的代码中是否存在一些不易发现的不安全的设置。
当你运行你的程序时发生了什么?
现在你已经拥有一个能工作的Palm OS程序了,让我们设置一个断点,再一次单步调试,以便于更加深入的了解它是如何工作的。
1.在PC上,开始运行调试器。选择Project | Debug 或 按下 F5 开始编译和连接工程以及弹出调试器。
2.在Palm上,设置你的设备到Console状态。使用find框,删除find输入框中原有的文字并输入快捷符号。快捷符号很像连写的小写字母“l ”,你在输入区上画一个连写的“l”以产生快捷符号。然后写一个句号(在输入区轻击两下)和数字“2”。在你这样做后,你会发现Pa lm设备会发出声音并且所画的内容将消失。
3.在PC上,单击OK后,下载应用程序到Palm。现在你需要在PilotMain()函数的第一行设置断点。
4.在PC上,从调试器窗口中找到,如下代码:
/* Parse the event */
if( event->eType == ctlSelectEvent )
SndPlaySystemSound( sndAlarm );
5.在PC上,在if语句前设置断点。
6.通过反复的按下执行一行代码的按钮,来单步逐行调试整个程序。注意到,FrmDispatchEvent()函数的调用经常停留在窗体事件处理器上(m yHandleEvent()),但由于发生的并不是ctlSelectEvent,所以SndPlaySystemSound()就没有运行。跳过SndPlaySystemSound()函数后,程序又进入了G etEvent()状态,等待其它事件的发生了。
7.点击Palm上的“Hello”按钮,注意到,GetEvent()函数响应了,窗体事件处理器被反复调用了许多次,这次终于发生了ctlSelectEve nt,于是SndPlaySystemSound()就被调用了。你现在已经亲眼目睹了你的代码分配和处理一些事件的全过程了。
8.在PC上,按下调试器窗口顶端的“X”按钮可以重置Palm设备。如果你的Palm设备已经关掉,那就再打开,然后按下“X”按钮,则设备将被置于一种良好的C o-nsole模式关闭的状态。当然,也可以通过使用图钉或其它尖的东西按下Palm背面的Reset按钮,来达到同样的目的。
把你的工程存放到固定的目录中
在后面的文章中,我们将使用做好的应用程序作为整个程序外观的基础。在Code Warrior中,提供了一种叫做“Stationary”的建立详细工程项目的模板,现在就请把当前项目做成一个模板。在你的 Code Warrior下的Stationary子目录中保存一个应用程序模板的拷贝,这样当你下一次选择New Project选项的时候它就会出现。你也可以按照以下步骤在Windows资源管理器中复制应用程序模板:
1.打开Windows资源管理器,找到你的Hello工程的目录。
2.选中整个Hello工程目录。
3.按下CTRL-C复制目录
4.在Code Warrior目录下找到Stationary子目录,进入。
5.按下CTRL-V复制Hello目录。
