wince 常见问题 2

来源:互联网 发布:那款网络电视好 编辑:程序博客网 时间:2024/05/07 04:50
wince 常见问题 2

40
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。
  
41
在不采用硬件计时器的情况下如何创建更精确的计时器?最精确周期能否达到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里处理任务了。

42
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表示默认


43
驱动程序如何发通知给应用程序?

这里介绍一下常见的两种办法。
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。

44
EVC创建的工程名称如果用中文就出错,该怎么办?

用EVC创建的工程名称如果为中文将导致资源文件打不开和编译出错,可以改资源文件名称为英文,再编辑.rc文件中的资源文件名称。但建议尽量

不要用中文为工程名称。
作为习惯,应该在EVC创建一个工程后,立刻在“project”—“settings”中设置资源的语言属性,然后在“resource view”中设置每个资源的语

言属性,这些工作做完后再修改资源就没有问题了。有人询问对话框的标题为乱码,其原因就是在没有修改语言属性的情况下设置标题为中文。

45
WinCE下如何读写几百兆的大文件呢? 使用内存映射文件吗?

一般嵌入式设备配备128MB物理内存就算顶级的了,所以要读写几百MB的文件用内存映射文件技术是最好的选择了。映射文件之后读数据是非常容易

的,要注意的是写数据,内存映射方面的API没有提供改变文件长度的功能,所以要在关闭映射文件对象后用文件API改变文件长度。

46
请问如何改系统调度的默认时间片值?

更改schedule.c文件中的dwDefaultThreadQuantum 变量,然后重新编译该文件并SYSGEN。调用API CeGetThreadQuantum就知道更改是否生效。

47
如何让系统加载自己写的驱动程序?

两种办法:
1、在[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]下添加注册键。
2、在应用程序中调用ActivateDeviceEx。

48
在一些文件中用分号来表示注释,例如下面的内容
; @CESYSGEN IF SERVERS_MODULES_HTTPD
; @CESYSGEN ENDIF
在“CESYSGEN...”前加了“@”,有没有什么特别的含义?

在WINCE的一些文件中,用“;”作为注释并在注释文字中用@CESYSGEN作为标记,后面接条件语句。Cefilter.exe工具负责按照条件来筛选文件内容

,所以不要轻易地删除包含@CESYSGEN的注释语句。

49
通过串口建立ActiveSync联接,串口线用三线的可以吗?

不可以,因为用串口同步时要用到其余口的状态。


50   
WINCE是否支持MAPI?

不支持。WINCE自带的pmail.exe软件也不是很好用。建议自开发邮件收发软件。如果需要购买WINCE下邮件收发软件可以联系我。
51
如何旋转屏幕显示的内容?

例子代码如下(前提是显示驱动程序支持旋转):
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);

52
请问如何修改字形缓存的容量?

[HKEY_LOCAL_MACHINE\System\GDI\GLYPHCACHE]
"limit"=dword:0400

53
如何得到从WINCE启动开始到现在的时间?

调用API GetTickCount,得到的值为32位整数,单位为毫秒。

54
如何调用WINCE的软键盘?

调用API SipShowIM(SIPF_ON),前提是内核加入了软键盘组件。

55
基于HIVE的注册表,如何在系统关闭前保存注册表的数据到文件system.hv?

调用API RegFlushKey函数。

56
使用VirtualAlloc和VirtualCopy的时候需要注意哪些事项?

1、 VirtualAlloc的作用是申请虚拟地址空间,这肯定不是最终的目的,最终目的可能是申请物理内存、映射寄存器、提交文件等。没有一个目的

会在意虚拟地址空间的位置,所以尽量传递参数1为0,也就是让WINCE自动分配虚拟地址空间。VirtualAlloc分配地址空间实际上是以64KB为单位,

所以要指定申请的虚拟空间的首地址的话,参数1应该为64KB的整数倍,申请的长度也应该为64KB的整数倍,即使你不需要那么大。
2、 VirtualCopy的主要作用是映射物理地址空间,如果参数2为物理地址,那么最后一个参数要添加PAGE_PHYSICAL,参数2必须是256的整数倍。如

果参数2为虚拟地址(0x80000000以上),那么最后一个参数就不要添加PAGE_PHYSICAL,WINCE内核会根据这个虚拟地址找到对应的物理地址。

