Smartphone 2003开发心得

buptpki 2004-07-08 01:17:21
1. 如何控制系统的重启或者关机
可以调用函数ExitWindowsEx, 在Win CE上,这属于Undocument API,虽然没有公开,但是可以使用。
/*
#define EWX_LOGOFF 0
#define EWX_SHUTDOWN 1
#define EWX_REBOOT 2 重启
#define EWX_FORCE 4
#define EWX_POWEROFF 8 关机
*/
extern "C" BOOL ExitWindowsEx(UINT uFlags, DWORD dwReason);
ExitWindowsEx(EWX_REBOOT, 0); //重启
ExitWindowsEx(EWX_POWEROFF, 0); //关机


2.动态链接库注入机制
在Windows 2000中提供了很多DLL注入机制:
使用注册表来插入DLL
使用Windows挂钩来插入DLL
使用远程线程来插入DLL
使用特洛伊DLL来插入DLL
将DLL作为调试程序来插入
但是,作为嵌入式操作系统,提供的接口少多了,在Smartphone 2002 (基于Windows CE 3.0) 中并不提供动态链接库注入机制,在Smartphone 2003才提供,使用Dll Inject机制,可以在注册表中设置关键字HKEY_LOCAL_MACHINE\System\Kernel\InjectDLL的值,该关键字数据类型为REG_MULTI_SZ,包含一个DLL文件名或者一组DLL文件名(用逗号分离),列出的每个DLL文件名都可以包含一个路径。当系统启动时,列在该键值中的每个DLL会映射到系统加载的每个进程中(Nk.exe和Filesys.exe除外)。
当进程创建时,系统将分配进程的地址空间,接着将可执行文件映像和所有需要的DLL文件(包括HKEY_LOCAL_MACHINE\System\Kernel\InjectDLL注册的所有DLL)映像映射到进程的地址空间中。然后才创建进程的主线程,并使用该线程调用每个DLL的带有DLL_PROCESS_ATTACH值的DllMain函数。当已经映射的所有DLL都对通知信息作出响应后,系统将使进程的主线程开始执行可执行模块的C/C++ Runtime启动代码,然后执行可执行模块的进入点函数(WinMain)。
如果DLL(不包括HKEY_LOCAL_MACHINE\System\Kernel\InjectDLL注册的任何DLL)的任何一个DllMain函数返回FALSE,表明初始化没有取得成功,系统便终止整个进程的运行,从它的地址空间中删除所有文件映像,给使用户显示一个消息框,说明进程无法启动运行。
前面说过,NK.exe和Filesys.exe不受该键值的影响,这主要是由于Windows CE的启动过程造成的[4]。
当系统复位时,CPU将跳转到NK.exe的入口。NK.exe是Windows CE的核心模块,入口的代码实际上是由OEM编写的,而不是Microsoft,因为不同的平台,硬件也不一定相同,因此Windows CE需要OEM编写一些特定的初始化代码,这些代码被并入内核的硬件抽象层(HAL, Hardware Abstraction Layer),当OEM为特定硬件平台建立系统时,HAL将和Windows CE内核代码静态连接而产生NK.exe。
Nk.exe在设计时与文件系统(包括注册表系统、数据库系统)是独立的,也就是说NK.exe不会负责文件系统的初始化,但是在NK.exe初始化过程中要用到注册表数据,因此,当NK.exe启动后,将创建有名事件(Named Events)SYSTEM/FSReady,加载并运行系统程序Filesys.exe, 然后转入睡眠等待事件变为已通知状态。
Filesys.exe负责管理文件系统、数据库函数和注册表。Filesys.exe被加载后,将检测这次启动是冷启动还是热启动。冷启动和热启动的最大区别就是内存中是否存在已初始化的文件系统。
如果是热启动,内存中保留了上次使用的数据,Filesys.exe将使用这个已初始化的文件系统。如果是冷启动,Filesys创建一个文件系统,将空内存文件系统和ROM中的文件合并在一起。
当文件系统存在后,Filesys.exe会创建默认的数据库和注册表,初始化本地语言支持模块(National Language Support, NLS)。
接着Filesys.exe将事件SYSTEM/FSReady置为已通知状态,然后转为睡眠状态等待Nk.exe完成初始化后将其唤醒。
由于事件SYSTEM/FSReady置为已通知状态,NK.exe被唤醒接着运行,此时,注册表已被Filesys.exe创建,NK.exe根据注册表的键值继续运行,如配置虚拟内存管理器,初始化默认处理程序的中断向量表,初始化端口等等,当然,最重要的是通知Filesys.exe可以继续运行。
Filesys.exe被唤醒后,接着进行后续的初始化工作,如根据列在注册表HKEY_LOCAL_MACHINE\System\Events中的名字创建有名事件SYSTEM/PowerManagerReady,SYSTEM/GweApiSetReady,SYSTEM/ShellAPIReady,SYSTEM/BatteryAPIsReady等等,这些有名事件的初始状态都是未通知状态,后面有相应的程序来对其进行设置,如图形窗口和事件管理器(GWE,Graphics Windowing and Event Manager)初始化图形API后,会把有名事件SYSTEM/GweApiSetReady值为通知状态,系统知道图形API有效,可以进行图形绘制。
所以,在Nk.exe和Filesys.exe被加载前,系统不可能去搜索注册表,更谈不上Dll Inject钩子机制。
关于Windows CE的详细的启动步骤请参考MSDN。
...全文
470 点赞 收藏 24
写回复
24 条回复
buptpki 2004年09月28日
to:nbcool(载舟之水)
是啊,好久没来这了,一直在http://www.91mobile.com/bbs/index.asp泡着,
借着这段时间,完成了API SPY for Smartphone 1.0,并且用它破解了20多个运行在Smartphone上的软件,这些破解后的软件我都放在这个网上了。

