首页 > 代码库 > 转:WinCE驱动开发问题精华集锦

转:WinCE驱动开发问题精华集锦

1、在应用程序中,如何向修改本机的ip 地址等网络参数,并使之立即生效?   
       网络设置保存在注册表中,位置[HKEY_LOCAL_MACHINE\Comm\网卡名称\Parms\TcpIp],例如常见的CS8900网卡设置:   
[HKEY_LOCAL_MACHINE\Comm\CS89001\Parms\TcpIp]   
   "EnableDHCP"=dword:0   
   "DefaultGateway"="192.168.0.1"   
   "DNS"="111.111.111.111"   
   "UseZeroBroadcast"=dword:0   
   "IpAddress"="192.168.0.2"   
   "Subnetmask"="255.255.255.0"   
     设置之后要生效有两种办法:一种热启动,调用KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL),热启动时间很短暂;另外一种调用DevieceIoControl API,传递IOCTL=IOCTL_NDIS_REBIND_ADAPTER。   
   
2、如何向控制面板中那样,修改系统声音的音量   
     调用API waveOutSetVolume(HWAVEOUT, dwVolume ),一般参数1为0。在[HKEY_CURRENT_USER\ControlPanel\Volume]下是系统声音的注册表设置。   
   
3、在应用程序中如何实现jpg、gif图片的显示   
有几种办法:   
1)、在MSDN中搜索标题为“Windows CE .NET Technical Frequently Asked Questions”的文档,其中有一个问题“How can I display JPEG, GIF, and other graphics files?”,下面就是答案。   
2)、如果安装了Windows CE 5.0,一个例子源码位于WINCE500\PUBLIC\GDIEX\SDK\SAMPLES\SIMPLE。   
3)、用IWebBrowser组件实现。   

4、 在应用程序中如何实现系统待机   
void GwesPowerOffSystem(void);   

5、   在WINCE中如何得到网卡MAC地址   
     事实证明,获得物理网卡的MAC地址并没有被统一成一个API或者IOCTL,如果网卡驱动程序没有提供接口的话只能直接访问寄存器获得。读者可以参考目录WINCE500\PUBLIC\COMMON\OAK\DRIVERS\NETCARD里的一些驱动源码。   
   
6、Windows XP Embedded 和Windows CE有何区别   
     简单地说Windows XP Embedded采用Windows XP内核,只能运行在x86处理器上,优点是能够运行PC上现有的应用软件,缺点是授权费太高,标价¥900元;Windows CE采用Windows CE内核,能够运行在多种处理器上,如x86、ARM、SHX、MIPS等,优点是授权费低,最低Core版标价¥30元。缺点是需要单独开发应用软件、定制内核,甚至开发BSP、Driver。   

7、 wince下只是把调制解调器的驱动挂接在了com1,如何将器驱动挂接在com2上?   
     1)、在HLM\drivers\buildin\com2\unimodem下复制和com1一样的数据 
     2)、在HLM\ExtModems\HayesCompat下改写Port为COM2:,再改写FriendlyName为"Hayes Compatible 在 COM2:"。   
   
8、如何定制自己的外壳   
   1)、先开发一个外壳软件,假设名称为MyShell.exe   
   2)、删除注册表[HKEY_LOCAL_MACHINE\init]下如下一行:   
           "Launch50"="explorer.exe"   
     3)、在注册表[HKEY_LOCAL_MACHINE\init]下添加如下一行:   
           "Launch50"="MyShell.exe"   
     上述的“LaunchXX”中的XX为序列数,内核依据这个序列数按由小到大的顺序来分别执行所有子键列出的应用程序,具体数值应该为多少请参考帮助文档的说明。   
     如果原来的内核中添加了标准外壳(standard shell)组件,或者添加了其它组件而这些组件需要依赖标准外壳,那么在PB中是无法删除标准外壳组件的,解决办法一是保留explorer.exe在内核中,二是删除依赖标准外壳的组件。   

9、我原来的工程是x86版本的,编译选项只有x86,我如何能够编译ARM版本的   
两种办法:   
   1)、用EVC新建一个工程的时候,建议复选“CPUs”列表,这样发生了这种事情也能够轻易通过选择“WCE Configuration”工具栏中的CPU列表来编译不同CPU版本的软件   
     2)、如果打开工程后CPU列表中只有x86,而此时已经安装了ARM版本的SDK,那么单击EVC菜单“build”-“configurations”,然后单击“add”按钮来添加CPU。   

   
10、通常情况下WINCE采用串口1作为调试时输出信息用途,要正式出产品前如何去掉串口1的调试功能,正常情况下串口1只有在编译debug版本的内核时才在BootLoader中初始化串口1用于输出信息,而编译release版本会跳过此代码。而有些BSP设计成没有宏定义,也就是说无论什么版本都会在BootLoader中初始化串口1,这样造成WINCE启动后串口1无法被应用程序使用。对于这种情况只能在BootLoader源码中删除初始化代码,如OEMInitDebugSerial。   
   
11、基于wince的应用程序能建成console project么?   
       不能   

12、ARM系统外扩一片512K RAM,驱动程序经过映射可以使用这一段RAM。1、应用系统如何使用这一段RAM?要加一层驱动吗?2、如果要将这一段RAM当作RAM盘存储系统,该如何作呢?应该要加文件系统吧,如何加呢? 
   解答这个问题前,先要说一下WINCE的地址映射机制。对于包含MMU(存储器管理单元)的处理器来说,如ARM和x86,WINCE要求OEM在定制内核的时候填写一个虚拟地址与物理地址映射关系的表,称为OEMAddressTable,在这个表中定义了所有物理设备的起始物理地址,对应的起始虚拟地址,地址空间大小,RAM就包括在其中(如果是x86平台还要求RAM起始虚拟地址从0x80000000开始)。如果有多片RAM,应该在OEMAddressTable中将它们定义在一起,使之地址连续。对于非OEM的开发者来说,他们拿到的是定制好的内核,不能做任何修改,如果在产品中外扩一片RAM,只能通过API函数通知操作系统增加一条虚拟地址与物理地址映射关系表项。相关API函数有两个,分别是CreateStaticMapping和VirtualCopy。它们的相同之处是都用于建立物理地址和虚拟地址的映射关系。它们的不同之处是CreateStaticMapping映射的虚拟地址范围在0xC400 0000 到 0xE000 0000之间,这个范围只能由内核访问,一般用于ISR访问,因为ISR只能访问静态映射的虚拟地址空间,不能用VirtualCopy。VirtualCopy通常和VirtualAlloc配合使用,映射的虚拟地址空间在0x8000 0000以下,一般用于驱动程序和应用程序访问。   
1)、应用程序要访问这片RAM,和驱动程序访问方法一样,调用VirtualAlloc和VirtualCopy。   
2)、可以做一个流驱动程序专门用于读写这片RAM,这样所有应用程序就可以通过调用流驱动接口函数来访问,非要加文件系统也是可行的,通过修改注册表就可以做到,但是麻烦一些。   

13、我怎么能在PB左边的定制平台加进我的驱动呢?   
两种办法:   
1)、在platform.bib或者project.bib的MODULES部分添加一条语句,例如:   
MyDriver.dll       C:\Driver\MyDriver.dll                     NK SH   
这样编译内核的时候就会把你的驱动DLL文件添加到内核中,如果有注册表需要设置,在platform.reg或者project.reg中添加注册表内容。   
2)、通过制作.cec文件来添加驱动,制作.cec文件的优点是只需制作一次,以后就可以通过将.cec文件导入到PB的Catalog中,象PB自带的feature一样通过菜单“Add to OS Design”添加到左边的内核工程中。   

