好多人已经知道利用strncpy替代strcpy来防止缓冲区越界。但是如果还要考虑运行效率的话,也许strlcpy是一个更好的方式。
1. strcpy
我们知道,strcpy 是依据 \0 作为结束判断的,如果 to 的空间不够,则会引起 buffer overflow。strcpy 常规的实现代码如下(来自 OpenBSD 3.9):
char *
strcpy(char *to, const char *from)
{
char *save = to;
for (; (*to = *from) != '\0'; ++from, ++to);
return(save);
}
但通常,我们的 from 都来源于用户的输入,很可能是非常大的一个字符串,因此 strcpy 不够安全。
2. strncpy
在 ANSI C 中,strcpy 的安全版本是 strncpy。
char *strncpy(char *s1, const char *s2, size_t n);
但 strncpy 其行为是很诡异的(不符合我们的通常习惯)。标准规定 n 并不是 sizeof(s1),而是要复制的 char 的个数。一个最常见的问题,就是 strncpy 并不帮你保证 \0 结束。
char buf[8];
strncpy( buf, "abcdefgh", 8 );
看这个程序,buf 将会被 "abcdefgh" 填满,但却没有 \0 结束符了。
另外,如果 s2 的内容比较少,而 n 又比较大的话,strncpy 将会把之间的空间都用 \0 填充。这又出现了一个效率上的问题,如下:
char buf[80];
strncpy( buf, "abcdefgh", 79 );
上面的 strncpy 会填写 79 个 char,而不仅仅是 "abcdefgh" 本身。
strncpy 的标准用法为:(手工写上 \0)
strncpy(path, src, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
len = strlen(path);
3. strlcpy
// Copy src to string dst of size siz. At most siz-1 characters
// will be copied. Always NUL terminates (unless siz == 0).
// Returns strlen(src); if retval >= siz, truncation occurred.
size_t
strlcpy(char *dst, const char *src, size_t siz);
而使用 strlcpy,就不需要我们去手动负责 \0 了,仅需要把 sizeof(dst) 告之 strlcpy 即可:
strlcpy(path, src, sizeof(path));
len = strlen(path);
if ( len >= sizeof(path) )
printf("src is truncated.");
并且 strlcpy 传回的是 strlen(str),因此我们也很方便的可以判断数据是否被截断。
[* 一点点历史 *]
strlcpy 并不属于 ANSI C,至今也还不是标准。
strlcpy 来源于 OpenBSD 2.4,之后很多 unix-like 系统的 libc 中都加入了 strlcpy 函数,我个人在 FreeBSD、Linux 里面都找到了 strlcpy。(Linux使用的是 glibc,glibc里面有 strlcpy,则所有的 Linux 版本也都应该有 strlcpy)
但 Windows 下是没有 strlcpy 的,对应的是strcpy_s函数。
这个库在VC下是非常好用的,在好朋友的帮助下快速安装成功,这里做一下详细的步骤:
1.先下载Codejock.Xtreme.Toolkit.Pro.v13.2.1
2.安装的时候一路下一步
3.安装完以后会出一个新的配置窗口,这里你可以跟据需要是否选择UNICODE的版本
或者DLL 还是静态的LIB方式这里推荐LIB用LIB发布时不用带DLL了。
4.选择完成后,会弹出一个CMD窗口开始自己编译,这个过程 比较慢慢5分钟到10分钟不等
5.编译完后,你可以编译例子了,编译前做一下设置,在Project->Setting->General->Microsoft Foundation Classes中选择
Use MFC in a Static Library就可以编译了。
-
静态链接注意事项
1)程序动态链接到MFC库情况下,静态链接到XTP
2)程序动态静态到MFC库情况下,静态链接到XTP
不论哪种情况,配置_XTP_STATICLINK都可以使能静态链接,静态链接时记得在你工程里的rc2文件中,包含如下语句,否则调用XTP中的资源时会发生异常。
#include <XTToolkitPro.rc>
由于toolkit安装时,编译生成相应的lib非常慢,我当时只生成了静态的库。分别是:
ToolkitPro1321vc80S.lib
ToolkitPro1321vc80SD.lib
ToolkitPro1321vc80SU.lib
ToolkitPro1321vc80SUD.lib
其中,S表示static, 后面的D表示debug, U表示unicode,
//编译的时候,是有3个选项的
a) As DLL
b) As Static Library, for using MFC as DLL
c) As Static Library, for using MFS as static library.
//我选择了b,所以,生成以上libs:S.lib, SD.lib, SU.lib, SUD.lib.
//如果选择了c),则会生成DS.lib, DSD.lib, DSU.lib, DSUD.lib//前面的D表示using mfc as dll
//如果选择了a),则会生成" ".dll, D.dll, U.dll, UD.dll.
假定xtreme toolkit安装在:C:\Program Files下。
则比如会有如下目录存在:C:\Program Files\Codejock Software\MFC\Xtreme ToolkitPro v13.2.1\Source
然后在项目属性中需要修改如下设置:
a) c/c++ | 常规 | 附加包含目录,增加“C:\Program Files\Codejock Software\MFC\Xtreme ToolkitPro v13.2.1\Source”
b) 链接器 | 常规 | 附加库目录:增加“C:Program Files\Codejock Software\MFC\Xtreme ToolkitPro v13.2.1\Lib\vc80”
c) 链接器 | 输入 | 忽略特定库,增加“libcd.lib”。如果遵照文档,还需要忽略更多。我这就先填这个了。
d) 资源 | 常规 | 附加包含目录,增加“C:\Program Files\Codejock Software\MFC\Xtreme ToolkitPro v13.2.1\Source”
然后,在代码中需要进行如下修改:
a) 在stdafx.h末尾增加 #include "XTToolkitPro.h"
b) 在xx.rc2末尾增加 #include "XTToolkitPro.rc"
c) 然后就是在mainfrm.h中的声明中加入继承关系:
class CMainFrame : public CXTPMDIFrameWnd
然后就可以顺利编译通过了。
注意:千万不要将xtreme toolkit的任何东西加入到项目中。我之前就把XTPResource.h, XTToolkitPro.h, XTToolkitPro.rc加入到项目中,然后报cvt1100:重复资源,type:BITMAP,...错误。
-
动态编译注意事项
其实,和静态编译很像。静态编译可以参考我之前的“xtreme toolkit静态编译注意事项 ”只不过需要xtreme toolkit的.dll拷贝到system32下。或者放在exe的输出目录下。
|
警告较为简单,主要是使用了原来RTL中字符串处理函数如strcpy等现在被认为不安全的函数造成的。
而错误就让人纳闷了,该错误为: mt.exe : general error c101008d: Failed to write the updated manifest to the resource of file ".\Release\jiaoshi.exe". f 其最终原因已查明见最后一部分 上网找了一下,只有英文网页有相关讨论,懒得去翻译了。中文网页中倒是有一个,而且有解决办法,那就是在项目属性——配置属性——链接器——清单文件中, 把生成清单改成否就行了。 经过进一步的探索,发现在项目属性——配置属性——清单工具——输入和输出中,把嵌入清单改成否也行。 然而,问题虽然解决了,原因却还不明。因为,既然生成清单和嵌入清单是微软的默认设置,为什么却会出现错误呢?第一种方法的作者认为清单是.net程序 (托管程序)需要的,所以去掉后便能正常生成了,然而后一种方法仍然生成了清单,而且也能正常运行又作何解,而且微软的说明中也没有提到清单只是为CLR 程序准备的,而是应该所有程序都要加上清单。 更新:还有一种更简单的方法:如果编译前将所有打开的与该项目有关的文件都关掉,也可以解决问题。 最终原因已查明 由于VC需要把生成的文件中嵌入MANIFEST文件,而由于杀毒软件之类的会握有这个文件句柄(因为杀毒软件 发现这个EXE,DLL正在读写),于是VC就写不进去了,方法是暂时关闭杀毒软件,这样就不用 改写MANIFECT 我用的是瑞星杀毒软件,有这个问题. Hi, This might be due to antivirus holding open handle on the exe generated and thus when VS tries to embed the manifest it fails with access denied. Is Antivirus running on your machine? Does this repro with Antivirus turned off? If above is not the case can you provide us with a repro. Thanks, VC++ Team |
微软的Visual Studio 2005 Service Pack 1 (SP1) 年前就发布了, 年前终于有点时间了,于是装了一下VS2005 sp1,看看到底有什么好东西。这次发布的语言版本包括十个国家的语言,可谓全上阵,容量为413M修正了许多bug,其中包括了很多人最最关心中文的Web Application Project问题。
VS2005 SP1下载地址:
VS2005 SP1英文正式版 431M
VS2005 SP1中文正式版 440M
VS2005 SP1号称是M$有史以来最bt的补丁,据说有人装了3个多小时还在收集信息……,为了让大家少走弯路,现总结一下供大家参考。
安装注意事项:
1.安装时间会非常长,如果机器配置不是很高的话需要2个小时,一般的话也需要1个小时左右所以请耐心等待。
2.安装前检查你的c盘是否有大于3G的空余空间,因为安装的时候会释放很多临时文件在c盘,如果不能保证大于3g的话可能会发生异常错误,不过我在安装的时候没有占用3G这么恐怖,不过也起码占用了1G多的空间。
3.如果你以前安装过web application project,在安装vs2005 sp1以前请先把他卸载,不然不能成功安装vs2005 sp1
4.vs2005sp1的补丁包是把你机器上所有单独装过vs2005的版本的补丁都打上,如果机器上装了Visual Web Developer Express和Visual Studio team sutie,那么他就要运行安装补丁两次.
安装步骤:
一.解决数字签名认证的问题
1. 单击 开始单击 运行 , 键入 控件 secpol.msc , 然后单击 确定 。
2. 双击 本地安全策略。
3. 单击 软件限制策略。
注意 如果列出, 没有软件限制右键单击 软件限制策略 , 然后单击 新建策略 。
4. 在 对象类型 , 双击 强制 。
5. 单击 除本地管理员以外的所有用户 , 并单击 确定 。
6. 重新启动计算机。
要点: 之后在执行以上步骤, 本地管理员可安装 .msi 软件包或 .msp 包。 通过以下以前步骤之后安装程序包, 重置强制级别。 在步骤 5, 单击 所有用户 代替 除本地管理员以外的所有用户。
二.执行批处理文件来安装
reg export HKLM\Software\Policies\Microsoft\Windows\Installer installer.reg
reg add HKLM\Software\Policies\Microsoft\Windows\Installer /v MaxPatchCacheSize /t REG_DWORD /d 0 /f
net stop msiserver
start /wait VS80sp1-KB926604-X86-CHS.exe
reg delete HKLM\Software\Policies\Microsoft\Windows\Installer /v MaxPatchCacheSize /f
reg import installer.reg
net stop msiserver
del /q installer.reg 2>nul
将上面的保存为InstallVS2005SP1.bat,再执行该文件以启动安装补丁,并可禁用缓存。
1 引言
以Windows CE 为操作系统的掌上电脑(如PocketPC或HPC),除具备PC的功能外,还具备很强的自身控制能力。Windows CE API超越微软其他操作系统的 API的一个方面是他提供了一个强有力的通知接口(Notification API),该接口允许应用程式自己安排自己在某个确定的时间运行,或在某个系统事件发生时运行,这使得我们能够应用他来设计研发各种高级控制程式,比如按时间或预订的事件来自动开启/关闭电脑,或按时间或预订的事件来自动开启/关闭一个或多个应用程式,乃至控制一个或多个应用程式的运行流程。
2 通知API的解析
所谓通知是操作系统对发生的某个事件所发出的响应信号。Windows CE对定时器事件发出的响应信号即“定时器事件通知”,而对系统事件发出的响应信号即“系统事件通知”。定时器事件表明已到达指定时间,系统事件表明发生了系统级事件,如添加或删除了某设备,系统时间更改了,和其他设备发生同步,检测到RS232口连接等。假如我们要在给定的时间直接运行某个应用程式(不用用户干涉),就能够简单地使用“定时器事件通知”,而当我们需要监控一些系统事件的发生时,就要使用″系统事件通知″。特别需要强调的是:除非不安装电池或处于死机状态,否则掌上电脑的电源始终不关闭;当用户按下关闭电源按钮或不使用时,机器也只是处于休眠状态而并没有真正切断电源(在休眠状态下,他仅提供能保持其时钟、应用程式及RAM中存储的数据所需的最少能量)。因此,对于注册使用“定时器事件通知”或″系统事件通知″的程式,即使系统是关闭的,当定时器事件到达时或发生系统事件时,要运行的应用程式也会启动。
上述的“定时器事件通知”的使用虽然方便,但有时不能满足用户的需要。比如对于复杂的控制流程,不但到了指定时间要运行应用程式,而且要根据用户的不同反应进行不同的控制。因此,Windows CE还提供了第三种通知接口: ″用户通知″。″用户通知″也使用定时器事件,但和“定时器事件通知”不同,“用户通知”发生时必须被用户确认,从而到了“用户通知”指定的时间可根据用户的不同反应进行不同的流程控制。比如,当用户仅需在指定的时间作一下提示,则使用“用户通知”的应用程式可设计为以四种方式(闪动LED,振动设备,播放声音和显示提示框)提示用户,而且用户可随时更改提示方式。又比如,当用户需要不但在指定的时间作一下提示,还要在用户做出确认后才使程式继续运行,这就只能使用“用户通知”而不能使用“定时器事件通知”。
当我们把自己研发的程式注册到特定的事件通知后,操作系统将在该事件发生时生成一个通知。系统使用通知和用户和其他程式通信。Windows CE共提供了六个通知接口:
CeSetUserNotification
CeGetUserNotificationPreferences
CeClearUserNotification
CeHandleAppNotifications
CeRunAppAtTime和CeRunAppAtEvent
前四个为″用户通知″所使用,后两个分别为“定时器事件通知”和″系统事件通知″所使用。下面分别介绍这六个API的使用方法:
(1)函数CeSetUserNotification用于注册用户通知,其原型是:
HANDLE CeSetUserNotification(HANDLE hNotification,
TCHAR* pwszAppName,
SYSTEMTIME* lpTime,
PCE_USER_NOTIFICATION lpUserNotification);
其参数含义是:
句柄hNotification配置为0表示创建一个新的通知,而要更改已注册的通知则配置hNotification为希望更改的用户通知的句柄(这个句柄是由注册用户通知的程式在调用CeSetUserNotification后的返回值);
pwszAppName是该应用程式的名称,当通知发生时,该应用程式的小图标将在任务栏上显示;lpTime是个指向SYSTEMTIME结构指针,该结构指定了通知发生的时间;lpUserNotification也是个结构指针,他指向PCE_USER_NOTIFICATION结构, Windows CE用该结构描述用户怎样被通知,这个结构的定义是:
typedef struct UserNotificationType {
DWORD ActionFlags;
TCHAR* pwszDialogTitle;
TCHAR* pwszDialogText;
TCHAR* pwszSound;
DWORD nMaxSound;
DWORD dwReserved;
} CE_USER_NOTIFICATION, *PCE_USER_NOTIFICATION;
其中变量ActionFlags是一组定义了在到达指定的时间时以何种形式提示用户的标志:
PUN_LED(闪动屏幕),
PUN_VIBRATE(振动设备),
PUN_DIALOG(显示对话框),
PUN_SOUND(播放声音文档)
PUN_REPEAT(重复声音文档10到15秒),
他能够是上述标志的任意组合。从程式调用CeSetUserNotification开始到用户得到通知的这一时间段中,通知一直处于活动状态。如要在他超时之前修改此通知,程式可通过再次调用CeSetUserNotification来实现
(2)调用CeGetUserNotificationPreferences,函数原型为:
BOOL CeGetUserNotificationPreferences(HWND hWndParent, PCE_USER_NOTIFICATION lpNotification);
这个函数可配置用户通知,以便让用户能有修改提示方式的机会,其中hWndParent是提示框父窗口的窗口句柄。
(3)调用CeClearUserNotification能够实现在用户通知到达之前清除他。
(4)调用CeHandleAppNotifications 函数以确认用户通知。用户通知到达后需要确认。对于显示提示框的通知,确认的方式是点击提示框的确定按钮或按下设备外壳上的通知按钮(此时用户通知仅起到提示的作用,不启动应用程式);对于不显示提示框的通知,系统将在任务栏上显示注册该通知的程式的图标,当用户点击此图标时系统将启动相应的应用程式的一个实例(系统还传递一个命令行参数lpCmdLine以表明为什么应用程式会运行,该参数是串 APP_RUN_TO_HANDLE_NOTIFICATION加空格加通知的句柄)。对于不显示提示框的用户通知,在应用程式中要调用 CeHandleAppNotifications 函数来确认通知,该函数将任何用于应用程式的活动通知都标记为已处理,并删除任务栏上的图标。在实际编码时还要考虑是否有该应用程式的另一个实例在运行,如有,则应向他发送一个自定义消息由该实例处理此通知并终止自身以节省资源。
(5)调用CeRunAppAtTime生成“定时器事件通知”,函数原型为:
BOOL CeRunAppAtTime(TCHAR* pwszAppName, SYSTEMTIME* lpTime );
其参数含义是:
lpTime是个结构指针,该结构指定了运行应用程式的时间;
pwszAppName是要运行的应用程式的名称。由于只是在给定的时间自动运行某个应用程式,因此比较简单。要修改“定时器事件通知”,只要再次调用CeRunAppAtTime。因为后一次调用CeRunAppAtTime将替换前一次的通知。要清除“定时器事件通知”,只要在调用CeRunAppAtTime时,在参数lpTime中传递一个NULL指针。
(6)调用CeRunAppAtEvent生成″系统事件通知″,函数原型:
BOOL CeRunAppAtEvent(TCHAR* pwszAppName, LONG lWhichEvent);
其参数含义是:
pwszAppName是要运行的应用程式的名称;
lWhichEvent 是指出要监控哪一个事件,标志常量如下:
NOTIFICATION_EVENT_NONE 清除事件通知
NOTIFICATION_EVENT_SYNC_END 同步完成通知
NOTIFICATION_EVENT_DEVICE_CHANGE 添加或删除设备通知
NOTIFICATION_EVENT_RS232_DETECTED 检测到RS232连接通知
NOTIFICATION_EVENT_TIME_CHANGE 系统时间更改通知
NOTIFICATION_EVENT_RESTORE_END 设备恢复完成通知
要停止响应系统事件通知,应用程式只要再次调用CeRunAppAtEvent,并在lWhichEvent参数中传递其名称和NOTIFICATION_EVENT_NONE。
3 通知API的使用代码实例
#include <Notify.h>
CE_USER_NOTIFICATION g_ceun;
(1) 对CE_USER_NOTIFICATION结构初始化的代码片段.
memset (&g_ceun, sizeof(g_ceun));
g_ceun.ActionFlags = PUN_DIALOG;
g_ceun.pwszDialogTitle = szDlgTitle;
g_ceun.pwszDialogText = szDlgText;
g_ceun.pwszSound = szSound;
g_ceun.nMaxSound = sizeof(szSound);
(2)注册用户通知的代码片段:
SYSTEMTIME st;
GetLocalTime (&st);
GetModuleFileName (hInst, szExeName,sizeof(szExeName));
hNotify = CeSetUserNotification (0, szExeName,&st, &g_ceun);
(3)配置用户通知的代码段:
CeGetUserNotificationPreferences(hWnd, &g_ceun);
(4)使用CeHandleAppNotifications并只运行一个实例(为节省资源)的代码段 :
//判断应用程式的启动是否源于用户通知
If (lstrcmp(szText,APP_RUN_TO_HANDEL_NOTIFICATION==0)
GetModuleFileName (hInst, szText, sizeof(szText));
CeHandleAppNotifications (szText);
hNotify =(HANDLE)_wtol(pPtr); //取通知的句柄
//检查是否已有应用程式的实例在运行
hWnd = FindWindow(NULL, szAppName);
if(hWnd)//如有,向他发送一个自定义消息,由他处理此用户通知
SendMessage(hWnd, MYMSG_TELLNOTIFY, 0, (LPARM)hNotify);
//终止自身, 代码略去
(5)使用“定时器事件通知”的代码段:
SYSTEMTIME st;
GetLocalTime (&st);
GetModuleFileName (hInst, szExeName, sizeof(szExeName));
CeRunAppAtTime (szExeName, &st);
(6)使用“系统事件通知”的代码段:
LONG lEvent;
if (IsDlgButtonChecked(hWnd, IDC_SYNC_END) ==1)
lEvent != NOTIFICATION_EVENT_SYNC_END;
if (IsDlgButtonChecked(hWnd, IDC_SERIAL_DETECT)==1)
lEvent !=NOTIFICATION_EVENT_RS232_DETECTED;
if (IsDlgButtonChecked(hWnd, IDC_DEVICE_CHANGE)== 1)
lEvent != NOTIFICATION_EVENT_DEVICE_CHANGE;
if (IsDlgButtonChecked(hWnd, IDC_TIME_CHANGE)==1)
lEvent != NOTIFACTION_EVENT_TIME_CHANGE;
if (IsDlgButtionChecked(hWnd, IDC_RESTORE_END) ==1)
lEvent != NOTIFICATION_EVENT_RESTORE_END;
GetModuleFileName (hInst, szExeName, sizeof(szExeName);
CeRunAPpAtEvent(szExeName, lEvent);
以上我们介绍了Windows CE Notification API的使用方法,关于Windows CE应用程式研发环境的使用。
- 如何去掉任务栏上的Notification
分为两种情况:
一、自己用SHNotificationAdd 创建出来的Notification,当然是用SHNotificationRemove来去掉了。
二、系统的Notification(比方说未接电话、新短信之类的),可以用以下的方法:
通过修改注册表[HKEY_CURRENT_USER\System\State]下一些相关的键值。e.g.
要去掉未接电话Notificatio,修改如下:
[HKEY_CURRENT_USER\System\State\Phone]
"Missed Call Count"=0
去掉新短消息Notification,修改如下:
[HKEY_CURRENT_USER\System\State\Messages\sms\Unread]
"Count"=0
上次说修改键值来达到删除Notification的目的,但可能会产生副作用,比方说Today Screen里面的计数可能会因为这个注册表的改动而显示了错误的值。
[HKEY_CURRENT_USER\System\State\Messages\sms\Unread]
"Count"=0
后来在网上搜了一下,codeproject上有个人是通过SHNotificationRemove实现的,我修改了一下,sample code如下:
void RemoveNewMessageNotification()
{
SHNOTIFICATIONDATA shnd;
CLSID clsid;
LRESULT result;
DWORD dwID = 0;
if (0 == CLSIDFromString(TEXT("{A877D65B-239C-47a7-9304-0D347F580408}"), &clsid))
{
memset(&shnd, 0, sizeof(shnd));
shnd.cbStruct = sizeof(SHNOTIFICATIONDATA);
do
{
result = SHNotificationGetData(&clsid,dwID,&shnd);
if (ERROR_SUCCESS == result)
{
SHNotificationRemove(&clsid,dwID);
if (shnd.pszHTML)
{
free((void *) shnd.pszHTML);
shnd.pszHTML = NULL;
}
if (shnd.pszTitle)
{
free((void *) shnd.pszTitle);
shnd.pszTitle = NULL;
}
}
else
dwID++;
} while ((ERROR_SUCCESS != result) && (dwID < 20000));
}
}
- 系统菜单扩展
系统菜单的扩展是通过COM来实现的,主要工作有两个:代码实现和创建注册表键值。
1. 代码实现:
实现接口为IObjectWithSite和IContextMenu。当然,还包括IClassFactory,它帮助生成指定CLSID的对象,它的代码其实都是千篇一律,把sample中的代码稍微修改一下就可以用了。
具体的实现请参考<SDK安装路径>\Samples\Common\CPP\Win32\InboxMenuExtensibility下的一个例子,它扩展了tmail.exe(Messaging) message list下的menu。
关键的实现主要还是在QueryContextMenu和InvokeCommand,QueryContextMenu一般用来添加菜单的操作,InvokeCommand是用于点击你刚添加的菜单项时的响应。值得一提的是QueryContextMenu的返回值,应设为你添加的菜单项个数,一般实现为return MAKE_HRESULT(SEVERITY_SUCCESS, 0, nMenuAdded); 这样系统就知道你到底添加了多少菜单,别人调用QueryContextMenu的时候idCmdFirst才是正确的。比方说其它应用程序恰好也在这个菜单里加了菜单项,就不会引起冲突(冲突就是说比方说点了你的菜单项,结果响应的却是其它软件加的菜单项的命令)。而且Smart Phone也比较特殊,每进到一个popup menu就会响应一下QueryContextMenu,所以一般都是计算当前弹出菜单的菜单项个数来避免每次进去一个popup menu都添加菜单项。
2. 注册表键值的创建:
[HKEY_CLASSES_ROOT\CLSID]下创建一个key,name为你所用的CLSID,再在这个key下面创建一个key,name为InProcServer32,默认值设为你的DLL。
[HKEY_LOCAL_MACHINE\Software\Microsoft\Shell\Extensions\ContextMenus]下创建的key需要根据你扩展菜单的位置来决定的,具体的值请看SDK document中的Menu Overview。
State and Notifications Broker
State and Notifications Broker提供了一个在注册表中存储系统和应用程序信息的机制和一个存储信息改变的通知系统。它可以用于监控系统中的任何注册表键值。通知种类包括以下几种:
System state information, such as features present (camera, keyboard), active application, cradled state, battery state.
Message information, such as count of unread, last received, account info.
Tasks and appointments information, such as upcoming, overdue, location.
Windows Media Player information, such as currently playing album, artist.
Phone information, such as signal information, operator information, call information, multiline information.
Connections information, related to bluetooth, cellular, network, modem, etc.
snapi.h中包含了需要监控的注册表Key、Path、Value Name和Mask,比方说你想知道现在的phone处在一个什么状态,可以看到snapi.h中有这么一段:
////////////////////////////////////////////////////////////////////////////////
// PhoneRoaming
// Gets a value indicating whether the phone is currently in roaming mode.
#define SN_PHONEROAMING_ROOT HKEY_LOCAL_MACHINE
#define SN_PHONEROAMING_PATH TEXT("System\\State\\Phone")
#define SN_PHONEROAMING_VALUE TEXT("Status")
#define SN_PHONEROAMING_BITMASK 0x200
当键值[HKLM\System\State\Phone] "Status"值的0x200这一位上为1时,说明phone处在漫游状态。
那么,如何监控注册表键值呢?可以用RegistryNotifyApp、RegistryNotifyWindow、RegistryNotifyCallback等API。以下是Sample Code:
#include <snapi.h>
#include <regext.h>
#define UM_REGNOTIFY WM_USER+1000
// Global Variables:
HREGNOTIFY g_hRegNotify;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
NOTIFICATIONCONDITION nc;
ZeroMemory(&nc, sizeof(NOTIFICATIONCONDITION));
nc.ctComparisonType = REG_CT_EQUAL;
nc.dwMask = 0x200;
nc.TargetValue.dw = 0x200;
RegistryNotifyWindow(SN_PHONEROAMING_ROOT, SN_PHONEROAMING_PATH, SN_PHONEROAMING_VALUE, hWnd, UM_REGNOTIFY, 0, &nc, &g_hRegNotify);
break;
case UM_REGNOTIFY:
MessageBox(hWnd, _T("Phone is roaming!"), _T("Notification Broker"), MB_OK);
break;
case WM_DESTROY:
RegistryCloseNotification(g_hRegNotify);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
本文来自CSDN博客,转载请标明出处:http://www.cnblogs.com/Kane_zzt/admin/file:///C:/Documents%20and%20Settings/Kane/桌面/WINCE/Windows%20CE%20Notification%20API的使用方法.mht