出书,这个Idea不错,我也曾经考虑过,可是时间上很难掌握。当然,关键是书出来后是否有人看,在国内,大家都在抵制Windows的产品,做Smartphone开发的还是不多,这也限制了开发人员的数量。

当然,这是我的想法,不知道nbcool(载舟之水) 怎样!
回复 点赞
mgphuang 2004年09月26日
mark
回复 点赞
无聊客 2004年09月15日
我就是用Entrek toolbox查的,资源泄露用Entrek toolbox(即使是未注册版也很好查),内存泄露未注册版可以用注释代码的方法来查,
回复 点赞
crazyeagle 2004年09月15日
收到!!谢了!
回复 点赞
heweixing_77 2004年09月14日
mark
回复 点赞
nothingneed 2004年09月14日
各位老兄不知道用什么来测试smartphone程序中的memoryleak呀?我下得Entrek toolbox只有掩饰版,极度郁闷。。。
回复 点赞
lenux 2004年09月13日
谢谢,收藏
回复 点赞
无聊客 2004年09月12日
学习
回复 点赞
载舟之水 2004年09月12日
我是从Smartphone2002 开始开发的,
不幸运的是,连Win32 SDK都是从零开始学起的,花了我一星期的时间。
自学的过程确实很艰难。
buptpki,你很久没来了,有机会的话,合作写本书吧。
回复 点赞
jians 2004年09月12日
嗨,您好,向您请教个问题,我原来做有一个程序是在POCKET PC上的
现在想兼容SMARTPHONE,但原来的代码大量使用了CString等MFC的类
编译的时候报错打不开afxwin.h等,才发现smartphone的SDK根本没有带有MFC
的支持,是这样的吗?有什么办法请指点一下,万分感谢
回复 点赞
91program 2004年07月09日
mark!
回复 点赞
buptpki 2004年07月09日
8. 清空Contacts和SIM中的所有记录。
实现时,没有采用POOM接口,直接采用数据库接口,一则是速度奇快,二则是让大家看看数据库Tips 4中列出的数据库是如何操作的。