14、 WINCE有没有相对路径概念?如果没有如何得到当前模块的路径?   
     1) 、WINCE没有相对路径概念,只有绝对路径,所以凡是涉及到路径均为绝对路径。   
     2)、调用API GetModuleFileName,传递一个模块的实例句柄就能够得到模块的绝对路径。   

15、   怎样让 POCKET WORD打开*.dat格式(里面都是数据)的文件? 
     两种办法:   
     1)、调用API ShellExecuteEx,在结构体SHELLEXECUTEINFO中添加.dat文件的路径。   
     2)、调用API CreateProcess,在第二个参数中设置.dat文件的路径。   

16、 x86 Rom Boot Loader真的可以实现吗?它确实能代替BIOS启动计算机?   
   Rom Boot 被设计存放在Flash/EEPROM中,也就是原来BIOS的位置,这样当上电后CPU到固定地址执行代码,也就是执行了Rom Boot的代码,它对整个硬件系统进行初始化和检测,并且支持通过网卡从远程机器上下载nk.bin或者从本地IDE/ATA 硬盘的活动分区中寻找nk.bin文件加载。Rom Boot的优点就是引导并且加载速度快,而且它自身完成了所有的操作,这样就不用BIOS、MSDOS,更不用Loadcepc了。   
   
17、 对于x86 Rom Boot Loader,如何Build得到Romboot.rom?   
   1)、在PB中打开一个内核工程(x86的) 
   2)、单击PB菜单“Build”-“Open Build Release Directory” 
   30 、用cd命令进入 %_WINCEROOT%\Platform\Geode\Romboot 4、build   
   
18、如何设置、更改显示分辨率   
     能否设置、更改显示分辨率由显示驱动程序决定,而没有统一的标准。例如CEPC,在启动的时候可以通过设置loadcepc.exe的参数 /L来决定WINCE启动后的显示分辨率,这是由于显示驱动“VGA Linear Framebuffer”支持,而Geode可以通过在定制内核时修改注册表项来决定WINCE启动后的显示分辨率。   
   
19、 几个硬件使用同一个IRQ,那么发生中断的时候系统怎么判断到底是哪一个硬件发生的中断呢?ISR里面又应该怎么控制呢?   
     Windows CE支持多个设备中断共享一个IRQ,当一个共享IRQ发生时,CE内核的异常处理程序检测设备特定的寄存器,因为大多数设备都有一个单独的寄存器用于表示设备的活动状态,所以通过遍历共享这个IRQ的所有设备的寄存器就可以判断哪个设备发生中断。nk.exe加载一个giisr.dll,这个.dll是微软提供的,它其实是第一个可安装ISR。默认CE内核就是调用这个dll来检测寄存器状态的,当然OEM可以编写自己的.dll。   
     CE内部有一个ISR链,也就是可安装ISR。因为CE允许OEM添加自己的ISR处理程序,所以ISR被设计成一个链表。排在前面的ISR比后面的ISR优先处理中断,如果当前ISR能够处理当前中断,那就返回中断ID由IST处理或者返回SYSINTR_NOP,如果当前ISR不能够处理当前中断,那就返回SYSINTR_CHAIN让下一个ISR处理。   
   
20、请问在wince中如何在内核中增加一个与\windows同级的目录?   
     在platform.dat或者project.dat中添加语句。例如要创建根目录下子目录Program Files,语句如下:   
     root:-Directory("Program Files")
21、 文件格式如下所示,我想把每行的4个值读到4个变量中,用EVC如何编程?   
   第一行: 460.000,   3384672.357342,   521268.972763   
   第二行: 475.117,   3384663.772419,   521281.415271   
   伪代码如下:   
FILE   *stream;   
stream = _wfopen(L"\\a.txt", L"r+");   
if( stream == NULL )   
     return;   
fseek(stream, 0L, SEEK_SET );   
while( !feof( stream ) )   
{   
fwscanf(stream, L"%s", WCHAR1);   
     fwscanf(stream, L"%f", float1);   
     fwscanf(stream, L"%f", float2);   
     fwscanf(stream, L"%f", float3);   
}   
fclose( stream );   
   
22、GWES组件的功能有哪些?   
     GWES不仅负责GDI、窗口、消息,还负责管理本机设备驱动程序,负责加载显示、键盘鼠标、触摸屏驱动程序,而且GWES本身包含电源、LED驱动程序。   
   
23、如何在PB中预先设定好存储内存和程序内存的大小,我想多划分一些空间给程序内存?   
   两种办法:   
   1)、在定制内核时在config.bib文件中设置FSRAMPERCENT = number,具体number可参考标题为“FSRAMPERCENT ”的帮助文档。这种办法是修改内核的设置,所有一直有效。   
   2)、在应用程序中调用API SetSystemMemoryDivision,如果函数返回SYSMEM_CHANGED表示成功,如果返回SYSMEM_MUSTREBOOT表示需要热启动才能有效。这种办法需要每次启动后调用API才有效。   
   
24、 如何取消鼠标光标?   
   通过取消SYS变量来实现此目的,在PB命令行下键入“set SYSGEN_CURSOR=”,然后回车确认。   
   
25、EVC下调用TextOut如何编译会出错?   
     类似这样的问题很多,这是因为EVC的帮助文档内容有错误。可能EVC的帮助文档内容是从桌面Windows帮助文档复制过来的,所以很多API函数还有例子代码都有错误,例如帮助文档中包含一个API函数的说明,但是实际编译的时候提示没有这个API,有的例子代码采用ANSI字符串,而WINCE的API都是宽字符版本,造成直接复制过来编译失败。   
     因为MFC for WINCE的CDC类中没有TextOut成员函数,所以编译会出错,可以用其它类成员函数ExtTextOut或者DrawText替换。   
   
26、我如何将我的dll软件让现有的ce系统认可?尽管我也知道应该使用signfile.exe程序进行签名,但是我并不知道那个ce系统认可的签名应该是啥   
     如果你说的WINCE系统内核已经加入了签名认证机制,那么没有私钥对你的DLL文件签名肯定是无法运行在此内核中的,一般签名密钥的密钥长度都是1024位,很难破解。   
   
27、如果查看WINCE注册表中的内容?   
   两种办法:   
   1)、建立同步后,用EVC自带的工具“Remote Registry Editor”打开查看。   
   2)、从网上下载注册表查看工具,放到WINCE设备中。   
   
28、调用directshow出现链接错误,如何解决?   
player.obj : error LNK2001: unresolved external symbol _IID_IVideoWindow   
player.obj : error LNK2001: unresolved external symbol _IID_IMediaControl   

这是因为链接器没有找到合适的.lib文件。两种办法:   
1)、在EVC菜单Tools—options—directories 里把library files的路径重新调整一下。如果你只安装了EVC自带的Standard SDK而没有其它SDK,可以指定WINCE目录中的.lib文件路径,例如D:\WINCE500\PUBLIC\DIRECTX\OAK\LIB\X86\RETAIL。注意CPU的类型。   
2)、安装SDK,前提是导出SDK的PB内核工程必须包括DirectShow或者其它组件。   
   
29、在PB的config.bib文件中,“IMGFLASH”表示什么意思呢?   
     表示能够刷NK到ROM中,具体请查看标题为“IMG Environment Variables”的帮助文档。   
   
