********************************LoongEmbedded********************************
作者:LoongEmbedded(kandi)
时间:2011.8.25
类别:WINCE 系统开发
********************************LoongEmbedded********************************
1. WINCE控制面板架构
WINCE系统的控制面板提供的应用是基于windows桌面系统下应用的小子集,在WINCE系统中,控制面板系统由三部分组成,分别是Ctlpnl.exe、Control.exe和一些控制面板文件(.cpl)。Ctlpnl.exe和Control.exe相应地被操作系统用于控制控制面板文件夹显示和机构,这两部分构成控制面板系统架构的最根本部分,而那些.cpl文件对应于控制面板功能的应用。如果要创建子集的控制面板应用,我们必须创建一个.cpl文件,这个文件导出适合于Control.exe建立的架构的回调函数
一个控制面板应用程序实际是一个dll,但它的扩展名是.cpl的文件,这个文件导出回调函数CPlApplet来处理控制面板系统发送过来的CPL_INIT、CPL_GETCOUNT、CPL_NEWINQURE、CPL_STOP|和CPL_EXIT消息,在处理这些消息的时候可以只直接调用某个.exe来执行相应的应用,也可以是直接的处理。当用户打开控制面板的时候,OS通过搜索位于“\Windows”目录下面的.cpl文件来列举出控制面板系统下的控制面板应用,比如我们当前的系统中有下面的.cpl:
图1
我双击“控制面板”图标进入控制面板的时候,输出下面的出错提示信息:
CPL: Failed to load '\Windows\system.cpl'
说明加载system.cpl失败,而我在系统中添加自己的应用HelloCPL.cpl的系统中,在双击“控制面板”的时候,也相应的提示下面的信息:
CPL: Failed to load '\Windows\HelloCPL.cpl'
后来在HelloCPL.文件中加入下面的内容才成功加载HelloCPL.cpl的
LIBRARY HelloCPL
EXPORTS
CPlApplet
可以在WINCE600的目录下没有找到生成system.cpl的地方,只在下面的目录中
\WINCE600\PUBLIC\DATASYNC\OAK\CTLPANEL\SYSTEM找到生成systemcpl.cpl的源代码所在地方,但其导出函数为KillAllApps,并且为空函数,所以在控制面板下是看不到此控制面板应用程序对应的图标。
CTLPNL文件夹中有九个文件夹,分别对应不同的CPL文件:
ADVBACKLIGHT:高级背光灯管理。
BTHPNL:蓝牙。
CONNPNL:拨号网络。
CONTROL:主控制台,我们在explorer中看到的"控制面板"应该就是这个。
CPLMAIN:会生成cplmain.cpl,控制面板的大多数选项均源自于此。
CTLPNL:和CONTROL相关联的
DMPNL:对应设备管理器。
INTL2:区域和语言设置。
STGUI:存储器管理。
图2
2. 控制面板应用程序的入口和导出函数
2.1 入口函数DllMain
因为.cpl文件实际就是dll,而dll的入口函数是DllMain,所以控制面板应用程序的入口函数是DllMain也就很自然的事情了。在进程和现场被初始化和终止的时候,系统会调用这个函数,或者在调用LoadLibrary()和FreeLibrary()函数的时候会调用这个入口函数。
DllMai函数在这里的作用主要是保存好传递进来的指向DLL的句柄,以便后面使用。
2.2 .def函数要导出的回调函数CPlApplet
LONG CPlApplet(HWND hwndCPl, UINT msg, LPARAM lParam1, LPARAM lParam2)
hwndCPl:控制面板窗口的句柄
msg:发给控制面应用程序的消息
WinCE中所支持的控制面板消息如下:
CPL_INIT:被首次加载的时候会收到该消息,也是第一个消息,控制面板应用程序接收到该消息后会立刻执行全局变量的初始化,尤其是内存的分配的动作也在这里执行。
CPL_GETCOUNT:第二个被收到的消息,该消息用于获得该控制面板应用程序中的组件数,因为.cpl文件中可能包含多个Applet程序,比如cplmain.cpl中就有十几个Applent程序,见图2。
CPL_NEWINQUIRE:查询当前控制面板程序支持的对话框(dialog box)信息,如果该.cpl中包含多个对话框,那么lParam1表示对话框号,lParam2是一个指向NEWCPLINFO结构的指针,描述的是一个对话框的相关信息,见图2,connpnl.cpl中有14个组件,比如“笔针”就是其支持的一个对话框,我们在双击“笔针”图标的时候,串口输出下面的信息:
CTLPNL: cmdline=\Windows\cplmain.cpl,9
--->>>CTLPNL: CPL='\Windows\cplmain.cpl' icon=9 tab=0
说明“笔针”是cplmain.cpl支持的第9个对话框,而双击图2的“显示分辨率”图标的时候,输出下面的串口信息:
CTLPNL: cmdline=\Windows\HelloCPL.cpl,0
--->>>CTLPNL: CPL='\Windows\HelloCPL.cpl' icon=0 tab=0
因为添加的控制面板应用程序HelloCPL.cpl只支持一个对话框,上面的0表示第0个对话框。lParam2指向的NEWCPLINFO结构体定义如下:
typedef struct tagNEWCPLINFO {
DWORD dwSize;
DWORD dwFlags;
DWORD dwHelpContext;
LONG lData;
HICON hIcon;
TCHAR szName[32];
TCHAR szInfo[64];
TCHAR szHelpFile[128];
} NEWCPLINFO;
dwSize:该结构的信息
dwFlags:忽略
dwHelpContext:忽略
lData:传给组建程序的数据
hIcon:显示在控制面板中的图标的句柄,见图2,就是这些图标信息。
szName:显示在控制面板中的组件的名字,比如“笔针”
szInfo:显示在控制面板中的描述信息
szHelpFile:忽略
CPL_DBCLK:用户在控制面板界面中双击某个应用时,会收到该消息,在该消息中执行对应的应用程序。如果包含多个对话框,那么lParam1表示对话框号,lParam2为传给应用程序的数据。
CPL_STOP:关闭控制面应用程序时,收到该消息,用于释放资源。如果包含多个对话框,那么lParam1表示对话框号,lParam2为传给应用程序的数据。
CPL_EXIT:在CPL_STOP消息之后,控制面板释放该应用程序时,收到该消息。
lParam1:消息参数1
lParam2:消息参数2
3. 在WINCE控制面板中添加控制面板应用程序的步骤
3.1 创建一个添加一个应用程序
在创建应用程序之前,我们首先要有一个WINCE的系统工程,在打开这个工程的基础上我们创建一个应用程序子工程,在File->New->Subproject中选择WCE Application,这里我们选择Hello World应用程序,创建这个应用程序的目的为后面创建的控制面板应用程序调用的。
3.2 创建控制面板应用程序的CPL工程
点击File->New->Subproject,然后选择WCE Dynamic-Link Library,工程名为HelloCPL。
图3
然后点击Next,在Auto-generated subproject files页面中选择A Dll that exports some symbols。
图4
然后点击Finish完成,经过前面两部后我们可以看到:
图5
3.3 修改DllMain和添加CPlApplet函数
#include "Cpl.h" //因为添加CPlApplet函数,此函数又对CPL_XXX消息的处理
#include "resource.h"// IDI_HELLO_CPL、IDS_HELLO_KANDI和IDS_HELLO_SANDI的定义在这里
#define lengthof(exp) ((sizeof((exp)))/sizeof((*(exp))))
HMODULE g_hModule = NULL; // Handle to the DLL.
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_hModule = (HMODULE) hModule;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// The entry point to the Control Panel application.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
extern "C" LONG CALLBACK CPlApplet(HWND hwndCPL,
UINT message, LPARAM lParam1, LPARAM lParam2)
{
switch (message)
{
case CPL_INIT:
// Perform global initializations, especially memory
// allocations, here.
// Return 1 for success or 0 for failure.
// Control Panel does not load if failure is returned.
//RETAILMSG(1, (TEXT("kandi helloworld test 1111\r\n")));
return 1;
case CPL_GETCOUNT:
// The number of actions supported by this Control
// Panel application.
//RETAILMSG(1, (TEXT("kandi helloworld test 2222\r\n")));
return 1;
case CPL_NEWINQUIRE:
// This message is sent once for each dialog box, as
// determined by the value returned from CPL_GETCOUNT.
// lParam1 is the 0-based index of the dialog box.
// lParam2 is a pointer to the NEWCPLINFO structure.
{
ASSERT(0 == lParam1);
ASSERT(lParam2);
NEWCPLINFO* lpNewCplInfo = (NEWCPLINFO *) lParam2;
// Release((TEXT("kandi test 1111\r\n")));
// RETAILMSG(1, (TEXT("kandi helloworld test 3333\r\n")));
if (lpNewCplInfo)
{
lpNewCplInfo->dwSize = sizeof(NEWCPLINFO);
lpNewCplInfo->dwFlags = 0;
lpNewCplInfo->dwHelpContext = 0;
lpNewCplInfo->lData = IDI_HELLO_CPL;
//RETAILMSG(1, (TEXT("kandi helloworld test 44444\r\n")));
// The large icon for this application. Do not free this
// HICON; it is freed by the Control Panel infrastructure.
lpNewCplInfo->hIcon = LoadIcon(g_hModule,
MAKEINTRESOURCE(IDI_HELLO_CPL));
LoadString(g_hModule, IDS_HELLO_KANDI, lpNewCplInfo->szName,
lengthof(lpNewCplInfo->szName));
LoadString(g_hModule, IDS_HELLO_SANDI, lpNewCplInfo->szInfo,
lengthof(lpNewCplInfo->szInfo));
_tcscpy(lpNewCplInfo->szHelpFile, _T(""));
return 0;
}
return 1; // Nonzero value means CPlApplet failed.
}
case CPL_DBLCLK:
{
// The user has double-clicked the icon for the
// dialog box in lParam1 (zero-based).
//RETAILMSG(1, (TEXT("kandi helloworld test 5555\r\n")));
PROCESS_INFORMATION pi = {0};
if (CreateProcess(_T("\\Windows\\HelloWorld.exe"), NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}
return 1; // CPlApplet failed.
}
case CPL_STOP:
// Called once for each dialog box. Used for cleanup.
//RETAILMSG(1, (TEXT("kandi helloworld test 66666\r\n")));
case CPL_EXIT:
// Called only once for the application. Used for cleanup.
//RETAILMSG(1, (TEXT("kandi helloworld test 77777\r\n")));
default:
return 0;
}
return 1; // CPlApplet failed.
} // CPlApple
3.4 为CPlApplet函数处理CPL_NEWINQUIRE时添加其所需要加载的图标和字符串
在这里图标和字符串分别对应ID: IDI_HELLO_CPL、IDS_HELLO_KANDI和IDS_HELLO_SANDI的定义,添加的步骤如下:
1) 为CPL工程添加rc资源文件
右击图5的HelloCPL->source files,选择Add->New Item,见下图:
图6
点击Add后,双击HelloCPL->source files->HelloCPL.rc,见下图
图7
3.5 为rc资源文件添加ICO图标和字符串
右击图7的HelloCPL.rc,选择Add Resource,弹出下面的对话框。
图8
具体就不描述了。
3.6 修改HelloCPL工程配置
1) 把HelloCPL.bib文件的内容修改为
MODULES
HelloCPL.cpl $(_FLATRELEASEDIR)\HelloCPL.cpl NK
2) 右击HelloCPL工程,选择Properities,选择General页面,在Custom Variable
项中添加变量,变量名字为CPL,值为1,这样做的目的就是强制生成HelloCPL应用程序的扩展名为.cpl,而不是dll。然后选择C/C++页面,确认Additional Macro Definitions的值为$(CDEFINES) -DHelloCPL_EXPORTS。设置DLL Entry Point项为DllMain,因为HelloCPL.cpl的入口函数就是DllMain。在Include Directories项中添加路径$(_PROJECTROOT)/cesysgen/oak/inc。
3) 在HelloCPL.def文件中添加下面的内容
LIBRARY HelloCPL
EXPORTS
CPlApplet
目的是到处接口函数CPlApplet,加入没有添加这些内容,将无法有效加载HelloCPL.cpl。
3.7 分别单独编译HelloWorld工程和HelloCPL工程,分别生成HelloWorld.exe和
HelloCPL.cpl,然后会生成新的NK.bin,把NK.bin烧录到WINCE设备中,双击控制面板后可以看到下图:
图9
双击HelloCPL.cpl对应的图标,可以看到下图:
图10
这就是HelloWorld.exe应用程序显示的界面,是由HelloCPL.cpl的CPlApplet函数在处理双击消息的时候调用的。