VOID DeleteAllContactsRecord()
{
HANDLE hContactsDB;
CEGUID guidContacts;
CEOID ContactsDbOID;
CEOID oid;

CREATE_INVALIDGUID(&guidContacts);
ContactsDbOID = 0;
hContactsDB = CeOpenDatabaseEx(&guidContacts,
&ContactsDbOID,
TEXT("Contacts Database"),
0X4013001F,
0, //CEDB_AUTOINCREMENT
NULL);

if(INVALID_HANDLE_VALUE != hContactsDB)
{
while(oid = CeSeekDatabaseEx(hContactsDB, CEDB_SEEK_BEGINNING, 0, 0, NULL))
{
CeDeleteRecord(hContactsDB, oid);
}

CloseHandle(hContactsDB);
}
}

VOID DeleteAllSIMRecord()
{
HANDLE hSimDB;
CEGUID guidSim;
CEOID SimDbOID;
CEOID oid;

CREATE_INVALIDGUID(&guidSim);
SimDbOID = 0;
hSimDB = CeOpenDatabaseEx(&guidSim,
&SimDbOID,
TEXT("\\SimEntries"),
0x0002001F,
0, //CEDB_AUTOINCREMENT
NULL);

if(INVALID_HANDLE_VALUE != hSimDB)
{
while(CeSeekDatabaseEx(hSimDB, CEDB_SEEK_BEGINNING, 0, 0, NULL))
{
CeDeleteRecord(hSimDB, oid);
}

CloseHandle(hSimDB);
}
}

类似,你可以clog.db数据库,把来电记录给删除。见前面Tips 4.
回复 点赞
buptpki 2004年07月09日
7. 在UI实现渐进颜色处理(或者说加入灯光效应)

大家可能都知道现在的Windows支持渐进标题栏,就像加入了灯光处理一样,每块地方的颜色都不一样,那么如何实现了,说白了,无非采用差分处理,在起始颜色和终点颜色中插入一系列中间过渡颜色,让人眼产生错觉。当然这个算法实现起来比较简单,但是微软已经提供了GradientFill函数,不用自己去实现了,这个函数原型是:
GradientFill(
HDC hdc,
PTRIVERTEX pVertex,
ULONG nVertex, // 注意pVertex可以是一个数组,大小用nVertex表示
PVOID pMesh,
ULONG nCount, // 同上pMesh可以是一个数组,大小用nCount表示
ULONG ulMode // GRADIENT_FILL_RECT_H or GRADIENT_FILL_RECT_V
);

其中用到两个结构,大家一看就明白,
typedef struct _TRIVERTEX {
LONG x;
LONG y;
COLOR16 Red; // RGB分量值
COLOR16 Green;
COLOR16 Blue;
COLOR16 Alpha; // 我没有用过这个分量,假若你感兴趣,试试
}TRIVERTEX,*PTRIVERTEX,*LPTRIVERTEX;

typedef struct _GRADIENT_RECT {
ULONG UpperLeft; // 这里的值是pVertex数组的下标。
ULONG LowerRight;
}GRADIENT_RECT, *PGRADIENT_RECT;

多说无意,举个例子大家都明白了:
VOID DrawRainbow(HWND hWnd)
{
HDC hdc;
RECT rect;
TRIVERTEX vert[2] ;
GRADIENT_RECT gRect;

hdc = GetDC(hWnd);
GetClientRect(hWnd, &rect);

vert [0] .x = rect.left;
vert [0] .y = rect.top;
vert [0] .Red = 0x0000;
vert [0] .Green = 0xFF00;
vert [0] .Blue = 0x0000;
vert [0] .Alpha = 0x0000;

vert [1] .x = rect.right;
vert [1] .y = rect.bottom;
vert [1] .Red = 0x0000;
vert [1] .Green = 0x0000;
vert [1] .Blue = 0xFF00;
vert [1] .Alpha = 0x0000;

gRect.UpperLeft = 0;
gRect.LowerRight = 1;

GradientFill(hdc,vert,2,&gRect,1,GRADIENT_FILL_RECT_H);

ReleaseDC(hWnd, hdc);
}

