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. 列出系统中安装的所有数据库
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-%8lX\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
3. 如何在SP2003上显示或者隐藏等待图标
// Set the cursor as the wait cursor.
SetCursor (LoadCursor (NULL, IDC_WAIT));
// Hide the cursor.
SetCursor (0);
4. 如何在最顶层的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);
}
5. 给大家推荐一个非常棒的国内网站, http://www.91mobile.com/bbs/index.asp
上面有很多SP2003 & Pocket PC 2003的使用技巧和心得,最棒的是有很多应用软件可以免费下载,这个网站上的高手很多。
就是在这个网站上,我知道了怎么在我的英文SPV上支持中文显示,更妙的是,我现在也能用中文写短消息了,不会被我的那帮哥们嘲笑我XX.
6.在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);
}
7.在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键值, 这个键值缺省时指向录音程序。这是一个全局事件,不管当前停留在什么窗口,按下录音键都会调用该子健对应的程序。假若你有一个程序想随时随地激活,使用这个键值是最佳选择。
当然,灵活运用这些键值可以使你的程序和系统程序无缝结合到一起,甚至可以为系统作一些扩展功能。
可以调用函数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. 列出系统中安装的所有数据库
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-%8lX\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
3. 如何在SP2003上显示或者隐藏等待图标
// Set the cursor as the wait cursor.
SetCursor (LoadCursor (NULL, IDC_WAIT));
// Hide the cursor.
SetCursor (0);
4. 如何在最顶层的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);
}
5. 给大家推荐一个非常棒的国内网站, http://www.91mobile.com/bbs/index.asp
上面有很多SP2003 & Pocket PC 2003的使用技巧和心得,最棒的是有很多应用软件可以免费下载,这个网站上的高手很多。
就是在这个网站上,我知道了怎么在我的英文SPV上支持中文显示,更妙的是,我现在也能用中文写短消息了,不会被我的那帮哥们嘲笑我XX.
6.在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);
}
7.在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键值, 这个键值缺省时指向录音程序。这是一个全局事件,不管当前停留在什么窗口,按下录音键都会调用该子健对应的程序。假若你有一个程序想随时随地激活,使用这个键值是最佳选择。
当然,灵活运用这些键值可以使你的程序和系统程序无缝结合到一起,甚至可以为系统作一些扩展功能。