57
驱动程序和应用程序之间传递数据时何时调用MapPtrToProcess?

因为设备管理器负责加载驱动程序DLL,这意味着当应用程序调用驱动程序接口函数的时候,WINCE内核会将调用驱动程序接口函数的线程转移到设

备管理器的进程空间然后执行具体的驱动程序代码,应用程序和设备管理器处于两个进程空间,这就造成设备管理器无法访问应用程序传递的指针

(虚拟地址),所以当我们在应用程序中传递指针给流驱动程序接口函数时,WINCE内核从中作了一个地址映射,例如ReadFile、WriteFile、

DeviceIoControl函数的参数凡是指针都经过了映射才传递给驱动程序,所以很多驱动程序开发者并不了解其中的奥秘就可以编程了。但是如果参数

是一个指向一个结构体的指针,而结构体里包括一个或多个指针,那么WINCE内核并不负责映射,所以就需要开发者在驱动程序接口函数中调用API

函数MapPtrToProcess来映射地址。例如:pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());

58
如何判断可插拔的设备是否存在?

1、通过查找注册表的值。凡是由API ActivateDeviceEx加载的驱动程序都在[HKEY_LOCAL_MACHINE\Drivers\Active]键下有注册键,通过查找

“name”或者其它键值就能够找到。设备管理器就调用这个API。如果是PCI设备,在注册表[HLM\Drivers\BuiltIn\PCI\Instance]下查找关键字,

例如[HLM\ Drivers\BuiltIn\PCI\Instance\WaveDev1],说明音频驱动已经加载。
2、调用驱动程序接口函数,根据返回值或者执行结果来判断。

59
如何做到通过串口过来的一个信号启动自己开发的应用程序?

创建一个线程负责等待串口过来的信号,调用API SetCommMask设置要等待的信号种类,具体可以等待的信号种类参见参数2的说明。然后再调用

API WaitCommEvent函数等待这个信号,接收之后再调用API CreateProcess启动应用程序。

60
在WINCE中如何只能启动应用程序的一个实例?

常用的两种办法:
1、如果应用程序实例创建了窗口,可通过API FindWindow函数通过窗口类名和窗口标题名称来查找,前提是系统内不会出现窗口名称重复的情况。


2、应用程序初始化的时候创建一个事件或互斥等内核对象,因为内核对象是由内核创建,名称在系统内唯一。  
61
能不能自己编辑一个数字签名文件导入到手机上,这样就可以用这个签名签自己的程序了?

WINCE的内核签名机制的用途是限制非法的可执行模块EXE、DLL等在设备上运行。要求内核的加载模块用公钥验证请求加载的EXE、DLL的签名是否合

法,而这个公钥是在定制内核的时候加进去的,所以除内核的定制者以外的人无法修改这个验证机制。

62
我按照版主的文章《加密WINCE系统》里操作,提示错误如下:
Error 80090016 during CryptSignHash 1!
Error signing hash

这是因为传递了无效的钥容器名称,使CryptoAPI调用失败。应该在使用signfile工具之前创建一个钥容器,在桌面Windows中调用 API

CryptAcquireContext创建一个指定名称的钥容器,接着再创建一个签名密钥对,这时再使用signfile工具就可以了。我在文章里写成-kfulinlin是

因为我创建钥容器的时候没有指定名称,系统就采用当前登录的用户名为容器名。

62
编译错误:CVTRES : fatal error CVT1102: out of memory; 42 bytes required ?

多数情况下出现这种错误是因EVC的bug而起,应该在安装EVC之后就立刻安装EVC的SP补丁。另外为了避开BUG,使用EVC编程应该养成一些习惯,比

如定期备份工程所有文件,每次编译时采用Clean + Rebuild All,正调试时不要关闭模拟器等等。

63
在WINCE下是否能够得到某一进程使用的物理内存总量?

目前没发现有这样一个API能够得到指定进程使用的物理内存总量。只有GlobalMemoryStatus能够得到整个系统使用的物理内存总量。

64
应用程序如何控制lcd的亮度?如何获得电池的电量?

从常见的平台如Geode、三星ARM系列来看,的确在驱动方面没有统一的控制LCD或者其它种类屏幕亮度的接口函数,所以只能根据具体平台提供的接