30、x86平台如何映射各种地址空间?如何编写中断服务例程?   
如果是x86平台,可以调用HalTranslateBusAddress转换物理总线地址到物理系统地址,调用HalTranslateSystemAddress转换物理系统地址到逻辑总线地址,也可以不调用这两个函数,因为x86平台除32位物理地址外还有16位的IO地址空间,对于16位的IO地址空间,可以直接调用WRITE_PORT_UCHAR或者READ_PORT_UCHAR等函数直接读写端口。对于32位物理地址可以调用VirtualAlloc和VirtualCopy来映射。这样做思路清晰,简单明了。   
在x86平台要实现ISR,有如下几个步骤(以Geode BSP为例):   
1)、用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用,用于唯一标识发生中断的硬件,又称为中断ID。在Nkintr.h文件中预定义了一些SYSINTR,OEM可以在Oalintr.h文件中自定义SYSINTR。   
2)、用HookInterrupt函数关联硬件中断号和ISR。这里提到的硬件中断号为物理中断号,IRQ为逻辑中断号。在InitPICs函数的最后调用了HookInterrupt函数,如下:   
for (i = 64; i < 80; i++)   
HookInterrupt(i, (void *)PeRPISR);   ///用ISR关联16个中断号   
3)、调用InterruptInitialize函数关联SYSINTR和IST创建的事件对象,也是IST等待的事件对象。详细内容请参考Geode BSP源码。   
   在x86平台要实现可安装ISR,先调用LoadIntChainHandler函数注册在注册表中指定的ISR DLL,然后填充GIISR_INFO结构体并调用KernelLibIoControl函数将此结构体传递给可安装ISR。详细内容请参考WINCE帮助文档或者我著的《Windows CE下驱动开发基础》。   
   
31、修改了WINCE自带的驱动程序后如何编译?如果是自己开发的驱动程序如何编译?   
1)、分为IDE方式和命令行方式。   
IDE方式的编译很简单,以PB5.0为例,打开定制内核的工程,在左边的“workspace”—“FileView”中找到你已经修改了的目录,然后单击右键弹出菜单,在菜单中选择“Build and Sysgen Current Project”,这样PB就会编译指定的目录中的项目源码文件,然后执行sysgen命令根据source文件中的内容生成目标文件并复制到当前内核工程目录下。   
命令行方式的编译需要打开“Build OS”—“Open Release Directory”,以cd命令进入你已经修改的驱动程序目录中,然后键入“build –cfs”,然后键入“sysgen –p 项目名称”,一般项目名称为source文件中的“TARGETNAME”。   
2)、如果想完全自己开发驱动程序,建议直接采用EVC或者PB来编写编译。   
   
32、开发PCI设备驱动时,InterruptInitialize函数的第一参数是否是PCI卡配置空间信息中的InterruptLine 参数?是否需要在HKLM\Drivers\BuiltIn\PCI\Template 加一個自己的 subkey,并填写相应内容?如何填?   
1)、InterruptInitialize的第一参数是IRQ,也就是逻辑中断号,而不是物理中断号,InterruptLine是指物理中断号   
2)、需要在template下加自己的PCI设备的信息,例如:   
   [HKEY_LOCAL_MACHINE\Drivers\PCI\Template\Serial]   
     "Dll"="Com16550.Dll"   
     "Class"=dword:07   
     "SubClass"=dword:00   
     "ProgIF"=dword:02   
     "VendorID"=multi_sz:"0AF0","B320","B320"   
     "DeviceID"=multi_sz:"0020","0300","0302"   
     "Prefix"="COM"   
而这些信息就来自于你执行pcienum.exe的结果。   
   
33、编译器报错:error C2065: ‘CFileFind‘ : undeclared identifier,如何解决?   
   MFC for WINCE版本没有CFileFind类,所以要查找文件只能调用API FindFirstFile 和FindNextFile。   
   
34、如何设置WINCE系统字体、字号?如何设置自己开发的软件的字体、字号?   
1)、系统字体通过注册表设置。如下:   
[HKEY_LOCAL_MACHINE\System\GDI\SysFnt]   ///系统字体   
Wt=420   
Ht=18   
Nm=Arial   
[HKEY_LOCAL_MACHINE\System\GWE\Menu\BarFnt]   ///菜单栏字体   
[HKEY_LOCAL_MACHINE\System\GWE\Menu\PopFnt]   ///弹出窗口字体   
[HKEY_LOCAL_MACHINE\System\GWE\Menu]         ///菜单字体   
HKEY_LOCAL_MACHINE\System\GWE\Button           ///按钮字体   
2)、创建字体时把字体高度参数设置大点就可以了。如CFont::CreateFont(nHeight,...),也可以在LOGFONT结构中设置字体高度或者字体种类。如果是控件,调用控件的SetFont成员函数。如果是直接画,在OnPaint响应函数中调用SelectObject选字体到DC。   
   
35、 nk.bin和nk.nb0有什么区别?   
   这里提到的bin是一种二进制镜像格式,以片断(section)为单位组织数据,每个片断都包括一个头,头里指定了起始地址,长度,校验值。Platform Builder调用工具将WINCE内核所有文件以bin格式合并成一个文件,默认文件名为nk.bin。BootLoader又以同样的格式将nk.bin分解成多个文件放到RAM中。可以在命令行中键入“viewbin nk.bin”来查看bin文件中具体包括了哪些内容。键入Cvrtbin命令转换.bin格式文件为.sre格式或者.abx格式。   
   nb0格式是原始的二进制镜像,它不包括头,一般情况下将内核下载到设备的RAM中运行都采用nb0格式。要生成nbx格式的文件,需要在相关.bib文件中确定如下值:ROMSTART、ROMWIDTH、ROMSIZE。   
   
36、在不采用硬件计时器的情况下如何创建更精确的计时器?最精确周期能否达到1毫秒?   
对于精确值的要求不同,所采用的办法不同。以下阐述几种办法。   
1)、在单线程中循环调用API Sleep函数,Sleep函数精确程度为如果Sleep(N),那么实际睡眠时间在N到N+1毫秒之间。而且还要注意调用Sleep的线程优先级的问题。如果任务过多并且此线程优先级低,那误差就更大些。   
2)、调用API QueryPerformanceCounter函数,举例如下:   
LARGE_INTEGER   liFrequency;   
if (QueryPerformanceFrequency(&liFrequency))   // 查询系统时钟的频率,这里将返回1000   
{   
   liFrequency.QuadPart /= 1000;     
   LARGE_INTEGER   liTimeOut;     
   if (QueryPerformanceCounter(&liTimeOut))   //得到截至到当前累计发生的系统时钟中断次数   
   {   
       liTimeOut.QuadPart += liFrequency.QuadPart;   ///计算下一秒到来时总的中断次数是多少   
       LARGE_INTEGER   liCurrent;   
       do   
       {           
         QueryPerformanceCounter(&liCurrent);     // 循环查询累计的的中断次数   
       } while (liCurrent.QuadPart < liTimeOut.QuadPart); ///到达下一秒   
   }   
}   
调用QueryPerformanceCounter同调用Sleep在本质上都是一样的,都是在单线程中无限循环等到周期一到执行任务,相比较QueryPerformanceCounter要比Sleep更精确些,越精确就越要求线程的优先级,保障线程能够正常得到处理器。   
3)、以上办法难以保证周期精确到1毫秒并且WINCE系统稳定地运行,所以要从中断入手。以x86平台为例,先在Timer.c中将默认的SetTimer0(TIMER_COUNT)中的TIMER_COUNT /=2,SetTimer0函数负责设置系统时钟的频率,默认1毫秒发生一次中断,如果除以2就是0.5毫秒发生一次中断。然后在fwpc.c文件中修改ISR函数PeRPISR,因为原来默认是1毫秒发生一次中断,在处理INTR_TIMER0时系统负责累计计数、管理线程的调度,返回相应的SYSINTR值,而我们没有办法再添加代码返回自己定义的SYSINTR值,所以现在要修改原来的处理代码,例如设置一个BOOL型变量,TRUE就执行原来默认的代码,而FALSE就返回我们自己定义的SYSINTR值,这样即不影响原来的ISR处理,又加入了我们的中断响应代码。ISR返回我们定义的SYSINTR后WINCE内核激活相对应的EVENT事件,我们就可以在我们编写的IST里处理任务了。   
   