这里实现了一个水平从蓝色变到绿色的区域,其中TRIVERTEX数组大小为2,在结构GRADIENT_RECT中左上角就是TRIVERTEX数组第一个元素,右下角就是TRIVERTEX数组第二个元素。比较简单吧。就像看到灯光效果一样。

来,我们看一个比较复杂了,大家自己去理解吧,这个例子懂了后,这个API你就会了:
VOID DrawRainbow2(HWND hWnd)
{
HDC hdc;
RECT rect;
TRIVERTEX vert[7] ;
GRADIENT_RECT gRect[4];

hdc = GetDC(hWnd);
GetClientRect(hWnd, &rect);

vert [0] .x = rect.left;
vert [0] .y = rect.top;
vert [0] .Red = 0x3100;
vert [0] .Green = 0x9A00;
vert [0] .Blue = 0xEF00;
vert [0] .Alpha = 0x0000;

vert [1] .x = rect.right;
vert [1] .y = 20;//rect.bottom/3;
vert [1] .Red = 0x0000;
vert [1] .Green = 0x4500;
vert [1] .Blue = 0x9C00;
vert [1] .Alpha = 0x0000;

vert [2] .x = rect.left;
vert [2] .y = rect.bottom/3;
vert [2] .Red = 0x0000;
vert [2] .Green = 0x0000;
vert [2] .Blue = 0x0000;
vert [2] .Alpha = 0x0000;

vert [3] .x = rect.right;
vert [3] .y = rect.bottom*2/3;
vert [3] .Red = 0x0000;
vert [3] .Green = 0xFF00;
vert [3] .Blue = 0x0000;
vert [3] .Alpha = 0x0000;

gRect[0].UpperLeft = 0;
gRect[0].LowerRight = 1;


gRect[1].UpperLeft = 2;
gRect[1].LowerRight = 3;

vert [4] .x = rect.left;
vert [4] .y = rect.bottom*2/3;
vert [4] .Red = 0x0000;
vert [4] .Green = 0x0000;
vert [4] .Blue = 0x0000;
vert [4] .Alpha = 0x0000;

vert [5] .x = rect.right/2;
vert [5] .y = rect.bottom*5/6;
vert [5] .Red = 0x0000;
vert [5] .Green = 0x0000;
vert [5] .Blue = 0xFF00;
vert [5] .Alpha = 0x0000;

vert [6] .x = rect.right;
vert [6] .y = rect.bottom;
vert [6] .Red = 0x0000;
vert [6] .Green = 0xFF00;
vert [6] .Blue = 0x0000;
vert [6] .Alpha = 0x0000;

gRect[2].UpperLeft = 4;
gRect[2].LowerRight = 5;
gRect[3].UpperLeft = 5;
gRect[3].LowerRight = 6;

GradientFill(hdc,vert,7,&gRect,4,GRADIENT_FILL_RECT_H);

ReleaseDC(hWnd, hdc);
}
回复 点赞
buptpki 2004年07月09日
给大家推荐一个非常棒的国内网站,http://www.91mobile.com/bbs/index.asp
上面有很多SP2003 & Pocket PC 2003的使用技巧和心得,最棒的是有很多应用软件可以免费下载,这个网站上的高手很多。
就是在这个网站上,我知道了怎么在我的英文SPV上支持中文显示,更妙的是,我现在也能用中文写短消息了,不会被我的那帮哥们嘲笑我XX.
回复 点赞
aawolf 2004年07月09日
支持一下。现在肯把自己的东西拿出来分享的老兄不多了,谢谢。
回复 点赞
buptpki 2004年07月09日
6. 如何在最顶层的Navigate Bar上显示自己订制的ICON
实现这个功能需要调用Shell_NotifyIcon函数,
首先,可以在WM_CREATE消息中加入下列语句:
g_hIcon = CreateStatusIcon(hwnd);
if(g_hIcon)
{
NOTIFYICONDATA nid;

nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = 100;
nid.hIcon = g_hIcon;
nid.uFlags = NIF_ICON;
if(!Shell_NotifyIcon(NIM_ADD, &nid))
{
MessageBox(NULL, L"Error", 0, 0);
}
}
else
{
MessageBox(NULL, L"Error2", 0, 0);
}

