ZZ From:
Windows 控制面板编程
说明:本文章为本人在做项目时查阅相关文章而写成,如有不当之处,请指出。
mail:
关键字:
控制面板,控制面板应用程序,
一、 什么是控制面板
打开 Windows 的控制面板会看到类似的图像
图一
双击其中的一个图标,会显示对话框,让用户来完成相应的软硬件设置工作。这就是我们看到的控制面板。那么如何开发控制面板程序呢?带着疑问在 MSDN 和 google 里搜索关键字“ Control Panel ”,就会找到相关的技术文章。这是我工作的方法:借鉴已有的资源。但实际情况是那样吗?我们可以跟着 MSDN 的讲述来一步一步深入下去。
经过挖掘,发现并不是 exe 文件( Windows Vista 下支持 exe 的控制面板应用程序,并且微软建议做成 exe 文件),而是有着 cpl 后缀名的文件,在 windows - >system32 下可以找到这样的文件。如果借助工具, Dependency Walker for Win32 (x86) 或 dumpbin 等就可以看到该文件导出了一些函数。
图二
多观察几个这样的文件,发现导出的函数虽有差异,但其中都有 CPLApplet 函数被导出。这些特征与 DLL 的特征吻合。去 MSDN 上查阅 CPLApplet 函数的说明证明我们的猜测是正确的。可以说控制面板应该程序就是以 CPL 为后缀名并且一定要导出 CPLApplet 函数的 dll 文件。
对于具体的描述可以参考:
二、 明确几个概念
l 控制面板管理程序: 用于管理控制面板的程序,在桌面 windows 版本是 CONTROL.EXE ,在 windows CE 版本是 CTLPNL.EXE ,它们负责管理控制面板里的控制面板条目。简单的说,我们打开控制面板时,这些管理程序就在运行了。只不过我们看到的是挂上了 Shell 外观而已( 注:这是我的猜测,还没有找到依据 )。
l 控制面板条目( Control Panel Item ): 在控制面板里看到的每个图标所对应的就是一个控制面板条目。
l 控制面板应用程序( Control Panel Application ): 就是最终看到的 CPL 文件,一个控制面板应用程序可以实现几个控制面板条目。
三、 控制面板应用程序的编写
编写控制面板应用程序,就是编写 dll 文件,在该文件中实现控制所需要的功能。这就涉及到一个不得不说的函数,没有它就无法完成控制面板程序的实现。该函数为 CPLApplet 。下面就该函数的参数等知识做些介绍。
函数: LONG CPLApplet ( HWND hwndCPl , UINT msg , LPARAM lParam1 , LPARAM lParam2 )
函数 CPLApplet 是控制面板应用程序( Control Panel application )的入口点,它被控制面板管理程序( control.exe 或 Ctlpnl.exe )自动调用,它是个回调函数( Callback ),注意: CPL 文件一定要把函数 CPLApplet 导出,这样控制面板才能找到程序的入口点。
当启动控制面板时,它会搜索 Windows 或 System32 或注册表的相应条目目录下的文件,并把以 CPL 作为扩展名的文件载入,它调用 CPL 文件的导出函数 CPLApplet (),发送消息给该函数。所以,控制面板应用程序要处理控制面板发送过来的消息,即在函数 CPLApplet 中进行处理,该函数没有默认的行为。如果一个 CPL 文件中实现了多个控制面板程序,那么只会有一个 CPLApplet 函数,它负责所有的控制面板应用程序。
参数说明:
hwndCPl :控制面板管理程序或称为控制面板的窗口句柄,即为 control.exe 的窗口句柄。如果控制面板应用程序或其它窗口需要传递父窗口句柄,可以使用该参数。
Msg :发送到控制面板应用程序的消息,由控制面板管理程序发送。
l Param1 :消息参数
l Param 2 :消息参数
函数的返回值依据消息的不同而不同。
应用程序要使用该函数需要包含头文件: cpl.h
消息名称 | 描述 |
CPL_INIT | 控制面板应用程序收到的第一个消息,通常在此处理全局初始化和内存分配。成功返回非 0 ,否则返回 0 ,此时控制面板管理程序终止和该应用程序的通信,并释放相应的 CPL 文件。 |
CPL_GETCOUNT | 该消息紧接在 CPL_INIT 消息之后被发送,它返回控制面板管理程序所能看到该 CPL 文件中所包含的控制面板组件的数目,即该 CPL 文件可以出现在控制面板中的图标的数目。 |
CPL_INQUIRE | 于 CPL_GETCOUNT 之后被发送,为指定的控制面板条目提供信息。 |
CPL_NEWINQUIRE | 于 CPL_GETCOUNT 之后被发送,与消息 CPL_INQUIRE 完成的功能类似,只不过其实现要求 TNewCPLInfo 结构指针,所包含的资源不提供缓存,所以控制面板启动的较慢,一般不建议处理该消息,除非特别必要,如要根据一定的条件动态的改变控制面板条目的图标、字符串等。 |
CPL_DBLCLK | 表明用户选定了一个控制面板条目,程序应该显示相应的对话框以便用户完成相应的任务。成功返回 0 ,否则,返回非 0. |
CPL_STOP | 控制面板管理程序关闭时被发送,控制面板应用程序在此时处理内存释放等动作。成功处理,返回 0. |
CPL_SELECT | 目前不被使用。只有 Windows 95 和 Microsoft Windows NT 4.0 之前的系统使用。 |
CPL_STARTWPARMS | 该消息与 CPL_DBLCLK 类似,但 lParam 2 指向 LPCTSTR ,该消息在 shell32.dll ( Windows 2000 , Windows Millennium Edition (Windows Me) )及以后版本有效 |
CPL_EXIT | 于 CPL_STOP 消息之后被发送,这是控制面板应用程序在释放资源的最后机会。成功处理返回 0. |
CPL_INQUIRE : lParam1 是以 0 为起点的整数,它是该 CPL 文件中所包含的控制面板条目的索引, lParam2 参数要求一个 CPLINFO 结构的指针,用来填充所需的图标、字符串等信息。如果成功处理了该消息,应该返回 0 。
CPL_NEWINQUIRE :该消息与 CPL_INQUIRE 都是 CPL_GETCOUNT 之后被发送的消息,但并没有明确的先后顺序。所以程序里不要依赖它们的顺序来处理不同的事务。
编写控制面板应用程序的步骤:
1 选择适当的开发工具(如: Visual Studio 2008 ),建立 DLL 项目;
2 导出函数 CPLApplet ;
3 在函数 CPLApplet 的消息处理过程中完成你需要的工作;
一个简单的例子
开发工具: Microsoft Visual Studio 2008
操作系统: Windows XP SP2
步骤:
1 新建 Win32 Project, 工程名为 CPLTest ;
2 应用程序类型选择 DLL ( CPL 文件本质上是 DLL );
3 在项目中新增或导入一个图标文件和两个字符串资源,用于在控制面板管理程序中显示图标和提示;
在 Resource Files 上右键选择 Add - >Resource ,然后选择 Icon 或 String Table
以下为 resource.h 的部分内容
#define IDI_ICON1 101 // 图标标识
#define IDS_STRING102 102 // 字符串 tom
#define IDS_STRING103 103// 字符串 cui
4 在 dllmain.cpp 文件中增加函数的导出 CPLApplet ;
extern "C" __declspec ( dllexport ) LONG APIENTRY CPlApplet ( HWND hwndCPL , UINT uMsg , LPARAM lParam1 , LPARAM lParam2 );
原则上可以按照上面的方式导出就可以了,但是请注意 CPlApplet 的调用方式是 APIENTRY ,通过这样方式导出的函数会被改名,通过多次实验也不可行。你可能会上去掉 APIENTRY ,但这样编出来的 CPL 文件无法运行,查阅了相关文档,在 Windows Mobile Version 5.0 SDK 的文档里指明了该函数的调用方式, windows CE 5.0 和 Windows Shell and Controls 没有指明这种调用方式。所以,只有加上 APIENTRY 。
现在的问题是如何导出该函数?看来要通过 DEF 文件了,如果你的项目里没有产生 DEF 文件,可以通过 Project->Properties->Linker->Module Definition File 来指定或自己用记事本建立这样的文件,输入如下内容。
; CPLTest.def : Declares the module parameters for the DLL.
LIBRARY "CPLTest"
EXPORTS
; Explicit exports can go here
CPlApplet
5 在 dllmain.cpp 文件中增加函数 CPLApplet 的消息处理函数来完成指定的功能;
在 dllmain.cpp 中包含以上两个头文件
#include "resource.h" // 资源标识
#include <Cpl.h> //CPLApplet 函数要求的头文件
我的例子完成显示一个 MessageBox 的功能。
dllmain.cpp 的完整代码:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include "resource.h"
#include <Cpl.h>
LONG APIENTRY CPlApplet ( HWND hwndCPL , UINT uMsg , LPARAM lParam1 , LPARAM lParam2 );
BOOL APIENTRY DllMain ( HMODULE hModule ,
DWORD ul_reason_for_call ,
LPVOID lpReserved
)
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH :
case DLL_THREAD_ATTACH :
case DLL_THREAD_DETACH :
case DLL_PROCESS_DETACH :
break ;
}
return TRUE ;
}
LONG APIENTRY CPlApplet ( HWND hwndCPL , UINT uMsg , LPARAM lParam1 , LPARAM lParam2 )
{
int i ;
LPCPLINFO lpCPlInfo ;
i = ( int ) lParam1 ;
switch ( uMsg ) {
case CPL_INIT : // first message, sent once
return TRUE ;
case CPL_GETCOUNT : // second message, sent once
return 1;
break ;
case CPL_INQUIRE : // third message, sent once per application
lpCPlInfo = ( LPCPLINFO ) lParam2 ;
lpCPlInfo -> lData = 0;
lpCPlInfo -> idIcon = IDI_ICON1 ;
lpCPlInfo -> idName = IDS_STRING102 ;
lpCPlInfo -> idInfo = IDS_STRING103 ;
break ;
case CPL_DBLCLK : // application icon double-clicked
MessageBox ( NULL , TEXT ( "Tom66" ), TEXT ( "Cuei666" ), MB_OK );
break ;
case CPL_STOP : // sent once per application before CPL_EXIT
break ;
case CPL_EXIT : // sent once before FreeLibrary is called
break ;
default :
break ;
}
return 0;
}
6 编译
Project->Properties->Linker->Output File 修改输出文件的后缀名为 CPL ,也可以不修改,到最后把 dll 改为 cpl 也可以的。
四、 控制面板应用程序的安装与运行
l 将 cpl 文件拷贝到 Windows ( Windows CE )或 Windows/system32( 桌面版本 Windows) ,可以在这里双击运行,也可以打开控制面板就可以看到该 CPL 文件所包含的控制面板条目,图标和文件就是你在 CPLApplet 里指定的,双击也可运行。
2 在命令行下运行 rundll32 shell32.dll,Control_RunDLL CPLTest.cpl ( CPL 文件名) @1( 数字指定运行第几个控制面板条目,一个 CPL 文件可以包含几个控制面板条目 ) 。在 windows CE 下,在命令行输入 ctlpnl.exe /windows/cplmain.cpl,5 ,与桌面版本有些差异。
3 在 windows 的注册表 [HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Control Panel/Cpls] 下新建字符串,并指定 cpl 所在的完整路径,然后就可以在控制面板里看到新增加的控制面板条目。通过写注册表的方式,是一些应用软件惯用的方式,安装时可以通过 InstallShield 等安装制作工具将其添加到注册表,卸载时,删除注册表中相 关的项。
4 通过拷贝的方式,直接删除相应的 CPL 文件就可以了。至于有没有更好的方式,我还没有发现。
五、 参考资料:
1
2Add by myself
通过修改注册表的方式,来安装 控制面板应用程序(Control panel application),必须在CPlApplet中响应CPL_INQUIRE消息,并在其中加载图标。(LPCPLINFO->IdIcon),否则打开控制面板时,所安装应用程序的图标无法加载,无法显示。
而通过拷贝文件至System32目录,则可以响应CPL_INQUIRE或者CPL_NEWINQUIRE,可以在其中的任意一个响应函数中load图标。


发布评论