37、flash中存放了BootLoader和内核镜像,如何把剩余flash部分划分为一个存储区域供应用程序读写?   
   以WINCE提供的驱动(FAT文件系统和MSFLASH驱动)来举例说明。如果采用默认common.reg中的注册表设置,那么MSFLASH驱动默认把整个flash作为存储区域来读写,这不符合问题的要求,所以必须告诉MSFLASH驱动程序可供读写的区域的起始地址和长度。以下是一个注册表例子:   
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\FASLD]   
     "Dll"="fasld.dll"         ///实际Flash存储器的驱动程序   
     "Order"=dword:2       ///该驱动程序相对于其它驱动程序的加载顺序   
     "Prefix"="DSK"         ///前缀   
     "Ioctl"=dword:4         ///IOCTL码,设备管理器加载驱动的时候调用IOControl函数,传递这个IOCTL码。   
"Profile"="MSFlash"     ///Profile名称,也就是[HLM\System\StorageManager\Profiles\MSFlash]   
///当设备管理器加载此驱动程序的同时发送通知给系统,IClass(GUID)的值表明这是一个存储设备的驱动程序。   
"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"     
     "MemBase"=dword:00000000     ///Flash中可供读写区域的起始物理地址,也就是Flash的首地址+偏移量   
     "MemLen"=dword:00000000     ///Flash中可供读写区域的长度   
[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\MSFlash]   
     "DefaultFileSystem"="FATFS"   ///MSFlash驱动默认采用的文件系统   
     "PartitionDriver"="mspart.dll"     ///采用的分区驱动程序   
     "MountAsRoot"=dword:1         ///此目录作为文件系统的根目录   
     "Folder"="NOR Flash"           ///目录名称   
     "Name"="FLASH Disk Block Device"   ///Flash驱动名称   
"PartitionDriverName"="MSPART" ///分区驱动名称   
"AutoMount"=dword:1         ///自动装载检测到的分区   
     "AutoPart"=dword:1             ///自动分区   
     "AutoFormat"=dword:1           ///自动格式化分区   
[HKEY_LOCAL_MACHINE\System\StorageManager\AutoLoad\MSFlash]   
     "DriverPath"="Drivers\\BuiltIn\\FASLD"     ///Flash驱动在注册表中的位置   
     "LoadFlags"=dword:1                 ///这个值可以被设置为0、1、2。1表示同步加载,其它表示异步加载   
     "Order"=dword:0   
[HKEY_LOCAL_MACHINE\System\StorageManager\FATFS]   
     "FriendlyName"="FAT FileSystem"     ///文件系统名称   
     "Dll"="fatfsd.dll"                   ///文件系统驱动程序   
     "Flags"=dword:00000064             ///标志,详见帮助文档   
     "Paging"=dword:1                   ///是否分页   
     "EnableCache"=dword:1               ///是否允许缓存数据   
     "CacheSize"=dword:0                 ///指定缓存大小,0表示默认   

38、驱动程序如何发通知给应用程序?   
   这里介绍一下常见的两种办法。   
   1)、驱动程序调用API SendNotifyMessage,发送特定的消息给应用程序,这就要求应用程序要有消息循环机制并且要事先做好消息的处理。参数1为窗口句柄,可以设置HWND_BROADCAST表示广播消息。要注意的是不要在参数中传递指针(虚拟地址),因为执行驱动程序的线程和应用程序并不在同一个进程空间中。解决办法可以利用内存映射文件技术,比如在驱动程序中创建一个内存映射文件对象,申请一块物理内存,然后把对象名称和内存长度传递给应用程序,应用程序打开同名的内存映射文件对象,读取里面的数据。对象名称可以事先协定好,也可以通过注册表来传递,内存长度是32位值,通过消息参数就可以传递,也可以通过注册表来传递。另外一种解决办法是在定制内核时候预留一块物理内存,这样驱动程序和应用程序都可以通过VirtualAlloc和VirtualCopy来映射到同一块物理内存,其原理同内存映射文件技术一样,但是这块物理内存不具备通用性。最后一个办法是应用程序事先将一个缓冲区地址传递给驱动程序,驱动程序调用MapPtrToProcess映射应用程序传递过来的地址,当驱动程序调用SendNotifyMessage后应用程序可以直接到该地址中读取数据。   
   设备管理器就是调用此函数广播WM_DEVICECHANGE消息的。另外WINCE的一个例子程序RNAApp在拨号连接建立的时候也是调用这个函数广播WM_NETCONNECT消息的。   
   2)、驱动程序调用API CeEventHasOccurred指明一个事件A发生,在此之前应用程序调用API CeRunAppAtEvent将驱动程序指明的A事件和一个应用程序名称相关联,或者和一个事件B相关联。这样当A事件发生时,如果指明和一个应用程序名称关联,那这个应用程序就会被启动。如果指明了和一个事件B相关联,那么等待事件B的线程将被激活。如果想了解当前系统内部所有驱动程序支持哪些类似事件A的事件,调用API CeNotifyPublic_FilterEvent,在该API的帮助文档里也列举了常见的事件,例如NOTIFICATION_EVENT_NET_CONNECT和 NOTIFICATION_EVENT_NET_DISCONNECT。   
   
40、 EVC创建的工程名称如果用中文就出错,该怎么办?   
   用EVC创建的工程名称如果为中文将导致资源文件打不开和编译出错,可以改资源文件名称为英文,再编辑.rc文件中的资源文件名称。但建议尽量不要用中文为工程名称。   
   作为习惯,应该在EVC创建一个工程后,立刻在“project”—“settings”中设置资源的语言属性,然后在“resource view”中设置每个资源的语言属性,这些工作做完后再修改资源就没有问题了。有人询问对话框的标题为乱码,其原因就是在没有修改语言属性的情况下设置标题为中文。
41、WinCE下如何读写几百兆的大文件呢? 使用内存映射文件吗?   
   一般嵌入式设备配备128MB物理内存就算顶级的了,所以要读写几百MB的文件用内存映射文件技术是最好的选择了。映射文件之后读数据是非常容易的,要注意的是写数据,内存映射方面的API没有提供改变文件长度的功能,所以要在关闭映射文件对象后用文件API改变文件长度。   

42、请问如何改系统调度的默认时间片值?   
   更改schedule.c文件中的dwDefaultThreadQuantum 变量,然后重新编译该文件并SYSGEN。调用API CeGetThreadQuantum就知道更改是否生效。   
   
43、如何让系统加载自己写的驱动程序?   
两种办法:   
1)、在[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]下添加注册键。   
2)、在应用程序中调用ActivateDeviceEx。   
   
44、在一些文件中用分号来表示注释,例如下面的内容   
; @CESYSGEN IF SERVERS_MODULES_HTTPD   
; @CESYSGEN ENDIF   
在“CESYSGEN...”前加了“@”,有没有什么特别的含义?   
     在WINCE的一些文件中,用“;”作为注释并在注释文字中用@CESYSGEN作为标记,后面接条件语句。Cefilter.exe工具负责按照条件来筛选文件内容,所以不要轻易地删除包含@CESYSGEN的注释语句。   
   
45、通过串口建立ActiveSync联接,串口线用三线的可以吗?   
   不可以,因为用串口同步时要用到其余口的状态。   