其中CreateStatusIcon函数就是调用CreateIconIndirect用来手工创建16x16图标
好像没有其他简单方法用来创建图标

HICON CreateStatusIcon(HWND hWnd)
{
// AND XOR Display
// -------------------------------------------
// 0 0 Black
// 0 1 White
// 1 0 Screen
// 1 1 Screen-Reverse
//
#define STATUSICONWIDTH 16
#define STATUSICONHEIGHT 16
HDC hdc;
HBITMAP hbmMask, hbmFront;
HDC hdcMask, hdcFront;
HBITMAP hbmMaskOld, hbmFrontOld;
ICONINFO ii;
HICON hIcon;
RECT rect;

hdc = GetDC(hWnd);

// When filling the specified rectangle, FillRect does not include
// the rectangle's right and bottom sides. GDI fills a rectangle up to,
// but not including, the right column and bottom row, regardless of
// the current mapping mode.
SetRect(&rect, 0, 0, STATUSICONWIDTH, STATUSICONHEIGHT);

hdcMask = CreateCompatibleDC(hdc);
hbmMask = CreateCompatibleBitmap(hdc, STATUSICONWIDTH, STATUSICONHEIGHT);
hbmMaskOld = (HBITMAP)SelectObject(hdcMask, hbmMask);
FillRect(hdcMask, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
Ellipse(hdcMask, 0, 0, STATUSICONWIDTH-1, STATUSICONHEIGHT-1);
SelectObject(hdcMask, (HPEN)GetStockObject(BLACK_PEN));
SelectObject(hdcMask, (HBRUSH)GetStockObject(BLACK_BRUSH));
Ellipse(hdcMask, 5, 5, 10, 10);

hdcFront = CreateCompatibleDC(hdc);
hbmFront = CreateCompatibleBitmap(hdc, STATUSICONWIDTH, STATUSICONHEIGHT);
hbmFrontOld = (HBITMAP)SelectObject(hdcFront, hbmFront);
FillRect(hdcFront, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));

SelectObject(hdcFront, (HPEN)GetStockObject(WHITE_PEN));
SelectObject(hdcFront, (HBRUSH)GetStockObject(BLACK_BRUSH));
Ellipse(hdcFront, 0, 0, STATUSICONWIDTH-1, STATUSICONHEIGHT-1);
SelectObject(hdcFront, (HBRUSH)GetStockObject(WHITE_BRUSH));
Ellipse(hdcFront, 5, 5, 10, 10);

SelectObject(hdcMask, hbmMaskOld);
SelectObject(hdcFront, hbmFrontOld);
DeleteDC(hdcMask);
DeleteDC(hdcFront);

ii.fIcon = TRUE;
ii.hbmColor = hbmFront;
ii.hbmMask = hbmMask;
ii.xHotspot = 0;
ii.yHotspot = 0;

hIcon = CreateIconIndirect(&ii);

DeleteObject(hbmMask);
DeleteObject(hbmFront);
ReleaseDC(hWnd, hdc);

return hIcon;
#undef STATUSICONWIDTH
#undef STATUSICONHEIGHT
}
现在,Navigate Bar上加上了我定制的图表,一个白色的圆圈,很简单,当然,可以做成彩色,但是好像没有人这么做。

别忘了,在退出程序时要删掉这个ICON
case WM_DESTROY:
if(g_hIcon)
{
NOTIFYICONDATA nid;

nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = 100;
nid.hIcon = g_hIcon;
nid.uFlags = NIF_ICON;
if(!Shell_NotifyIcon(NIM_DELETE, &nid))
{
MessageBox(NULL, L"Error3", 0, 0);
}
DestroyIcon(g_hIcon);
}
回复 点赞
buptpki 2004年07月09日
5. 如何在SP2003上显示或者隐藏等待图标

