导航

Smartphone 2003开发心得

Posted on 2004-07-14 10:15  黑暗中的舞者  阅读(2251)  评论(0编辑  收藏  举报
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键值, 这个键值缺省时指向录音程序。这是一个全局事件,不管当前停留在什么窗口,按下录音键都会调用该子健对应的程序。假若你有一个程序想随时随地激活,使用这个键值是最佳选择。
当然,灵活运用这些键值可以使你的程序和系统程序无缝结合到一起,甚至可以为系统作一些扩展功能。