46、 WINCE是否支持MAPI?   
   不支持。 

47、如何旋转屏幕显示的内容?   
   例子代码如下(前提是显示驱动程序支持旋转):   
DEVMODE   devmode = {0};   
devmode.dmSize = sizeof(DEVMODE);   
devmode.dmDisplayOrientation = DMDO_90;       ///垂直模式   
devmode.dmFields = DM_DISPLAYORIENTATION;   
ChangeDisplaySettingsEx(NULL, &devmode, NULL, 0, NULL);   ///改变显示的设置   
CRect   rcWorkArea(0, 0, 320, 240);     ///整个屏幕尺寸   
///设置客户区大小并广播消息,这样所有软件也就随之更改显示   
SystemParametersInfo(SPI_SETWORKAREA, 0, (void*)&rcWorkArea, SPIF_SENDCHANGE);   
   
48、请问如何修改字形缓存的容量?   
[HKEY_LOCAL_MACHINE\System\GDI\GLYPHCACHE]   
"limit"=dword:0400   
   
49、 如何得到从WINCE启动开始到现在的时间?   
   调用API GetTickCount,得到的值为32位整数,单位为毫秒。   
   
50、如何调用WINCE的软键盘?   
调用API SipShowIM(SIPF_ON),前提是内核加入了软键盘组件。   
   
51、 基于HIVE的注册表,如何在系统关闭前保存注册表的数据到文件system.hv?   
调用API RegFlushKey函数。   
   
52、使用VirtualAlloc和VirtualCopy的时候需要注意哪些事项?   
   1)、VirtualAlloc的作用是申请虚拟地址空间,这肯定不是最终的目的,最终目的可能是申请物理内存、映射寄存器、提交文件等。没有一个目的会在意虚拟地址空间的位置,所以尽量传递参数1为0,也就是让WINCE自动分配虚拟地址空间。VirtualAlloc分配地址空间实际上是以64KB为单位,所以要指定申请的虚拟空间的首地址的话,参数1应该为64KB的整数倍,申请的长度也应该为64KB的整数倍,即使你不需要那么大。   
   2)、VirtualCopy的主要作用是映射物理地址空间,如果参数2为物理地址,那么最后一个参数要添加PAGE_PHYSICAL,参数2必须是256的整数倍。如果参数2为虚拟地址(0x80000000以上),那么最后一个参数就不要添加PAGE_PHYSICAL,WINCE内核会根据这个虚拟地址找到对应的物理地址。   
   
53、驱动程序和应用程序之间传递数据时何时调用MapPtrToProcess?   
   因为设备管理器负责加载驱动程序DLL,这意味着当应用程序调用驱动程序接口函数的时候,WINCE内核会将调用驱动程序接口函数的线程转移到设备管理器的进程空间然后执行具体的驱动程序代码,应用程序和设备管理器处于两个进程空间,这就造成设备管理器无法访问应用程序传递的指针(虚拟地址),所以当我们在应用程序中传递指针给流驱动程序接口函数时,WINCE内核从中作了一个地址映射,例如ReadFile、WriteFile、DeviceIoControl函数的参数凡是指针都经过了映射才传递给驱动程序,所以很多驱动程序开发者并不了解其中的奥秘就可以编程了。但是如果参数是一个指向一个结构体的指针,而结构体里包括一个或多个指针,那么WINCE内核并不负责映射,所以就需要开发者在驱动程序接口函数中调用API函数MapPtrToProcess来映射地址。例如:pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());   
   
55、 如何判断可插拔的设备是否存在?   
   1)、通过查找注册表的值。凡是由API ActivateDeviceEx加载的驱动程序都在[HKEY_LOCAL_MACHINE\Drivers\Active]键下有注册键,通过查找“name”或者其它键值就能够找到。设备管理器就调用这个API。如果是PCI设备,在注册表[HLM\Drivers\BuiltIn\PCI\Instance]下查找关键字,例如[HLM\Drivers\BuiltIn\PCI\Instance\WaveDev1],说明音频驱动已经加载。   
   2)、调用驱动程序接口函数,根据返回值或者执行结果来判断。   

56、如何做到通过串口过来的一个信号启动自己开发的应用程序?   
   创建一个线程负责等待串口过来的信号,调用API SetCommMask设置要等待的信号种类,具体可以等待的信号种类参见参数2的说明。然后再调用API WaitCommEvent函数等待这个信号,接收之后再调用API CreateProcess启动应用程序。   
   
57、在WINCE中如何只能启动应用程序的一个实例?   
   常用的两种办法:   
1)、如果应用程序实例创建了窗口,可通过API FindWindow函数通过窗口类名和窗口标题名称来查找,前提是系统内不会出现窗口名称重复的情况。   
2)、应用程序初始化的时候创建一个事件或互斥等内核对象,因为内核对象是由内核创建,名称在系统内唯一。   
   
58、能不能自己编辑一个数字签名文件导入到手机上,这样就可以用这个签名签自己的程序了?   
   WINCE的内核签名机制的用途是限制非法的可执行模块EXE、DLL等在设备上运行。要求内核的加载模块用公钥验证请求加载的EXE、DLL的签名是否合法,而这个公钥是在定制内核的时候加进去的,所以除内核的定制者以外的人无法修改这个验证机制。   
   
59、 在WINCE下是否能够得到某一进程使用的物理内存总量?   
   目前没发现有这样一个API能够得到指定进程使用的物理内存总量。只有GlobalMemoryStatus能够得到整个系统使用的物理内存总量。   
   
60、 应用程序如何控制lcd的亮度?如何获得电池的电量?   
   从常见的平台如Geode、三星ARM系列来看,的确在驱动方面没有统一的控制LCD或者其它种类屏幕亮度的接口函数,所以只能根据具体平台提供的接口来做。从帮助文档来看微软的带有DirectDraw功能的显示驱动程序的确有标准的增加亮度的接口函数,关于背景光参见标题为“Enabling a Backlight”的帮助文档。   
获得电池电量有标准的接口函数GetSystemPowerStatusEx,前提是驱动程序和硬件都要支持。
61、WINCE的socket函数好像不支持发送/接收超时?   
是的,最早版本的WINCE支持选项SO_RCVTIMEO、SO_SNDTIMEO,后来却不支持了。   
   
62、WINCE下如何设置窗口最大化和最小化?   
   WINCE的帮助文档在介绍API ShowWindow函数的参数时指出SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE都不被支持,但实际上并不完全是这样,具体来说:   
SW_MAXIMIZE             比原来窗口大,但不是最大化   
SW_MINIMIZE             编译成功,但是不起作用   
SW_SHOWMAXIMIZED     最大化   
SW_SHOWMINIMIZED       编译出错   
SW_RESTORE               能恢复   
SW_SHOWDEFAULT         编译出错   
SW_SHOWMINNOACTIVE   编译出错   
SW_HIDE                   能够隐藏   
   
63、如何用程序调用控制面板的触摸屏校对程序?   
   两种办法:   
1)、调用API TouchCalibrate函数   
2)、调用CreateProcess,参数1为L"\\windows\\ctlpnl.exe",参数2为L"cplmain.cpl,9"。   
   
64、 如何获得U盘或者其它类型的存储器总容量和剩余可用容量?   
调   用API GetStoreInfo得到扇区数、每扇区字节数,相乘即是总容量。调用API GetDiskFreeSpaceEx得到剩余可用容量。   
   