// Set the cursor as the wait cursor.
SetCursor (LoadCursor (NULL, IDC_WAIT));

// Hide the cursor.
SetCursor (0);
回复 点赞
buptpki 2004年07月09日
本来想把Coredll.dll导出的函数名全部列出来,这样用户根据Ordinal Value可以方便的查到对应的函数名,因为Coredll.dll中的函数只导出了Ordinal Value,但是CSDN发帖限制太多。

4. 列出系统中安装的所有数据库

VOID EnumDataBaseName()
{
FILE *pf;
CEGUID guid;
HANDLE dbEnum;
TCHAR szBuf[256];
CEOID oidDb;
CEOIDINFO oidDbInfo;
UINT VolumeIndex;
UINT DataBaseIndex;

CREATE_INVALIDGUID(&guid);
pf = fopen("\\temp\\volname.txt", "w");

if(pf)
{
VolumeIndex = 0;

fprintf(pf, "Init GUID is %08lX-%08lX-%08lX-%08lX\n\n",
guid.Data1, guid.Data2, guid.Data3, guid.Data4);

while(CeEnumDBVolumes(&guid, szBuf, 256))
{
fprintf(pf, "Volume %3u : ", ++VolumeIndex);
fwprintf(pf, szBuf);
fprintf(pf, "\nGUID is %08lX-%08lX-%08lX-%08lX\n\n",
guid.Data1, guid.Data2, guid.Data3, guid.Data4);

DataBaseIndex = 0;
dbEnum = CeFindFirstDatabaseEx(&guid, 0);
if(dbEnum)
{
while(oidDb = CeFindNextDatabaseEx(dbEnum, &guid))
{
++DataBaseIndex;

if (CeOidGetInfoEx(&guid, oidDb, &oidDbInfo))
{
fprintf(pf, "\tDatabase %3u : ", DataBaseIndex);
fwprintf(pf, oidDbInfo.infDatabase.szDbaseName);
fprintf(pf, "\n");
}
}
CloseHandle(dbEnum);
}
else
{
fprintf(pf, "Invalid handle\n");
}

fprintf(pf, "\n");
}
fclose(pf);
}
else
{
MessageBox(NULL, L"Open file error", L"EnumDataBaseName", MB_OK);
}
}

在我的SPV手机中,输出如下:

Init GUID is FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFF

Volume 1 : SystemHeap
GUID is 00000000-00000000-00000000-00000000


Volume 2 : \Storage\mxip_notify.vol
GUID is 343CC4C1-154CA6EF-6520DAD2-0F943778

Database 1 : DB_notify_queue
Database 2 : DB_notify_events

Volume 3 : \Storage\mxip_lang.vol
GUID is F618DD27-0D3F2492-1BCB24B0-DF873166

Database 1 : \MetabaseOptions
Database 2 : \MetabaseLabels
Database 3 : \FileNameLang

Volume 4 : \Storage\mxip_initdb.vol
GUID is 135488F0-B87ADC98-1B51C8C6-826801C0

Database 1 : MailActiveSync
Database 2 : cidcache.db
Database 3 : SyncStateMoves
Database 4 : clog.db
Database 5 : speed.db
Database 6 : \Categories Database
Database 7 : Contacts Database
Database 8 : \SimEntries
Database 9 : Tasks Database
Database 10 : Appointments Database
Database 11 : \DesktopPositions

Volume 5 : \Storage\cemail.vol
GUID is 8CAF0001-652D0098-08E35CCD-576D6963

