3.1 创建应用程序
C++Builder的主要用途于设计创建Windows应用程序。有三种基本的Windows应用程序:
· WindowsGUI应用程序。
· 控制面板应用程序。
· 服务应用程序。
3.1.1 Windows应用程序
当编译一个工程时,将生成一个可执行文件(.EXE)。这个可执行文件通常提供程序的基本功能。
简单的程序通常只包含一个.EXE文件,可以在可执行文件中通过调用DLL、程序包和其他支持文件来扩充程序。
Windows提供了两种应用程序UI(用户界面)模式:
· 单文档界面(SDI)。
· 多文档界面(MDI)。
除了应用程序的实现模式,可以通过在IDE中设置工程选项来治理工程在设计时的行为和应用程序在运行时的行为。
1.用户界面模式
对于所设计的任意窗体可以在应用程序中以多文档界面(MDI)或单文档界面(SDI)来实现。在一个MDI应用程序中,一个单个的父窗口可以打开多个文档或子文档,这种情况在表处理器和字处理器中十分常见。相对而言,一个SDI应用程序通常只包含单个文档,为了将窗体设计成SDI应用程序,应将窗体对象的FormStyle属性设置成fsNormal。
关于开发应用程序UI的更多信息可参阅第4章。
(1)SDI应用程序
为了生成新的SDI应用程序:
1)选择FileNew,打开NewItems对话框。
2)单击PRoject页面并选中SDIapplication。
3)单击OK。
缺省时,Form对象的FormStyle属性被设置为fsNormal,所以C+Builder假设所有新的应用程序都是SDI应用程序。
(2)MDI应用程序
为了生成新的MDI应用程序:
1)选择FileNew,打开NewItems对话框。
2)单击Project页面并选中MDIApplication。
3)单击OK。
设计MDI应用程序需要更多的设计,比设计SDI应用程序更为复杂。MDI应用程序派生客户端窗口中的子窗口,主窗体包含子窗体,通过设置TForm对象的FormStyle属性,可定义一个窗体是子窗体(fsMDIForm),还是主窗体(fsMDIChild)。一个好的方法是,为子窗体定义一个基类,并从这个基类中派生出各个子窗体,从而避免重新设置子窗体的属性。
2.设置IDE、工程和编译选项
使用ProjectProjectOptions来定义工程的不同选项。要获得更多的信息可参阅在线帮助。
设置缺省的工程选项要改变应用于未来所有工程的缺省选项,可设置ProjectOptions对话框中的选项,选中窗口右下部的Default框,则所有新工程都会以目前的选项作为缺省值。
3.1.2 程序设计模板
程序设计模板通常用于程序的“框架”结构,可以在其中填加程序代码。比如,若要在代码中使用一个for循环,可以插入如下模板:
for(;;)
{
}
要在代码编辑器中插入一程序代码模板,可按下Ctrl-J,再选择要用的模板。也可以将自己的模板加入这个集合。为了填加一个模板:
1)选择ToolsEnvironmentOptions。
2)单击CodeInsight标签页。
3)在模板区域中单击Add。
4)选择一个快捷键名称并输入对新模板的简洁描述。
5)在Code文本框中加入模板程序代码。
6)单击OK。
3.1.3 控制台应用程序
控制台应用程序是32位Windows程序,它没有图形界面,通常在控制台窗口中运行。这种应用程序一般不要求用户太多的输入,仅仅完成有限的功能。
为了创建新的控制台程序:
1)选择FileNew对话框中选择ConsoleWizard(向导)。
2)在ConsoleWizard对话框,选中ConsoleApplication选项,为工程的主模板选择源程序类型(C或C++),或者指定一个包含了一个main或winmain函数的已存在的文件,然后单击OK按钮。
ConsoleWizard将为这种类型的源程序文件创建一个工程文件。
在控制台应用程序中使用VCL当创建了一个新的控制台应用程序时,IDE并不创建一个新的窗体,而只有代码编辑器出现。可以在控制台应用程序中使用VCL对象。为此,必须在ConsoleWizard中说明要使用VCL(选中UseVCL选项)。
假如不这样说明,将不能在程序中使用VCL。而强行使用将导致链接程序错误。
3.1.4 服务应用程序
服务应用程序接收客户应用程序的请求,然后处理它们,并给客户应用程序返回信息。一般来说,它们在幕后运行,不要太多的用户输入。Web、FTP或者E-MAIL(电子邮件)服务程序都是一个服务应用程序的例子。
为了创建实现Win32服务的应用程序,选择FileNew,并从NewItems页面中选择ServiceApplication,这将在工程中加入一个类型为TServiceApplication的全局变量Application。一旦创建了服务应用程序,在设计栏中会看见一个对应服务(TService)的窗口,通过在Object Inspector(对象观察器)中设置其属性和事件处理程序就可实现该服务;也可以在NewItems对话框中选择Service为服务应用程序增加其他的服务,不要将服务加入非服务应用程序中。否则,虽然可以加入一个TService对象,但应用程序不能生成必要的事件,也不能将该服务作适当的Windows函数调用。
一旦建立了服务应用程序,就能用ServiceControlManager(SCM)安装它的各项服务,其他应用程序可以向SCM发送请求,启动这些服务。
为了安装应用程序的服务,可以使用/INSTALL选项来运行它,应用程序安装完服务后退出,假如安装成功将给出一个确认消息,可以使用/SILENT选项运行服务应用程序来抑制确认信息。
若要卸载服务,可使用/UNINSTALL选项运行命令(也可以在卸载时用/SILENT选项来抑制确认信息)。
范例:这个服务具有端口号为80的TServerSocket。这是Web浏览器向Web服务器请求服务和Web服务器响应Web浏览器的缺省端口号,这个特例会在C:\Temp目录下生成名为WebLogxxx.log的文本文档(其中xxx是ThreadID号)。任意给定端口只应被一个服务器监听,因此,假如具有Web服务器,必须确定它没有进行监听(该服务被停止了)。
为了看到结果,可打开一个本地的Web浏览器,在地址栏中输入LocalHost,浏览器最终会超时,但在C:\Temp目录下会得到一个名为WebLogxxx.Log的文件。
1)为了创建这个例子,选择FileNew,在NewItems对话框中选择ServiceApplication,可以看见名为Service1的窗口,在组件面板的Internet页中,把一个ServerSocket组件加入到服务窗口(Service1)。
2)第2步,把一个TMemoryStream类型的私有数据成员添加到TService1类中,程序单元的头文件如下:
3)接着选择在第1步中加入的ServerSocket1组件。在对象观察器的Events页面中,双击OnClientRead事件,添加以下事件处理程序:
4)最后,单击窗口的客户区(但不是ServiceSocket)选择Service1。在对象观察器中,双击OnExecute事件,添加以下事件处理程序。
编写服务器应用程序时,应当知道:
· 服务线程。
· 服务名属性。
· 调试服务。
1.服务线程
每个服务都有自己的线程(TServiceThread),因此,假如服务应用程序实现多个服务,则必须确定服务实例是安全的线程(thread_safe)。TServiceThread被设计成可以使用TService的OnExecute事件处理程序实现服务,服务线程有自己的Execute方法,它包含一个能在处理新请求之前调用服务的OnStart和OnExecute处理程序的循环。
因为服务请求可能会花很长时间进行处理,服务应用程序也可能同时收到多个用户的类似请求,较有效的方法是对每个请求产生新线程(从TThread而不是TServiceThread中派生),并将服务实例移到新线程的Execute方法中。这将答应服务线程的Execute循环连续地处理新请求且不用等待服务的OnExecute处理程序结束。下面是一个例子。
范例:这个服务会在标准的线程中每隔500μs便发出声响,当服务被告之要暂停、继续或停止时,它会处理线程的暂停、继续或停止。
1)选择FileNew,在NewItems对话框中选择ServiceApplication,将打开一个名为Service1的窗口。
2)在程序单元的头文件中,声明一个名为TSparkyThread的TThread的新后代,它是为服务工作的线程,声明如下:
3)下一步,在程序单元的.cpp的文件中,为TSparkyThread实例创建全局变量。
TSparkThread *Sparkythread;
4)把以下代码加入到.cpp文件中的TSparkyThread构造函数中。
5)在.cpp文件的TSparkyThreadExecute方法(线程函数)中加入以下代码:
6)选择Service窗口(Service1),在对象观察器中双击OnStart事件,在OnStart事件处理程序中加入以下代码:
7)在对象观察器中双击OnContinue事件,在OnContinue事件处理程序中加入以下代码。
8)在对象观察器中,双击OnPause事件,在OnPause事件处理程序中添加以下代码:
9)最后,双击对象观察器中的OnStop事件,在OnStop事件处理程序中添加以下代码:
在开发服务应用程序时,应根据所提供服务的性质、连接的预期数目以及期望运行服务的处理器数派生新线程。
2.服务名属性
VCL提供了创建服务应用程序的类,包括TService和TDependency,使用这些类时,不同的名称属性易使人感到混淆,本节描述了它们之间的区别。
具有与口令关联的用户名(称之为Service启动名)的服务,在治理器和编辑窗口显示名称和真名称(该服务的名字)。Dependency 可以是服务或者按分组顺序装载,它们也有名字和显示名。因为服务对象是从TComponent派生,所以它们继续了Name属性,下面两节概要介绍这些名称属性。
(1)TDependency属性
TDependency的DisplayName既是服务的显示名又是真实名,它几乎总是和TDependency的Name属性相同。
(2)TService名称属性
TService的Name属性从TComponent中派生,它是组件名,也是服务名,对于作为服务的Dependency,这个属性与TDependency的Name和DisplayName属性相同。
TService的DisplayName是显示在服务治理器(ServiceManager)窗口中的名字,它通常和真实服务名不同(TService::Name、TDependency::DisplayName、TDependency::Name),注重通常Dependency的DisplayName和Service的DisplayName不同。
服务启动名与服务显示名、真实服务名是不一样的,ServiceStartName是在服务控制治理器中选择Start对话框输入的用户名。
3.调试服务
调试服务应用程序有一些技巧,因为它需要一些短的间隔时间。
1)首先,在调试器中,启动应用程序,等待数秒直至装入完成。
2)从控制面板或命令行快速启动服务。
必须快速启动服务(应用程序启动在15~30s内),因为假如没有服务被启动,则程序会终止。
当应用程序运行时,附加服务应用程序也是一种方法(也就是说,先启动服务,再附加调试器)。为了与服务应用程序进程连上,可选择RunAttachToProcess,并在结果对话框中选择服务应用程序,在某些情况下,因为没有充分的权限,会使第二种方法失败。假如是这样,可以使用服务控制治理器来激活调试器的调试服务。
1)首先,在下列注册项中创建名为ImageFileExecutionOptions的要害字。
HKEY_LOCAL_MACHINE\SIFTWARE\Microsoft\Windows NT\CurrentVersion
2)创建与服务(如:MYSERV.EXE)同名的子要害字。在这个子要害字中加入类型为REG_SZ的值,命名为Debugger。使用到BCB.exe的完整路经名作为字符串的串值。
3)在服务控制面板的应用程序中选择服务,单击Startup并选中AllowService为Interactwith Desktop。