65、三星2440头文件定义#define IIC_BASE 0xB1400000 // 54000000,datasheet是54000000,那么怎么转成0xB1400000?   
     物理地址映射方法分为两种,一种静态映射另一种为动态映射。在OEMAddressTable中定义了物理地址与虚拟地址的映射关系属于静态映射,用VirtualCopy映射属于动态映射,采用哪种办法都可以。问题中提到的属于静态映射,2440的BSP在map.a文件中定义了IIC控制寄存器的物理起始地址和对应的虚拟地址如下:   
DCD 0x91400000, 0x54000000, 1 ;   
   在OEMAddressTable中定义的虚拟地址范围在0x8000 0000—0x9FFF FFFF,这部分可缓存,适合内核程序和应用程序使用,同时WINCE内核在0xA000 0000—0xBFFF FFFF中映射了另一份,指向了同样的物理地址,这部分不可缓存,适合驱动程序使用。三星ARM处理器带有L1级高速缓存,可缓存会提高执行效率。对于特殊的设备寄存器适合映射到不可缓存的虚拟地址。   
   当驱动程序调用VirtualCopy对0xB1400000地址读写时,WINCE自动将这个地址减去0x2000 0000,也就是0x91400000,对应的物理地址就是0x54000000,也就是IIC控制寄存器的物理起始地址。   
   
66、基于RAM的注册表如何保存数据?   
   调用API RegCopyFile备份注册表。调用API RegRestoreFile恢复注册表,然后调用KernelIoControl热启动使恢复生效。   
   
67、如何隐藏和显示winCE下标准外壳的任务栏?   
HANDLE   hTaskBar = FindWindow(L"HHTaskBar", NULL);   
ShowWindow(hTaskBar, SW_HIDE);   
ShowWindow(hTaskBar, SW_SHOWNORMAL);   
   
68、如果能让WINCE的IE浏览器播放flash动画?   
   播放flash需要Macromedia Flash Player SDK,参见http://www.adobe.com/products/flashplayer_sdk/。这和real player相似,都需要WINCE平台的SDK,都需要申请。   
   
69、WINCE下内核模式和用户模式有什么区别?   
   为了使读者能够详细了解WINCE的地址映射原理还有两种模式,在这里我分几个部分说明:   
1)、WINCE内核nk.exe的任务是管理操作系统核心功能。按照OEMAddressTable的映射要求,所有物理地址都映射到0x80000000以上,所以对于内核程序nk.exe和内核模式下的线程来说,只要访问0x80000000以上的有效虚拟地址经MMU就能够访问物理地址,无需再映射是内核模式的一个特点。内核模式的第二个特点是没有地址访问限制,内核模式线程可以访问任何有效虚拟地址,所谓有效虚拟地址是指有实际事物对应。   
2)、用户模式线程只能访问0x80000000以下的虚拟地址空间,WINCE6.0之前版本的内核为每个进程划分32MB的地址空间,在不调用特殊函数的情况下不能相互访问,这样的设计使得WINCE系统更安全、更稳定,限制访问地址是用户模式的第一个特点。第二个特点就是需要多一层映射,如果线程要访问物理内存的话需要先映射到0x80000000以上,再经MMU访问物理内存地址。   
   WINCE的线程具有转移性(参考API GetCallerProcess的说明,有一个很好的例子),当应用程序的线程调用API或者调用驱动程序接口函数时,该线程会转移到gwes.exe、device.exe、filesys.exe等进程中执行,转移是由WINCE内核操作的,它会修改线程的上下文,记录线程的当前进程、调用者进程、拥有者进程三个值。   
3)、如果在定制内核的时候选择了“Full Kernel Mode”,那么在这个内核上运行的所有线程都处于内核模式,即使调用SetKMode(FALSE)后线程仍然具有内核模式的特点,能够访问任何有效的虚拟地址。假设现有一个64MB RAM的WINCE产品,RAM映射从0x80000000到0x84000000,如果线程处于内核模式,它就直接可以访问这个范围的虚拟地址:   
   在OnButton1()中编写   
DWORD oldMode = SetKMode(FALSE);   
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)   
*piTemp = 12345;   
在OnButton2()中编写   
DWORD oldMode = SetKMode(FALSE);   
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)   
int iTemp = *piTemp;   
   先只执行OnButton1()然后关闭程序,再重启程序然后执行OnButton2(),iTemp仍然等于12345。结果说明了两点:内核模式线程可以直接访问0x80000000以上的有效虚拟地址;我们写到RAM中的数据没有丢失,说明虚拟地址有效。   
   如果在定制内核的时候没有选择“Full Kernel Mode”,那么在这个内核上运行的所有线程都处于用户模式。可以调用SetKMode(TRUE)使调用线程暂时处于内核模式,还是原来的假设环境,我再举个例子:   
在OnButton1()中编写   
DWORD oldMode = SetKMode(TRUE);   
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)   
*piTemp = 12345;   
   在用户模式下,如果不调用SetKMode(TRUE),那么执行*piTemp = 12345一定会弹出对话框,提示地址访问非法,如果调用SetKMode(TRUE)就不会提示地址访问非法,而且在OnButton2()中仍然能得到12345这个值。   
   通过这两个例子我相信读者能够完全了解两种模式的区别了。   
4)、WINCE提供了两个函数SetKMode和SetProcPermissions,其中SetKMode能够把调用线程切换到内核模式,还可以切换回用户模式。SetProcPermissions + GetCurrentPermissions添加当前进程访问权限给调用线程,SetProcPermissions (0xFFFFFFFF)能让调用线程访问所有进程空间,但是调用线程仍然处于用户模式。SetKMode和SetProcPermissions函数使得用户模式的特点不那么明晰。   
   如上所说一个应用程序的线程可能转移到其它两个进程地址空间中读写数据,而每一个线程在被创建的时候只有访问创建它的进程地址空间的权限,所以驱动程序开发者必须在驱动程序读写数据前调用SetKMode或者SetProcPermissions增加调用此函数的线程访问其它进程空间的权限。如果一个应用程序的线程只转移到一个进程地址空间,一般为设备管理器进程device.exe,这种情况下不必增加线程访问其它进程空间的权限,但如果驱动程序本身创建了一个线程,那还是要调用SetKMode或者SetProcPermissions增加新的线程访问其它进程的权限的,因为驱动程序创建线程时,当前进程为设备管理器,所以新线程只具有访问设备管理器进程空间的权限,而不具备访问应用程序进程空间的权限。   
5)、可能一个编写过简单的流驱动的初学者会很疑惑,因为开发一个简单的流驱动程序根本不需要调用这些函数,也没有调用过MapPtrToProcess,那是因为如果标准流驱动接口函数的参数为指针(ReadFile、WriteFile、DeviceIoControl参数都有指针),WINCE内核会自动映射指针包含的地址,但仅此而已,其余任何情况都要求开发者自行处理,比如流接口函数的参数是一个指向结构体的指针PA,而结构体中包括指针PB,PB指针就必须在流接口函数中映射,映射后才能访问,否则就会造成地址访问非法。所以结构体中每个指针都要映射。   
   为了让读者能了解其中的原因,我举个例子:   
   假设设备管理器被加载到Slot4,应用程序A被加载到Slot 8,A只有一个主线程T,T开始执行,按照WINCE的规定,正获得CPU的进程必须映射到Slot0,那么在执行代码的时候A的所有虚拟地址都被减去一个偏移值,也就是8×0x02000000,A调用DeviceIoControl,传递一个指向一个结构体的指针B,而这个结构体中包含一个指针C,指针C包含的地址假设为0x00030000,当执行DeviceIoControl时WINCE把设备管理器的进程地址空间映射到Slot0,因为放在注册表[HKLM\Drivers\BuiltIn]下的驱动程序是由设备管理器加载的,自然驱动程序的代码段被加载到设备管理器进程空间,但是线程仍然是T,此时T的当前所在进程为设备管理器(CurrentProcess),A变成了T的调用者进程(CallerProcess),T自动具有了访问调用者进程空间的权限。这时访问Slot0中的虚拟地址其实质就是访问设备管理器的进程地址空间,要把地址加上一个偏移值,也就是4×0x02000000,所以DeviceIoControl访问指针C包含的地址时本应该加上8×0x02000000,却加上4×0x02000000,结果地址并不是设备管理器的合法区域,系统就会提示地址访问非法。而如果做了一个映射,指针C包含的地址就会被加一个正确的偏移值,使地址处于A的地址空间Slot 8中,T此时具有访问A进程空间的权限,访问到正确的虚拟地址当然会得到正确的数据了。   
   
