zhongqian 2008-6-13 05:53 PM
WinCE驱动开发问题精华集锦三
[align=center]WinCE驱动开发问题精华集锦[/align][table=97%][tr][td=1,1,120][日期:2008-04-30][/td][td]来源:中嵌网 作者:aea22[/td][td=1,1,100][字体:[color=#0000ff]大[/color] [color=#0000ff]中[/color] [color=#0000ff]小[/color]] [/td][/tr][/table][font=宋体][size=9pt]如何调用[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]的软键盘?[/size][/font]
[font=宋体][size=9pt]调用[/size][/font][font=Arial][size=9pt]API SipShowIM(SIPF_ON)[/size][/font][font=宋体][size=9pt],前提是内核加入了软键盘组件。[/size][/font]
[font=宋体][size=9pt]基于[/size][/font][font=Arial][size=9pt]HIVE[/size][/font][font=宋体][size=9pt]的注册表,如何在系统关闭前保存注册表的数据到文件[/size][/font][font=Arial][size=9pt]system.hv[/size][/font][font=宋体][size=9pt]?[/size][/font]
[font=宋体][size=9pt]调用[/size][/font][font=Arial][size=9pt]API RegFlushKey[/size][/font][font=宋体][size=9pt]函数。[/size][/font]
[font=宋体][size=9pt]使用[/size][/font][font=Arial][size=9pt]VirtualAlloc[/size][/font][font=宋体][size=9pt]和[/size][/font][font=Arial][size=9pt]VirtualCopy[/size][/font][font=宋体][size=9pt]的时候需要注意哪些事项?[/size][/font][font=Arial][size=9pt]
1[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt] VirtualAlloc[/size][/font][font=宋体][size=9pt]的作用是申请虚拟地址空间,这肯定不是最终的目的,最终目的可能是申请物理内存、映射寄存器、提交文件等。没有一个目的会在意虚拟地址空间的位置,所以尽量传递参数[/size][/font][font=Arial][size=9pt]1[/size][/font][font=宋体][size=9pt]为[/size][/font][font=Arial][size=9pt]0[/size][/font][font=宋体][size=9pt],也就是让[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]自动分配虚拟地址空间。[/size][/font][font=Arial][size=9pt]VirtualAlloc[/size][/font][font=宋体][size=9pt]分配地址空间实际上是以[/size][/font][font=Arial][size=9pt]64KB[/size][/font][font=宋体][size=9pt]为单位,所以要指定申请的虚拟空间的首地址的话,参数[/size][/font][font=Arial][size=9pt]1[/size][/font][font=宋体][size=9pt]应该为[/size][/font][font=Arial][size=9pt]64KB[/size][/font][font=宋体][size=9pt]的整数倍,申请的长度也应该为[/size][/font][font=Arial][size=9pt]64KB[/size][/font][font=宋体][size=9pt]的整数倍,即使你不需要那么大。[/size][/font][font=Arial][size=9pt]
2[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt] VirtualCopy[/size][/font][font=宋体][size=9pt]的主要作用是映射物理地址空间,如果参数[/size][/font][font=Arial][size=9pt]2[/size][/font][font=宋体][size=9pt]为物理地址,那么最后一个参数要添加[/size][/font][font=Arial][size=9pt]PAGE_PHYSICAL[/size][/font][font=宋体][size=9pt],参数[/size][/font][font=Arial][size=9pt]2[/size][/font][font=宋体][size=9pt]必须是[/size][/font][font=Arial][size=9pt]256[/size][/font][font=宋体][size=9pt]的整数倍。如果参数[/size][/font][font=Arial][size=9pt]2[/size][/font][font=宋体][size=9pt]为虚拟地址([/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以上),那么最后一个参数就不要添加[/size][/font][font=Arial][size=9pt]PAGE_PHYSICAL[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核会根据这个虚拟地址找到对应的物理地址。[/size][/font]
[font=宋体][size=9pt]驱动程序和应用程序之间传递数据时何时调用[/size][/font][font=Arial][size=9pt]MapPtrToProcess[/size][/font][font=宋体][size=9pt]?[/size][/font]
[font=宋体][size=9pt]因为设备管理器负责加载驱动程序[/size][/font][font=Arial][size=9pt]DLL[/size][/font][font=宋体][size=9pt],这意味着当应用程序调用驱动程序接口函数的时候,[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核会将调用驱动程序接口函数的线程转移到设备管理器的进程空间然后执行具体的驱动程序代码,应用程序和设备管理器处于两个进程空间,这就造成设备管理器无法访问应用程序传递的指针(虚拟地址),所以当我们在应用程序中传递指针给流驱动程序接口函数时,[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核从中作了一个地址映射,例如[/size][/font][font=Arial][size=9pt]ReadFile[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]WriteFile[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]DeviceIoControl[/size][/font][font=宋体][size=9pt]函数的参数凡是指针都经过了映射才传递给驱动程序,所以很多驱动程序开发者并不了解其中的奥秘就可以编程了。但是如果参数是一个指向一个结构体的指针,而结构体里包括一个或多个指针,那么[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核并不负责映射,所以就需要开发者在驱动程序接口函数中调用[/size][/font][font=Arial][size=9pt]API[/size][/font][font=宋体][size=9pt]函数[/size][/font][font=Arial][size=9pt]MapPtrToProcess[/size][/font][font=宋体][size=9pt]来映射地址。例如:[/size][/font][font=Arial][size=9pt]pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());
[/size][/font][font=宋体][size=9pt]如何判断可插拔的设备是否存在?[/size][/font][font=Arial][size=9pt]
1[/size][/font][font=宋体][size=9pt]、通过查找注册表的值。凡是由[/size][/font][font=Arial][size=9pt]API ActivateDeviceEx[/size][/font][font=宋体][size=9pt]加载的驱动程序都在[/size][/font][font=Arial][size=9pt][HKEY_LOCAL_MACHINE\Drivers\Active][/size][/font][font=宋体][size=9pt]键下有注册键,通过查找[/size][/font][font=Arial][size=9pt]“name”[/size][/font][font=宋体][size=9pt]或者其它键值就能够找到。设备管理器就调用这个[/size][/font][font=Arial][size=9pt]API[/size][/font][font=宋体][size=9pt]。如果是[/size][/font][font=Arial][size=9pt]PCI[/size][/font][font=宋体][size=9pt]设备,在注册表[/size][/font][font=Arial][size=9pt][HLM\Drivers\BuiltIn\PCI\Instance][/size][/font][font=宋体][size=9pt]下查找关键字,例如[/size][/font][font=Arial][size=9pt][HLM\ Drivers\BuiltIn\PCI\Instance\WaveDev1][/size][/font][font=宋体][size=9pt],说明音频驱动已经加载。[/size][/font][font=Arial][size=9pt]
2[/size][/font][font=宋体][size=9pt]、调用驱动程序接口函数,根据返回值或者执行结果来判断。[/size][/font]
[font=宋体][size=9pt]如何做到通过串口过来的一个信号启动自己开发的应用程序?[/size][/font]
[font=宋体][size=9pt]创建一个线程负责等待串口过来的信号,调用[/size][/font][font=Arial][size=9pt]API SetCommMask[/size][/font][font=宋体][size=9pt]设置要等待的信号种类,具体可以等待的信号种类参见参数[/size][/font][font=Arial][size=9pt]2[/size][/font][font=宋体][size=9pt]的说明。然后再调用[/size][/font][font=Arial][size=9pt] API WaitCommEvent[/size][/font][font=宋体][size=9pt]函数等待这个信号,接收之后再调用[/size][/font][font=Arial][size=9pt]API CreateProcess[/size][/font][font=宋体][size=9pt]启动应用程序。[/size][/font]
[font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]中如何只能启动应用程序的一个实例?[/size][/font]
[font=宋体][size=9pt]常用的两种办法:[/size][/font][font=Arial][size=9pt]
1[/size][/font][font=宋体][size=9pt]、如果应用程序实例创建了窗口,可通过[/size][/font][font=Arial][size=9pt]API FindWindow[/size][/font][font=宋体][size=9pt]函数通过窗口类名和窗口标题名称来查找,前提是系统内不会出现窗口名称重复的情况。[/size][/font][font=Arial][size=9pt]
2[/size][/font][font=宋体][size=9pt]、应用程序初始化的时候创建一个事件或互斥等内核对象,因为内核对象是由内核创建,名称在系统内唯一。[/size][/font]
[font=宋体][size=9pt]能不能自己编辑一个数字签名文件导入到手机上,这样就可以用这个签名签自己的程序了?[/size][/font][font=Arial][size=9pt]
WINCE[/size][/font][font=宋体][size=9pt]的内核签名机制的用途是限制非法的可执行模块[/size][/font][font=Arial][size=9pt]EXE[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]DLL[/size][/font][font=宋体][size=9pt]等在设备上运行。要求内核的加载模块用公钥验证请求加载的[/size][/font][font=Arial][size=9pt]EXE[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]DLL[/size][/font][font=宋体][size=9pt]的签名是否合法,而这个公钥是在定制内核的时候加进去的,所以除内核的定制者以外的人无法修改这个验证机制。[/size][/font]
[font=宋体][size=9pt]我按照版主的文章《加密[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]系统》里操作,提示错误如下:[/size][/font][font=Arial][size=9pt]
Error 80090016 during CryptSignHash 1!
Error signing hash
[/size][/font][font=宋体][size=9pt]这是因为传递了无效的钥容器名称,使[/size][/font][font=Arial][size=9pt]CryptoAPI[/size][/font][font=宋体][size=9pt]调用失败。应该在使用[/size][/font][font=Arial][size=9pt]signfile[/size][/font][font=宋体][size=9pt]工具之前创建一个钥容器,在桌面[/size][/font][font=Arial][size=9pt]Windows[/size][/font][font=宋体][size=9pt]中调用[/size][/font][font=Arial][size=9pt] API CryptAcquireContext[/size][/font][font=宋体][size=9pt]创建一个指定名称的钥容器,接着再创建一个签名密钥对,这时再使用[/size][/font][font=Arial][size=9pt]signfile[/size][/font][font=宋体][size=9pt]工具就可以了。我在文章里写成[/size][/font][font=Arial][size=9pt]-kfulinlin[/size][/font][font=宋体][size=9pt]是因为我创建钥容器的时候没有指定名称,系统就采用当前登录的用户名为容器名。[/size][/font]
[font=宋体][size=9pt]编译错误:[/size][/font][font=Arial][size=9pt]CVTRES : fatal error CVT1102: out of memory; 42 bytes required [/size][/font][font=宋体][size=9pt]?[/size][/font]
[font=宋体][size=9pt]多数情况下出现这种错误是因[/size][/font][font=Arial][size=9pt]EVC[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt]bug[/size][/font][font=宋体][size=9pt]而起,应该在安装[/size][/font][font=Arial][size=9pt]EVC[/size][/font][font=宋体][size=9pt]之后就立刻安装[/size][/font][font=Arial][size=9pt]EVC[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt]SP[/size][/font][font=宋体][size=9pt]补丁。另外为了避开[/size][/font][font=Arial][size=9pt]BUG[/size][/font][font=宋体][size=9pt],使用[/size][/font][font=Arial][size=9pt]EVC[/size][/font][font=宋体][size=9pt]编程应该养成一些习惯,比如定期备份工程所有文件,每次编译时采用[/size][/font][font=Arial][size=9pt]Clean + Rebuild All[/size][/font][font=宋体][size=9pt],正调试时不要关闭模拟器等等。[/size][/font]
[font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]下是否能够得到某一进程使用的物理内存总量?[/size][/font]
[font=宋体][size=9pt]目前没发现有这样一个[/size][/font][font=Arial][size=9pt]API[/size][/font][font=宋体][size=9pt]能够得到指定进程使用的物理内存总量。只有[/size][/font][font=Arial][size=9pt]GlobalMemoryStatus[/size][/font][font=宋体][size=9pt]能够得到整个系统使用的物理内存总量。[/size][/font]
[font=宋体][size=9pt]应用程序如何控制[/size][/font][font=Arial][size=9pt]lcd[/size][/font][font=宋体][size=9pt]的亮度?如何获得电池的电量?[/size][/font]
[font=宋体][size=9pt]从常见的平台如[/size][/font][font=Arial][size=9pt]Geode[/size][/font][font=宋体][size=9pt]、三星[/size][/font][font=Arial][size=9pt][url=http://www.chinaeda.cn/edu/gaoji.html][color=#800080]ARM[/color][/url][/size][/font][font=宋体][size=9pt]系列来看,的确在驱动方面没有统一的控制[/size][/font][font=Arial][size=9pt]LCD[/size][/font][font=宋体][size=9pt]或者其它种类屏幕亮度的接口函数,所以只能根据具体平台提供的接口来做。从帮助文档来看微软的带有[/size][/font][font=Arial][size=9pt]DirectDraw[/size][/font][font=宋体][size=9pt]功能的显示驱动程序的确有标准的增加亮度的接口函数,关于背景光参见标题为[/size][/font][font=Arial][size=9pt] “Enabling a Backlight”[/size][/font][font=宋体][size=9pt]的帮助文档。[/size][/font]
[font=宋体][size=9pt]获得电池电量有标准的接口函数[/size][/font][font=Arial][size=9pt]GetSystemPowerStatusEx[/size][/font][font=宋体][size=9pt],前提是驱动程序和硬件都要支持。[/size][/font][font=Arial][size=9pt]
WINCE[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt]socket[/size][/font][font=宋体][size=9pt]函数好像不支持发送[/size][/font][font=Arial][size=9pt]/[/size][/font][font=宋体][size=9pt]接收超时?[/size][/font]
[font=宋体][size=9pt]是的,最早版本的[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]支持选项[/size][/font][font=Arial][size=9pt]SO_RCVTIMEO[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]SO_SNDTIMEO[/size][/font][font=宋体][size=9pt],后来却不支持了。[/size][/font][font=Arial][size=9pt]
WINCE[/size][/font][font=宋体][size=9pt]下如何设置窗口最大化和最小化?[/size][/font][font=Arial][size=9pt]
WINCE [/size][/font][font=宋体][size=9pt]的帮助文档在介绍[/size][/font][font=Arial][size=9pt]API ShowWindow[/size][/font][font=宋体][size=9pt]函数的参数时指出[/size][/font][font=Arial][size=9pt]SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE[/size][/font][font=宋体][size=9pt]都不被支持,但实际上并不完全是这样,具体来说:[/size][/font][font=Arial][size=9pt]
SW_MAXIMIZE [/size][/font][font=宋体][size=9pt]比原来窗口大,但不是最大化[/size][/font][font=Arial][size=9pt]
SW_MINIMIZE [/size][/font][font=宋体][size=9pt]编译成功,但是不起作用[/size][/font][font=Arial][size=9pt]
SW_SHOWMAXIMIZED [/size][/font][font=宋体][size=9pt]最大化[/size][/font][font=Arial][size=9pt]
SW_SHOWMINIMIZED [/size][/font][font=宋体][size=9pt]编译出错[/size][/font][font=Arial][size=9pt]
SW_RESTORE [/size][/font][font=宋体][size=9pt]能恢复[/size][/font][font=Arial][size=9pt]
SW_SHOWDEFAULT [/size][/font][font=宋体][size=9pt]编译出错[/size][/font][font=Arial][size=9pt]
SW_SHOWMINNOACTIVE [/size][/font][font=宋体][size=9pt]编译出错[/size][/font][font=Arial][size=9pt]
SW_HIDE [/size][/font][font=宋体][size=9pt]能够隐藏[/size][/font]
[font=宋体][size=9pt]如何用程序调用控制面板的触摸屏校对程序?[/size][/font]
[font=宋体][size=9pt]两种办法:[/size][/font][font=Arial][size=9pt]
1[/size][/font][font=宋体][size=9pt]、调用[/size][/font][font=Arial][size=9pt]API TouchCalibrate[/size][/font][font=宋体][size=9pt]函数[/size][/font][font=Arial][size=9pt]
2[/size][/font][font=宋体][size=9pt]、调用[/size][/font][font=Arial][size=9pt]CreateProcess[/size][/font][font=宋体][size=9pt],参数[/size][/font][font=Arial][size=9pt]1[/size][/font][font=宋体][size=9pt]为[/size][/font][font=Arial][size=9pt]L"\\windows\\ctlpnl.exe"[/size][/font][font=宋体][size=9pt],参数[/size][/font][font=Arial][size=9pt]2[/size][/font][font=宋体][size=9pt]为[/size][/font][font=Arial][size=9pt]L"cplmain.cpl,9"[/size][/font][font=宋体][size=9pt]。[/size][/font]
[font=宋体][size=9pt]如何获得[/size][/font][font=Arial][size=9pt]U[/size][/font][font=宋体][size=9pt]盘或者其它类型的存储器总容量和剩余可用容量?[/size][/font]
[font=宋体][size=9pt]调用[/size][/font][font=Arial][size=9pt]API GetStoreInfo[/size][/font][font=宋体][size=9pt]得到扇区数、每扇区字节数,相乘即是总容量。调用[/size][/font][font=Arial][size=9pt]API GetDiskFreeSpaceEx[/size][/font][font=宋体][size=9pt]得到剩余可用容量。[/size][/font]
[font=宋体][size=9pt]三星[/size][/font][font=Arial][size=9pt]2440[/size][/font][font=宋体][size=9pt]头文件定义[/size][/font][font=Arial][size=9pt]#define IIC_BASE 0xB1400000 // 54000000[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]datasheet[/size][/font][font=宋体][size=9pt]是[/size][/font][font=Arial][size=9pt]54000000[/size][/font][font=宋体][size=9pt],那么怎么转成[/size][/font][font=Arial][size=9pt]0xB1400000[/size][/font][font=宋体][size=9pt]?[/size][/font]
[font=宋体][size=9pt]物理地址映射方法分为两种,一种静态映射另一种为动态映射。在[/size][/font][font=Arial][size=9pt]OEMAddressTable[/size][/font][font=宋体][size=9pt]中定义了物理地址与虚拟地址的映射关系属于静态映射,用[/size][/font][font=Arial][size=9pt] VirtualCopy[/size][/font][font=宋体][size=9pt]映射属于动态映射,采用哪种办法都可以。问题中提到的属于静态映射,[/size][/font][font=Arial][size=9pt]2440[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt]BSP[/size][/font][font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]map.a[/size][/font][font=宋体][size=9pt]文件中定义了[/size][/font][font=Arial][size=9pt]IIC[/size][/font][font=宋体][size=9pt]控制寄存器的物理起始地址和对应的虚拟地址如下:[/size][/font][font=Arial][size=9pt]
DCD 0x91400000, 0x54000000, 1 ;
[/size][/font][font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]OEMAddressTable [/size][/font][font=宋体][size=9pt]中定义的虚拟地址范围在[/size][/font][font=Arial][size=9pt]0x8000 0000—0x9FFF FFFF[/size][/font][font=宋体][size=9pt],这部分可缓存,适合内核程序和应用程序使用,同时[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核在[/size][/font][font=Arial][size=9pt] 0xA000 0000—0xBFFF FFFF[/size][/font][font=宋体][size=9pt]中映射了另一份,指向了同样的物理地址,这部分不可缓存,适合驱动程序使用。三星[/size][/font][font=Arial][size=9pt][url=http://www.chinaeda.cn/edu/gaoji.html][color=#800080]ARM[/color][/url][/size][/font][font=宋体][size=9pt]处理器带有[/size][/font][font=Arial][size=9pt]L1[/size][/font][font=宋体][size=9pt]级高速缓存,可缓存会提高执行效率。对于特殊的设备寄存器适合映射到不可缓存的虚拟地址。[/size][/font]
[font=宋体][size=9pt]当驱动程序调用[/size][/font][font=Arial][size=9pt]VirtualCopy[/size][/font][font=宋体][size=9pt]对[/size][/font][font=Arial][size=9pt]0xB1400000[/size][/font][font=宋体][size=9pt]地址读写时,[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]自动将这个地址减去[/size][/font][font=Arial][size=9pt]0x2000 0000[/size][/font][font=宋体][size=9pt],也就是[/size][/font][font=Arial][size=9pt]0x91400000[/size][/font][font=宋体][size=9pt],对应的物理地址就是[/size][/font][font=Arial][size=9pt]0x54000000[/size][/font][font=宋体][size=9pt],也就是[/size][/font][font=Arial][size=9pt]IIC[/size][/font][font=宋体][size=9pt]控制寄存器的物理起始地址。[/size][/font]
[font=宋体][size=9pt]基于[/size][/font][font=Arial][size=9pt]RAM[/size][/font][font=宋体][size=9pt]的注册表如何保存数据?[/size][/font]
[font=宋体][size=9pt]调用[/size][/font][font=Arial][size=9pt]API RegCopyFile[/size][/font][font=宋体][size=9pt]备份注册表。调用[/size][/font][font=Arial][size=9pt]API RegRestoreFile[/size][/font][font=宋体][size=9pt]恢复注册表,然后调用[/size][/font][font=Arial][size=9pt]KernelIoControl[/size][/font][font=宋体][size=9pt]热启动使恢复生效。[/size][/font]
[font=宋体][size=9pt]如何隐藏和显示[/size][/font][font=Arial][size=9pt]winCE[/size][/font][font=宋体][size=9pt]下标准外壳的任务栏?[/size][/font][font=Arial][size=9pt]
HANDLE hTaskBar = FindWindow(L"HHTaskBar", NULL);
ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOWNORMAL);
[/size][/font][font=宋体][size=9pt]如果能让[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt]IE[/size][/font][font=宋体][size=9pt]浏览器播放[/size][/font][font=Arial][size=9pt]flash[/size][/font][font=宋体][size=9pt]动画?[/size][/font]
[font=宋体][size=9pt]播放[/size][/font][font=Arial][size=9pt]flash[/size][/font][font=宋体][size=9pt]需要[/size][/font][font=Arial][size=9pt]Macromedia Flash Player SDK[/size][/font][font=宋体][size=9pt],参见[/size][/font][font=Arial][size=9pt]http://www.adobe.com/products/flashplayer_sdk/[/size][/font][font=宋体][size=9pt]。这和[/size][/font][font=Arial][size=9pt]real player[/size][/font][font=宋体][size=9pt]相似,都需要[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]平台的[/size][/font][font=Arial][size=9pt]SDK[/size][/font][font=宋体][size=9pt],都需要申请。[/size][/font][font=Arial][size=9pt]
WINCE[/size][/font][font=宋体][size=9pt]下内核模式和用户模式有什么区别?[/size][/font]
[font=宋体][size=9pt]为了使读者能够详细了解[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]的地址映射原理还有两种模式,在这里我分几个部分说明:[/size][/font][font=Arial][size=9pt]
1[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt] WINCE[/size][/font][font=宋体][size=9pt]内核[/size][/font][font=Arial][size=9pt]nk.exe[/size][/font][font=宋体][size=9pt]的任务是管理操作系统核心功能。按照[/size][/font][font=Arial][size=9pt]OEMAddressTable[/size][/font][font=宋体][size=9pt]的映射要求,所有物理地址都映射到[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以上,所以对于内核程序[/size][/font][font=Arial][size=9pt]nk.exe[/size][/font][font=宋体][size=9pt]和内核模式下的线程来说,只要访问[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以上的有效虚拟地址经[/size][/font][font=Arial][size=9pt]MMU[/size][/font][font=宋体][size=9pt]就能够访问物理地址,无需再映射是内核模式的一个特点。内核模式的第二个特点是没有地址访问限制,内核模式线程可以访问任何有效虚拟地址,所谓有效虚拟地址是指有实际事物对应。[/size][/font][font=Arial][size=9pt]
2[/size][/font][font=宋体][size=9pt]、用户模式线程只能访问[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以下的虚拟地址空间,[/size][/font][font=Arial][size=9pt]WINCE6.0[/size][/font][font=宋体][size=9pt]之前版本的内核为每个进程划分[/size][/font][font=Arial][size=9pt]32MB[/size][/font][font=宋体][size=9pt]的地址空间,在不调用特殊函数的情况下不能相互访问,这样的设计使得[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]系统更安全、更稳定,限制访问地址是用户模式的第一个特点。第二个特点就是需要多一层映射,如果线程要访问物理内存的话需要先映射到[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以上,再经[/size][/font][font=Arial][size=9pt]MMU[/size][/font][font=宋体][size=9pt]访问物理内存地址。[/size][/font][font=Arial][size=9pt]
WINCE[/size][/font][font=宋体][size=9pt]的线程具有转移性(参考[/size][/font][font=Arial][size=9pt] API GetCallerProcess[/size][/font][font=宋体][size=9pt]的说明,有一个很好的例子),当应用程序的线程调用[/size][/font][font=Arial][size=9pt]API[/size][/font][font=宋体][size=9pt]或者调用驱动程序接口函数时,该线程会转移到[/size][/font][font=Arial][size=9pt] gwes.exe[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]device.exe[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]filesys.exe[/size][/font][font=宋体][size=9pt]等进程中执行,转移是由[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核操作的,它会修改线程的上下文,记录线程的当前进程、调用者进程、拥有者进程三个值。[/size][/font][font=Arial][size=9pt]
3[/size][/font][font=宋体][size=9pt]、如果在定制内核的时候选择了[/size][/font][font=Arial][size=9pt]“Full Kernel Mode”[/size][/font][font=宋体][size=9pt],那么在这个内核上运行的所有线程都处于内核模式,即使调用[/size][/font][font=Arial][size=9pt]SetKMode(FALSE)[/size][/font][font=宋体][size=9pt]后线程仍然具有内核模式的特点,能够访问任何有效的虚拟地址。假设现有一个[/size][/font][font=Arial][size=9pt]64MB RAM[/size][/font][font=宋体][size=9pt]的[/size][/font][font=Arial][size=9pt] WINCE[/size][/font][font=宋体][size=9pt]产品,[/size][/font][font=Arial][size=9pt]RAM[/size][/font][font=宋体][size=9pt]映射从[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]到[/size][/font][font=Arial][size=9pt]0x84000000[/size][/font][font=宋体][size=9pt],如果线程处于内核模式,它就直接可以访问这个范围的虚拟地址:[/size][/font]
[font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]OnButton1()[/size][/font][font=宋体][size=9pt]中编写[/size][/font][font=Arial][size=9pt]
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///[/size][/font][font=宋体][size=9pt]或者[/size][/font][font=Arial][size=9pt](0x84000000-0x00019000)
*piTemp = 12345;
[/size][/font][font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]OnButton2()[/size][/font][font=宋体][size=9pt]中编写[/size][/font][font=Arial][size=9pt]
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///[/size][/font][font=宋体][size=9pt]或者[/size][/font][font=Arial][size=9pt](0x84000000-0x00019000)
int iTemp = *piTemp;
[/size][/font][font=宋体][size=9pt]先只执行[/size][/font][font=Arial][size=9pt]OnButton1()[/size][/font][font=宋体][size=9pt]然后关闭程序,再重启程序然后执行[/size][/font][font=Arial][size=9pt]OnButton2()[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]iTemp[/size][/font][font=宋体][size=9pt]仍然等于[/size][/font][font=Arial][size=9pt]12345[/size][/font][font=宋体][size=9pt]。结果说明了两点:内核模式线程可以直接访问[/size][/font][font=Arial][size=9pt]0x80000000[/size][/font][font=宋体][size=9pt]以上的有效虚拟地址;我们写到[/size][/font][font=Arial][size=9pt]RAM[/size][/font][font=宋体][size=9pt]中的数据没有丢失,说明虚拟地址有效。[/size][/font]
[font=宋体][size=9pt]如果在定制内核的时候没有选择[/size][/font][font=Arial][size=9pt]“Full Kernel Mode”[/size][/font][font=宋体][size=9pt],那么在这个内核上运行的所有线程都处于用户模式。可以调用[/size][/font][font=Arial][size=9pt]SetKMode(TRUE)[/size][/font][font=宋体][size=9pt]使调用线程暂时处于内核模式,还是原来的假设环境,我再举个例子:[/size][/font]
[font=宋体][size=9pt]在[/size][/font][font=Arial][size=9pt]OnButton1()[/size][/font][font=宋体][size=9pt]中编写[/size][/font][font=Arial][size=9pt]
DWORD oldMode = SetKMode(TRUE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///[/size][/font][font=宋体][size=9pt]或者[/size][/font][font=Arial][size=9pt](0x84000000-0x00019000)
*piTemp = 12345;
[/size][/font][font=宋体][size=9pt]在用户模式下,如果不调用[/size][/font][font=Arial][size=9pt]SetKMode(TRUE)[/size][/font][font=宋体][size=9pt],那么执行[/size][/font][font=Arial][size=9pt]*piTemp = 12345[/size][/font][font=宋体][size=9pt]一定会弹出对话框,提示地址访问非法,如果调用[/size][/font][font=Arial][size=9pt]SetKMode(TRUE)[/size][/font][font=宋体][size=9pt]就不会提示地址访问非法,而且在[/size][/font][font=Arial][size=9pt]OnButton2()[/size][/font][font=宋体][size=9pt]中仍然能得到[/size][/font][font=Arial][size=9pt]12345[/size][/font][font=宋体][size=9pt]这个值。[/size][/font]
[font=宋体][size=9pt]通过这两个例子我相信读者能够完全了解两种模式的区别了。[/size][/font][font=Arial][size=9pt]
4[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt] WINCE[/size][/font][font=宋体][size=9pt]提供了两个函数[/size][/font][font=Arial][size=9pt]SetKMode[/size][/font][font=宋体][size=9pt]和[/size][/font][font=Arial][size=9pt]SetProcPermissions[/size][/font][font=宋体][size=9pt],其中[/size][/font][font=Arial][size=9pt]SetKMode[/size][/font][font=宋体][size=9pt]能够把调用线程切换到内核模式,还可以切换回用户模式。[/size][/font][font=Arial][size=9pt]SetProcPermissions + GetCurrentPermissions[/size][/font][font=宋体][size=9pt]添加当前进程访问权限给调用线程,[/size][/font][font=Arial][size=9pt] SetProcPermissions (0xFFFFFFFF)[/size][/font][font=宋体][size=9pt]能让调用线程访问所有进程空间,但是调用线程仍然处于用户模式。[/size][/font][font=Arial][size=9pt]SetKMode[/size][/font][font=宋体][size=9pt]和[/size][/font][font=Arial][size=9pt] SetProcPermissions[/size][/font][font=宋体][size=9pt]函数使得用户模式的特点不那么明晰。[/size][/font]
[font=宋体][size=9pt]如上所说一个应用程序的线程可能转移到其它两个进程地址空间中读写数据,而每一个线程在被创建的时候只有访问创建它的进程地址空间的权限,所以驱动程序开发者必须在驱动程序读写数据前调用[/size][/font][font=Arial][size=9pt]SetKMode[/size][/font][font=宋体][size=9pt]或者[/size][/font][font=Arial][size=9pt] SetProcPermissions[/size][/font][font=宋体][size=9pt]增加调用此函数的线程访问其它进程空间的权限。如果一个应用程序的线程只转移到一个进程地址空间,一般为设备管理器进程[/size][/font][font=Arial][size=9pt]device.exe[/size][/font][font=宋体][size=9pt],这种情况下不必增加线程访问其它进程空间的权限,但如果驱动程序本身创建了一个线程,那还是要调用[/size][/font][font=Arial][size=9pt]SetKMode[/size][/font][font=宋体][size=9pt]或者[/size][/font][font=Arial][size=9pt] SetProcPermissions[/size][/font][font=宋体][size=9pt]增加新的线程访问其它进程的权限的,因为驱动程序创建线程时,当前进程为设备管理器,所以新线程只具有访问设备管理器进程空间的权限,而不具备访问应用程序进程空间的权限。[/size][/font][font=Arial][size=9pt]
5[/size][/font][font=宋体][size=9pt]、可能一个编写过简单的流驱动的初学者会很疑惑,因为开发一个简单的流驱动程序根本不需要调用这些函数,也没有调用过[/size][/font][font=Arial][size=9pt]MapPtrToProcess[/size][/font][font=宋体][size=9pt],那是因为如果标准流驱动接口函数的参数为指针([/size][/font][font=Arial][size=9pt]ReadFile[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt] WriteFile[/size][/font][font=宋体][size=9pt]、[/size][/font][font=Arial][size=9pt]DeviceIoControl[/size][/font][font=宋体][size=9pt]参数都有指针),[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]内核会自动映射指针包含的地址,但仅此而已,其余任何情况都要求开发者自行处理,比如流接口函数的参数是一个指向结构体的指针[/size][/font][font=Arial][size=9pt]PA[/size][/font][font=宋体][size=9pt],而结构体中包括指针[/size][/font][font=Arial][size=9pt]PB[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]PB[/size][/font][font=宋体][size=9pt]指针就必须在流接口函数中映射,映射后才能访问,否则就会造成地址访问非法。所以结构体中每个指针都要映射。[/size][/font]
[font=宋体][size=9pt]为了让读者能了解其中的原因,我举个例子:[/size][/font]
[font=宋体][size=9pt]假设设备管理器被加载到[/size][/font][font=Arial][size=9pt]Slot4[/size][/font][font=宋体][size=9pt],应用程序[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]被加载到[/size][/font][font=Arial][size=9pt]Slot 8[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]只有一个主线程[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt]开始执行,按照[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]的规定,正获得[/size][/font][font=Arial][size=9pt]CPU[/size][/font][font=宋体][size=9pt]的进程必须映射到[/size][/font][font=Arial][size=9pt]Slot0[/size][/font][font=宋体][size=9pt],那么在执行代码的时候[/size][/font][font=Arial][size=9pt] A[/size][/font][font=宋体][size=9pt]的所有虚拟地址都被减去一个偏移值,也就是[/size][/font][font=Arial][size=9pt]8×0x02000000[/size][/font][font=宋体][size=9pt],[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]调用[/size][/font][font=Arial][size=9pt]DeviceIoControl[/size][/font][font=宋体][size=9pt],传递一个指向一个结构体的指针[/size][/font][font=Arial][size=9pt]B[/size][/font][font=宋体][size=9pt],而这个结构体中包含一个指针[/size][/font][font=Arial][size=9pt]C[/size][/font][font=宋体][size=9pt],指针[/size][/font][font=Arial][size=9pt]C[/size][/font][font=宋体][size=9pt]包含的地址假设为[/size][/font][font=Arial][size=9pt]0x00030000[/size][/font][font=宋体][size=9pt],当执行[/size][/font][font=Arial][size=9pt]DeviceIoControl[/size][/font][font=宋体][size=9pt]时[/size][/font][font=Arial][size=9pt]WINCE[/size][/font][font=宋体][size=9pt]把设备管理器的进程地址空间映射到[/size][/font][font=Arial][size=9pt]Slot0[/size][/font][font=宋体][size=9pt],因为放在注册表[/size][/font][font=Arial][size=9pt][HKLM\Drivers\BuiltIn][/size][/font][font=宋体][size=9pt]下的驱动程序是由设备管理器加载的,自然驱动程序的代码段被加载到设备管理器进程空间,但是线程仍然是[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt],此时[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt]的当前所在进程为设备管理器([/size][/font][font=Arial][size=9pt]CurrentProcess[/size][/font][font=宋体][size=9pt]),[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]变成了[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt]的调用者进程([/size][/font][font=Arial][size=9pt]CallerProcess[/size][/font][font=宋体][size=9pt]),[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt]自动具有了访问调用者进程空间的权限。这时访问[/size][/font][font=Arial][size=9pt]Slot0[/size][/font][font=宋体][size=9pt]中的虚拟地址其实质就是访问设备管理器的进程地址空间,要把地址加上一个偏移值,也就是[/size][/font][font=Arial][size=9pt]4×0x02000000[/size][/font][font=宋体][size=9pt],所以[/size][/font][font=Arial][size=9pt]DeviceIoControl[/size][/font][font=宋体][size=9pt]访问指针[/size][/font][font=Arial][size=9pt]C[/size][/font][font=宋体][size=9pt]包含的地址时本应该加上[/size][/font][font=Arial][size=9pt]8×0x02000000[/size][/font][font=宋体][size=9pt],却加上[/size][/font][font=Arial][size=9pt]4×0x02000000[/size][/font][font=宋体][size=9pt],结果地址并不是设备管理器的合法区域,系统就会提示地址访问非法。而如果做了一个映射,指针[/size][/font][font=Arial][size=9pt]C[/size][/font][font=宋体][size=9pt]包含的地址就会被加一个正确的偏移值,使地址处于[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]的地址空间[/size][/font][font=Arial][size=9pt]Slot 8[/size][/font][font=宋体][size=9pt]中,[/size][/font][font=Arial][size=9pt]T[/size][/font][font=宋体][size=9pt]此时具有访问[/size][/font][font=Arial][size=9pt]A[/size][/font][font=宋体][size=9pt]进程空间的权限,访问到正确的虚拟地址当然会得到正确的数据了。[/size][/font]