口来做。从帮助文档来看微软的带有DirectDraw功能的显示驱动程序的确有标准的增加亮度的接口函数,关于背景光参见标题为 “Enabling a

Backlight”的帮助文档。
获得电池电量有标准的接口函数GetSystemPowerStatusEx,前提是驱动程序和硬件都要支持。

65
WINCE的socket函数好像不支持发送/接收超时?

是的,最早版本的WINCE支持选项SO_RCVTIMEO、SO_SNDTIMEO,后来却不支持了。

66
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                  能够隐藏

67
如何用程序调用控制面板的触摸屏校对程序?

两种办法:
1、调用API TouchCalibrate函数
2、调用CreateProcess,参数1为L"\\Windows\\ctlpnl.exe",参数2为L"cplmain.cpl,9"。

68
如何获得U盘或者其它类型的存储器总容量和剩余可用容量?

调用API GetStoreInfo得到扇区数、每扇区字节数,相乘即是总容量。调用API GetDiskFreeSpaceEx得到剩余可用容量。

69
三星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控制寄存器的物理起始地址。

70
基于RAM的注册表如何保存数据?

调用API RegCopyFile备份注册表。调用API RegRestoreFile恢复注册表,然后调用KernelIoControl热启动使恢复生效。

71
如何隐藏和显示winCE下标准外壳的任务栏?

HANDLE hTaskBar = FindWindow(L"HHTaskBar", NULL);
ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOWNORMAL);

72
如果能让WINCE的IE浏览器播放flash动画?

播放flash需要Macromedia Flash Player SDK,参见http://www.adobe.com/products/flashplayer_sdk/。这和real player相似,都需要WINCE平

台的SDK,都需要申请。  

73
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,应用程序X被加载到Slot 8,X只有一个主线程T,T开始执行,按照WINCE的规定,正获得CPU的进程必须映射到

Slot0,那么在执行代码的时候 X的所有虚拟地址都被减去一个偏移值,也就是8×0x02000000,X调用DeviceIoControl,传递一个指向一个结构体

的指针B,而这个结构体中包含一个指针C,指针C包含的地址假设为0x00030000,当执行DeviceIoControl时WINCE把设备管理器的进程地址空间映射

到Slot0,因为放在注册表[HKLM\Drivers\BuiltIn]下的驱动程序是由设备管理器加载的,自然驱动程序的代码段被加载到设备管理器进程空间,但

是线程仍然是T,此时T的当前所在进程为设备管理器(CurrentProcess),X变成了T的调用者进程(CallerProcess),T自动具有了访问调用者进

程空间的权限。这时访问Slot0中的虚拟地址其实质就是访问设备管理器的进程地址空间,要把地址加上一个偏移值,也就是4×0x02000000,所以

DeviceIoControl访问指针C包含的地址时本应该加上8×0x02000000,却加上4×0x02000000,结果地址并不是设备管理器的合法区域,系统就会提

示地址访问非法。而如果做了一个映射,指针C包含的地址就会被加一个正确的偏移值,使地址处于X的地址空间Slot 8中,T此时具有访问X进程空

间的权限,访问到正确的虚拟地址当然会得到正确的数据了。


74
为什么WINCE目录下的例子用build+sysgen能够编译成EXE文件,而我添加的例子就不能编译呢?

如果这个例子是一个应用程序,那么肯定包括代码文件(.h .c .cpp)和资源文件(.rc和其它资源文件),build工具根据source文件内容把代码

文件编译成lib文件,资源文件编译成.res文件,sysgen工具根据makefile文件内容将source文件中列出的需要链接的各个库文件合并成一个EXE文

件。所以说关键在于makefile文件,WINCE目录下凡是能够用build+sysgen编译的都在makefile中有如何链接的设置,而我们添加的例子当然没有在

makefile中找到如何链接的设置,nmake工具就会提示不知道如何创建。

75
pcienum.exe干什么用的?

如果你要开发某一个PCI设备的驱动程序,首先要知道这个PCI设备的信息(如VendorID、DeviceID、BaseClass、 SubClass)和PCI总线的信息。运

行这个pcienum.exe就能得到相关信息。pcienum.exe提供了源码,位置\Public\ Common\Oak\Drivers\Ceddk\Test\Pcienum。 

原创粉丝点击