19.WINCE的socket函数好像不支持发送/接收超时?
是的,最早版本的WINCE支持选项SO_RCVTIMEO、SO_SNDTIMEO,后来却不支持了。
20.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 能够隐藏
21.如何用程序调用控制面板的触摸屏校对程序?
两种办法:
1、调用API TouchCalibrate函数
2、调用CreateProcess,参数1为L"\\windows\\ctlpnl.exe",参数2为L"cplmain.cpl,9"。
如何获得U盘或者其它类型的存储器总容量和剩余可用容量?
调用API GetStoreInfo得到扇区数、每扇区字节数,相乘即是总容量。调用API GetDiskFreeSpaceEx得到剩余可用容量。
22.三星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控制寄存器的物理起始地址。
基于RAM的注册表如何保存数据?
调用API RegCopyFile备份注册表。调用API RegRestoreFile恢复注册表,然后调用KernelIoControl热启动使恢复生效。
23.如何隐藏和显示winCE下标准外壳的任务栏?
HANDLE hTaskBar = FindWindow(L"HHTaskBar", NULL);
ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOWNORMAL);
24.如果能让WINCE的IE浏览器播放flash动画?
播放flash需要Macromedia Flash Player SDK,参见http://www.adobe.com/products/flashplayer_sdk/。这和real player相似,都需要WINCE平台的SDK,都需要申请。
25.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进程空间的权限,访问到正确的虚拟地址当然会得到正确的数据了。
为什么WINCE目录下的例子用build+sysgen能够编译成EXE文件,而我添加的例子就不能编译呢?
如果这个例子是一个应用程序,那么肯定包括代码文件(.h .c .cpp)和资源文件(.rc和其它资源文件),build工具根据source文件内容把代码文件编译成lib文件,资源文件编译成.res文件,sysgen工具根据makefile文件内容将source文件中列出的需要链接的各个库文件合并成一个EXE文件。所以说关键在于makefile文件,WINCE目录下凡是能够用build+sysgen编译的都在makefile中有如何链接的设置,而我们添加的例子当然没有在makefile中找到如何链接的设置,nmake工具就会提示不知道如何创建。
pcienum.exe干什么用的?
如果你要开发某一个PCI设备的驱动程序,首先要知道这个PCI设备的信息(如VendorID、DeviceID、BaseClass、SubClass)和PCI总线的信息。运行这个pcienum.exe就能得到相关信息。pcienum.exe提供了源码,位置\Public\Common\Oak\Drivers\Ceddk\Test\Pcienum。
26.wince下如何让操作系统进入待机模式?又如何把它激活?
通过注册表就可以设置,前提是你的驱动和硬件都支持。注册表项参见标题为“GWES Suspend Time-outs”的帮助文档。
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]
"BattPowerOff"=dword:300
"ExtPowerOff"=dword:0
"WakeupPowerOff"=dword:60
"ScreenPowerOff"=dword:0 |