当我们打开控制面板时,会看到一些控制面板项目,如“添加/删除程序”、“调制解调器”、“系统”等。我们经常需要通过这些项目来对Windows进行配置。还有一些软件,如雅马哈声卡的驱动程序,会在控制面板中增加自己的配置项目。使用C++Builder 能方便快速地开发出自己的控制面板应用程序。
使用标准的控制面板程序 控制面板程序实际上就是一个DLL(动态链接库)文件,关键是它实现了CPlApplet函数。CPlApplet是一个回调(callback)函数,它处理所有发送给控制面板应用程序的消息。当控制面板应用程序运行起来时,调用它的程序从该控制面板应用程序中取得CPlApplet函数的地址,然后用该地址调用CPlApplet函数,并将消息传递给它。在处理消息时,必须按照一定的顺序进行。控制面板应用程序还有一个特点,就是一个DLL文件可以实现多个模块,每个模块可以有自己的图标、字符串资源,每一个模块对应于控制面板中的一个项目。
首先我们来看看控制面板应用程序执行的过程,弄明白了这点,才能编写出正确的控制面板应用程序。
由于控制面板应用程序是一个DLL文件,所以调用者必须使用LocaLibrary()函数来装载并执行。在控制面板应用程序装载时,CPlApplet 函数会接收到CPL_INIT消息,它表示控制面板应用程序正在初始化。这时CPlApplet 函数应该进行一些必要的初始化工作。如果初始化失败,CPlApplet 函数应该返回零值,通知调用者中止该控制面板应用程序的运行并释放占用的资源。该消息只发送一次。
当CPlApplet 函数返回初始化成功的信息后,调用者会发送CPL_GETCOUNT消息给它,CPlApplet 函数应该返回控制面板应用程序中模块的个数。该消息只发送一次。
随后,对于每个模块,CPlApplet 函数都会收到一条 CPL_INQUIRE 和CPL_NEWINQUIRE消息。响应此消息,CPlApplet函数将填充CPLINFO 或者NEWCPLINFO结构,其中存放了控制面板应用程序的名称、图标及描述信息。通常只需要处理CPL_INQUIRE消息,如果想更改图标和显示信息(如在不同的语言平台上显示不同的语言文字),则需要响应CPL_NEWINQUIRE消息。对于每个模块都会发送这两个消息一次。
当CPlApplet 函数接收到CPL_DBLCLK消息时,表示将要运行相应的模块。这个消息可以多次发送,一般的,当使用者用鼠标双击某个图标时,就会发送一个CPL_DBLCLK消息。该消息包含了模块的识别号,因此CPlApplet 函数可以分辨出是需要执行哪个模块的功能。
在控制面板应用程序退出前,对于每一个模块CPlApplet 函数都会接收到一条CPL_STOP消息,消息中包含该模块的识别号。此时CPlApplet函数需要针对不同的模块做不同的清理工作。
在处理完最后一条CPL_STOP消息后,CPlApplet函数会收到CPL_EXIT消息,表示控制面板应用程序即将退出。在CPlApplet函数返回后,调用者将立即使用FreeLibrary()函数释放占用的资源。
下面我们用C++ Builder来创建一个DLL文件,并编写 CPlApplet函数代码。该控制面板应用程序包含一个模块,该模块的功能是显示一个窗口,窗口中显示“Hello World”。以下代码在BCB4专业版和BCB5企业版中调试通过。
1.定义新文件
选择菜单“File -> new ”,在出现的画面中选择“DLL Wizard”。
2.保存文件
选择菜单中的“save project as”,保存项目文件和CPP 文件到指定的目录下。项目文件名称使用默认project1。
3.定义图标
定义控制面板应用程序在控制面板中显示的图标。这里我们使用RC资源文件。 [Page]
首先选择菜单中的“new->Text file”,生成一个文本文件,然后将它另存为ico.rc文件。编辑该文件,在其中加入一行“ MYICON ICON icon1.ico”,将一个图标文件复制到程序所在的目录里,并将文件名改为 icon1.ico,或者用Image Editor创建一个。注意,这个图标大小应该为32X32(大图标)。
在Project Manager中,将该RC文件加入到工程项目中。
4.加入一个新的Form,该Form将被作为窗口显示出来
选择菜单中的“file->new”,从中选择Form。然后在 Form上加上一个Label对象,设置其Caption为“Hello World”。
5.编辑project1.cpp文件
首先需要包含头文件cpl.h,这样才可以使用一些常数,如CPL_INIT等消息。
然后定义一个全局变量 gInstance。
处理DLL的入口函数,将程序句柄保存到全局变量 gInstance中。
程序内容如下: #include < vcl.h > #include < cpl.h > HINSTANCE gInstance; int WINAPI DllEntryPoint (HINSTANCE hinst, unsigned long reason, void*) { if (reason==DLL_PROCESS_ATTACH) gInstance=hinst; //gInstance 中存放着程序的句柄。 return 1; }
6.继续编辑project1.cpp文件,完成CPlApplet函数 // 由于CPlApplet函数需要在DLL外部使用,所以必须被输出。 extern “C\" int __stdcall __declspec(dllexport) CPlApplet(HWND HwControlPanel, int Msg, int lParam1, int lParam2) { switch (Msg) { case CPL_INIT: return true; // 返回初始化成功的信息 case CPL_GETCOUNT: return 1; // 告诉调用者该控制面板应用程序包含一个模块 case CPL_NEWINQUIRE: { // 在这里设置图标,名称等信息 NEWCPLINFO *Info=(NEWCPLINFO *)lParam2; ZeroMemory(Info,sizeof(NEWCPLINFO)); Info->dwSize=sizeof(NEWCPLINFO); // 图标用资源文件ico.rc文件中定义的MYICON Info->hIcon=LoadIcon(gInstance,“MYICON\"); strcpy(Info->szName,“模块名称\"); // szName为控制面板中该模块的名称 strcpy(Info->szInfo,“模块详细说明\" ); // szInfo为对该模块的说明。在控制面板中使用列表方式查看时, // 左面是模块的名称,右面是模块的说明。 return 0; } case CPL_DBLCLK: // 在这里加入需要实现的功能。 //这里我们只是简单地显示Form。 try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } return 0; } }
需要注意的是,在BCB4中,用DLL Wizard会生成 Project1.cpp文件,新加入的Form为Form1,对应CPP文件为Unit1.cpp;而在BCB5中,不会生成project1.cpp,而是生成了Unit1.cpp,新加入的Form为Form2,对应CPP文件为 Unit2.cpp。如果使用的是BCB5,则需要对上述代码做修改。