70、 为什么WINCE目录下的例子用build+sysgen能够编译成EXE文件,而我添加的例子就不能编译呢?   
   如果这个例子是一个应用程序,那么肯定包括代码文件(.h .c .cpp)和资源文件(.rc和其它资源文件),build工具根据source文件内容把代码文件编译成lib文件,资源文件编译成.res文件,sysgen工具根据makefile文件内容将source文件中列出的需要链接的各个库文件合并成一个EXE文件。所以说关键在于makefile文件,WINCE目录下凡是能够用build+sysgen编译的都在makefile中有如何链接的设置,而我们添加的例子当然没有在makefile中找到如何链接的设置,nmake工具就会提示不知道如何创建。   
   
71、pcienum.exe干什么用的?   
   如果你要开发某一个PCI设备的驱动程序,首先要知道这个PCI设备的信息(如VendorID、DeviceID、BaseClass、SubClass)和PCI总线的信息。运行这个pcienum.exe就能得到相关信息。pcienum.exe提供了源码,位置\Public\Common\Oak\Drivers\Ceddk\Test\Pcienum。   
   
72、wince下如何让操作系统进入待机模式?又如何把它激活?   
   通过注册表就可以设置,前提是你的驱动和硬件都支持。注册表项参见标题为“GWES Suspend Time-outs”的帮助文档。   
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]   
     "BattPowerOff"=dword:300   
     "ExtPowerOff"=dword:0   
     "WakeupPowerOff"=dword:60   
     "ScreenPowerOff"=dword:0   
   
73、 现有一个GPRS模块,如何通过GPRS连接到Internet?   
1)、先在内核中加入WAN下面的几个组件,如RAS/PPP、TAPI。WINCE采用unimodem驱动,所以不必担心没有Modem驱动的支持。   
2)、WINCE启动后新建一个拨号连接,比如名称叫“gprs1”,输入用户名、密码、电话号码。电话号码不同,所采用的模式不一样,例如“*99#”是GPRS模式,“17201”是普通的数据模式,速度差很多,价钱也差很多。   
3)、开始连接,连接过程会在对话框中显示,直到显示“连接成功”。   
4)、打开浏览器或者自己开发的通讯软件测试网络连接情况。   
5)、关闭连接。   
6)、保存[HKEY_CURRENT_USER\Comm\RasBook\gprs1]下的所有数据,添加到project.reg中,重新编译后内核中就有了一个拨号连接“gprs1”。   
7)、调用RAS函数可以修改拨号连接“gprs1”的参数,如用户名、密码、电话号码,但是不能修改硬件设置,如波特率、串口、数据位、停止位等。RAS函数还能够拨号、挂断。为了修改波特率可以多保存几个拨号连接,也可以直接调用TAPI开发拨号软件,另外WINCE自带的拨号连接是有源码的,位置在\PUBLIC\COMMON\OAK\DRIVERS\NETSAMP\CONNMC。   
   
74、采用基于HIVE的注册表如何删除用户保存在注册表中的数据,恢复到出厂时的注册表?   
   用户修改的数据保存在user.hv文件中,直接删除一定失败,所以不能通过删除文件实现恢复出厂设置。微软考虑到了这个问题,在WINCE启动过程中filesys.exe加载注册表时会调用OEMIoControl函数并传递一个IOCTL,这个IOCTL在pkfuncs.h中定义如下:   
#define   IOCTL_HAL_GET_HIVE_CLEAN_FLAG   CTL_CODE(FILE_DEVICE_HAL, 49, METHOD_BUFFERED, FILE_ANY_ACCESS)   
f   ilesys.exe会分别传递参数HIVECLEANFLAG_SYSTEM和HIVECLEANFLAG_USERS,如果返回值为TRUE那么filesys.exe清除原来的注册表文件,如果返回值为FALSE那么filesys.exe保留原来的注册表文件。默认WINCE并没有实现这个IOCTL,所以OEM要删除注册表文件就必须先编写这个IOCTL代码。代码的例子可参考标题为“IOCTL_HAL_GET_HIVE_CLEAN_FLAG”的帮助文档。另外必须在ioctl.h和ioctl.c两个文件中编写该代码。在ioctl.c文件中找到const OAL_IOCTL_HANDLER g_oalIoCtlTable[],添加IOCTL和对应的处理函数。要进一步了解这个全局数组,参见标题为“IOCTL Library”的帮助文档。   
   
75、如何在不删除必要组件的前提下减小内核文件长度?   
   要减小内核文件长度首先要在使用PB的定制内核向导中选择自定义,也就是说对于每个组件都由自己来选择,而不是选择PB的标准配置。但减小内核文件长度最有效最直接的办法是缩小字体,尤其对于东亚字体,采用字体压缩技术并且选择合理的字库文件将明显缩小文件长度。   
1)、在定制内核时选择AGFA AC3 Font Compression组件。SYSGEN变量为SYSGEN_AGFA_FONT。   
2)、参考标题为“East Asian Font Versions”的帮助文档,从中选择你需要的字库文件加到内核中,从文档可以看出加AC3压缩比不加压缩在文件长度方面差距很大。   
   
76、如何得到WAV文件播放的总时间?   
1、直接读取wav文件头信息,从文件起始地址偏移28个字节长度为4个字节保存的是每秒钟播放的字节数,从文件起始地址偏移40个字节长度为4个字节保存的是声音数据的总的字节数,相除就是播放时间。   
2、调用IGraphBuilder::RenderFile打开一个wav文件,然后通过IGraphBuilder得到IMediaSeeking指针,再调用IMediaSeeking::GetDuration得到总的时间(结果要除以10000000),IMediaSeeking::GetCurrentPosition得到当前播放时间。   
   
77、如何在Dialog-Based程序中加入menubar?   
   先调用CommandBar_Create再调用CommandBar_InsertMenubar。   
   
78、请问MultiByteToWideChar与_T、L、TEXT的区别?   
   MultiByteToWideChar函数转换的对象可以是常量也可以是变量。其它只能转换常量。_T和TEXT会根据当前系统是否定义_UNICODE宏来决定是否转换,而L就是转换成宽字符,当然也包括其他类型常量的转换。   
   
79、 在用USB线缆通过ActiveSync同步有效的情况下,如何插上USB线缆后WINCE自动与PC同步?   
1)、新建一个拨号连接,假设名称为“usb1”,选择连接类型为“直接连接”,并在连接设备里选择通过USB线缆连接。   
2)、将注册表[HKEY_CURRENT_USER\Comm\RasBook\usb1]下的数据添加到project.reg或者platform.reg中。   
3)、在[HKEY_CURRENT_USER\ControlPanel\Comm]下添加如下:   
"AutoCnct"=dword:1   ///直接连接   
"Cnct"="usb1"       ///连接名称   
4)、重新编译内核。为了节省编译时间也可以在内核工程下搜索*.reg文件,将2、3步骤中的注册表数据添加其中,然后直接make image。   
   