Database 1 : fldr3100001b
Database 2 : fldr31000023
Database 3 : fldr31000025
Database 4 : pmailAttachs
Database 5 : fldr31000021
Database 6 : fldr32000020
Database 7 : fldr31000017
Database 8 : fldr31000024
Database 9 : fldr31000022
Database 10 : pmailNamedProps
Database 11 : pmailMsgClasses
Database 12 : pmailServices
Database 13 : pmailOldTables
Database 14 : pmailMsgs
Database 15 : pmailFolders

Volume 6 : \Storage\mxip_system.vol
GUID is 513CC4C1-0E4CA6E1-C420DC7F-C897A562

Database 1 : \ConfigMetabase


回复 点赞
buptpki 2004年07月09日
10 控制程序在开机后只初始化一次。换句话说,程序虽然多次启动关闭,但有些语句只执行一次:

HANDLE hFlagFile=CreateFile(_T(“\\Temp\\FlagFile”),
GENERIC_READ| GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_ARCHIVE,
NULL
);
if(INVALID_HANDLE_VALUE != hFlagFile)
{
CloseHandle(hFlagFile);
g_bFirstRun = TRUE;
}
else
{
g_bFirstRun = FALSE;
}

if(g_bFirstRund)
{
// Do Something
}

为什么目录选择\Temp而不是程序的当前目录, 这是因为放在\Temp目录的文件在下次启动后会自动消失,假若用户随意取掉电池,可能程序来不及删除这个文件,结果重启后\Temp文件会消失,但是程序的当前目录可能会存在,所以Flag文件放到程序的当前目录可能会出现一些意想不到的情况。
当然,放到\Temp目录下有一个文件名重名的情况需要考虑。

当然,控制程序开机后某些代码只运行一次的方法有很多种,但是要经受住用户随意断掉手机电池的考验还是比较少的。目前,我也只想到两种方法,上面就是其中的一种,还有一种比较安全,但是实现起来比较复杂,这里就不再介绍了。
回复 点赞
buptpki 2004年07月09日
在SP2003注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shell\Rai\下面隐藏了一些有趣的主键,如
:MSINBOX
:MSCONTACTS
:MSCALENDAR
:MSTASKS
:MSSettings
:MSHome
:MSStart
:MSTNOTES
等等。
当用户在Start Menu中选择Contacts,系统会查找注册表:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shell\Rai\:MSCONTACTS下的子键”0”,它是一个String Value,包含对应窗口的类名,对于Contacts,它的类名,也就是子键”0”的值就是” Contacts”,系统根据这个类去查找系统冲的窗口,如果匹配,激活它,如果不存在,继续查找子键”1”的值,它是一个String Value,包含启动这个程序的快捷方式,对于Contacts,子键”1”的值是“appman.exe -s Apps :MSCONTACTS tpcutil.dll AMContacts“,我们暂且不考虑这个值具体表达的意思,想想如果把子键”1”改为我们定义的程序,那么在Start Menu中选择Contacts,就会启动我们定义的程序。也就是,如果想扩展系统程序Contacts的功能,只需修改这个键值指向我们定义的程序,一切OK了。事实确实如此。
事实上,不光Start Menu中的程序Inbox, Contacts, Calendar, Tasks, Settings等程序的处理原理同Contacts类似,连Start Menu程序本身也是这样处理的,商业软件Surreal Start就是修改了HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shell\Rai\:MSStart\1的键值,替换了系统的Start程序。
更有趣的是,对于系统一些按键,如录音键的处理方式也是如此。当用户按了录音键,操作系统查找注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shell\Rai\:MSTNOTES\1键值, 这个键值缺省时指向录音程序。这是一个全局事件,不管当前停留在什么窗口,按下录音键都会调用该子健对应的程序。假若你有一个程序想随时随地激活,使用这个键值是最佳选择。
当然,灵活运用这些键值可以使你的程序和系统程序无缝结合到一起,甚至可以为系统作一些扩展功能。

回复 点赞
发动态
发帖子
嵌入开发(WinCE)
创建于2007-09-28

8169

社区成员

4.1w+

社区内容

硬件/嵌入开发 嵌入开发(WinCE)
社区公告
暂无公告