80、如何通过进程句柄来获得该进程的主窗口句柄?   
   好像没有API能够通过进程句柄直接获得主窗口的句柄,因为并非每个应用程序都带UI。但是可以反过来,先枚举当前系统所有主窗口,然后根据每个窗口的句柄调用GetWindowThreadProcessId函数得到进程的ID,再调用OpenProcess得到进程句柄,与现有的进程句柄比较。   
   
81、我做的显示驱动DLL已经编译成功了,但是在加载显示驱动的过程中弹出话框,提示如下:   
   unhandled exception   in   gwes.exe   (0xc0000005 access violation)   
   提示的错误——地址访问非法,表明你的驱动程序代码并没有在读写数据前添加SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数让线程能够访问任何进程的地址空间。你可以调用 IsBadReadPtr和IsBadWritePtr函数检测地址是否能够合法访问。编写和gwes有关的驱动程序应该首先调用SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数,这是一个好习惯。   
   
82、请问在嵌入式系统中如何设置GPRS拔号用的APN?   
对一个拨号连接比如“我的连接”单击鼠标右键,在弹出的菜单中选择“属性”,然后单击“配置”—“拨号选项”,在“附加设置”中添加AT命令如“+cgdc,"ip","cmnet"”。“cmnet”位置即为APN。   
   
83、WINCE的IP Phone功能如何?   
   WINCE的voip需要c-s-c结构,既需要服务器的中转,而skype采用第三代p2p技术就不需要中转,但是在gprs下也做不到语音流畅。skype有pocket pc版本,但是无线方面需要wlan或者cdma。   
   
84、 三星ARM平台如何定义自己的中断ID?   
   以S3C2410为例,在oalintr.h文件中定义中断ID,也称SYSINTR,例如 #define SYSINTR_MYINT   (SYSINTR_FIRMWARE+20),最大值不能超过SYSINTR_FIRMWARE+23。然后在armint.c文件中找到OEMInterruptHandler函数,用if (IntPendVal == INTSRC_XXX) 判断当前发生的中断源号,然后返回SYSINTR_MYINT。内核分别调用OEMInterruptDisable(禁止当前中断)、OEMInterruptDone(中断处理结束)、OEMInterruptEnable(当前中断有效)三个函数,参数都为中断ID,在这三个函数中用 case SYSINTR_MYINT判断当前要处理的中断。   
   
85、如何开发软件从PC端复制文件到基于WINCE的设备?   
   调用RAPI(Remote Application Programming Interface)函数,此函数集由桌面计算机调用,由基于WINCE的设备执行。一旦连接上就可以在桌面计算机端调用RAPI。通过注册表还可以限制RAPI能够访问目录的范围。具体参考RAPI和RDP(远程桌面协议)。   
   
86、请问如何对NandFlash分区、格式化?   
   你看看WINCE420\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cpp,在Eboot中先要调用BP_LowLevelFormat(   
DWORD dwStartBlock, DWORD dwNumBlocks, DWORD dwFlags)再flash的一个区域建立空的MBR,然后连续两次调用BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)函数来建立BINFS和FAT分区。建好后,将nk.bin烧入binfs分区中。   
   
87、要做个弹出对话框具有 always on top 属性,如何实现?   
   调用SetWindowPos(.. , HWND_TOPMOST, ...., SWP_NOACTIVATE)。   
   
88、s3c2410+WINCE下网络PING一会就断,如何解决?   
   原因在于中断处理程序把已经产生的中断标志清除掉了,这样就丢失一次中断。因为原驱动里配置中断为上升沿触发,一次中断丢失就导致不会再产生中断信号跳变,因为只有在中断服务中读取了cs8900的 Interrupt status queue寄存器后,才会产生下一次中断!解决办法:   
1)、在cfw.c文件中全局定义BOOL Inited = FALSE   
2)、修改OEMInterruptEnable()中case SYSINTR_ETHER: 下面的语句为:   
if(Inited == FALSE)   
{   
s2410IOP->rEINTPEND = 0x200;   
s2410INT->rSRCPND = BIT_EINT8_23;   
if (s2410INT->rINTPND & BIT_EINT8_23)   
s2410INT->rINTPND = BIT_EINT8_23;   
Inited = TRUE;   
}   
s2410IOP->rEINTMASK &= ~0x200;   
s2410INT->rINTMSK &= ~BIT_EINT8_23;   
break;   
   
89、已经搜索到文件,如何用CListBox以图标形式显示出来?   
CListCtrl         ListCtrl;   
CImageList       ImageList;   
ImageList.Create(IDB_BITMAP, 48, 2, RGB(0,0,0));   
ListCtrl.SetImageList(&ImageList, LVSIL_NORMAL);   
ListCtrl.InsertItem(iListIndex, strItem, 1);   
   
90、如何改变控制面板中电源属性对话框的尺寸?   
1)、需要修改对话框的尺寸是因为对话框是以资源方式加载的,不会根据当前系统显示分辨率而自我调节尺寸。   
2)、安装WINCE后有一些组件(feature)的资源文件*.res就已经有了,如果你不改变,那么build内核的时候PB只是把这些.res复制到工程目录下,然后与*.obj合并成EXE、DLL、CPL。所以修改了.rc文件里面的对话框尺寸后要重新编译.rc文件为.res文件,然后再覆盖原来WINCE自带的.res文件。   
3)、改变对话框尺寸有两种办法:一种方法是更改系统字体字号,系统字体的字号变化会影响对话框的尺寸,但是缺点是所有系统字体有关的UI都会改变。另一种是在.rc文件中调整对话框尺寸,然后编译成.res文件,再将.res复制到对应的语言目录里,比如目录名为0804(中文),再执行Rebuild命令重新编译内核,或者执行sysgen+build。在研究中我发现.res文件虽然能够直接用EVC打开、修改、保存,但是和其它Obj链接成EXE、DLL、CPL后并不能运行,所以还是建议读者用CE自带的rc工具编译最好。读者可在PB的命令行中键入“rc /?”了解rc.exe工具的用途和参数。   
   
91、使用EVC build之后连接模拟器的时候,提示download file等了一会又出现download failed?   
一般这样的问题从下面几个步骤解决:   
1)、如果之前能启动模拟器而现在不能,那么先clean然后重启计算机再build。   
2)、如果开发的主机为WINXP+SP2,可能存在与EVC模拟器不兼容的情况,检查C:\boot.ini,将/noexecute=optin改为/execute=optin。   
3)、检查你的模拟器是否能运行,假设你正用的SDK名称为MYSDK,单击菜单tools—configure platform manager,选择MYSDK—MYSDK emulator,再单击properties—test,看看模拟器是否能够启动,如果能启动那问题就不大。   
4)、单击菜单build—update remote output files,看看模拟器是否能够启动。   
5)、如果上述办法均不行,关闭EVC然后重新建立一个新的工程,编译,看看模拟器是否能够启动,如果能启动说明原来工程出了问题,最好恢复原工程的备份。   
   
92、如何设置能够自动拨号、禁止自动拨号?   
在[HKEY_LOCAL_MACHINE\Comm\Autodial]下是自动拨号的注册表设置。   
Enabled=DWORD:1           ///是否能够自动拨号   
FailRetryWaitMS=DWORD     ///如果失败再次拨号的等待时间   
RasEntryName1= REG_SZ     ///自动拨号采用的拨号连接名称   
更多细节请参考标题为“Auto Dial Registry Settings”的帮助文档。

转:WinCE驱动开发问题精华集锦