﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Do What You think !!</title><link>http://www.cppblog.com/kuronca/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 09:37:11 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 09:37:11 GMT</pubDate><ttl>60</ttl><item><title>（转）WinCE驱动开发问题精华集锦</title><link>http://www.cppblog.com/kuronca/archive/2008/05/09/49332.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Fri, 09 May 2008 09:24:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2008/05/09/49332.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/49332.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2008/05/09/49332.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/49332.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/49332.html</trackback:ping><description><![CDATA[<strong>如何让系统加载自己写的驱动程序？</strong>&nbsp;&nbsp; <br>两种办法：&nbsp; <br>1、在[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]下添加注册键。&nbsp; <br>2、在应用程序中调用ActivateDeviceEx。&nbsp; <br>&nbsp; <br>&nbsp;<strong>在一些文件中用分号来表示注释，例如下面的内容&nbsp;&nbsp; <br></strong>;&nbsp;@CESYSGEN&nbsp;IF&nbsp;SERVERS_MODULES_HTTPD&nbsp; <br>;&nbsp;@CESYSGEN&nbsp;ENDIF&nbsp; <br>在&#8220;CESYSGEN...&#8221;前加了&#8220;@&#8221;，有没有什么特别的含义？&nbsp; <br>在WINCE的一些文件中，用&#8220;;&#8221;作为注释并在注释文字中用@CESYSGEN作为标记，后面接条件语句。Cefilter.exe工具负责按照条件来筛选文件内容，所以不要轻易地删除包含@CESYSGEN的注释语句。&nbsp; <br>&nbsp; <br>&nbsp;通过串口建立ActiveSync联接,串口线用三线的可以吗?&nbsp;&nbsp; <br>不可以，因为用串口同步时要用到其余口的状态。&nbsp; <br><br>&nbsp;&nbsp; <br>&nbsp;<strong>&nbsp;&nbsp;&nbsp;&nbsp;WINCE是否支持MAPI？</strong>&nbsp; <br>不支持。WINCE自带的pmail.exe软件也不是很好用。建议自开发邮件收发软件。如果需要购买WINCE下邮件收发软件可以联系我。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何旋转屏幕显示的内容？</strong>&nbsp; <br>例子代码如下（前提是显示驱动程序支持旋转）：&nbsp; <br>DEVMODE&nbsp;&nbsp;devmode&nbsp;=&nbsp;{0};&nbsp; <br>&nbsp;devmode.dmSize&nbsp;=&nbsp;sizeof(DEVMODE);&nbsp; <br>&nbsp;devmode.dmDisplayOrientation&nbsp;=&nbsp;DMDO_90;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///垂直模式&nbsp; <br>&nbsp;devmode.dmFields&nbsp;=&nbsp;DM_DISPLAYORIENTATION;&nbsp; <br>&nbsp;ChangeDisplaySettingsEx(NULL,&nbsp;&amp;devmode,&nbsp;NULL,&nbsp;0,&nbsp;NULL);&nbsp;&nbsp;///改变显示的设置&nbsp; <br>&nbsp;CRect&nbsp;&nbsp;rcWorkArea(0,&nbsp;0,&nbsp;320,&nbsp;240);&nbsp;&nbsp;&nbsp;&nbsp;///整个屏幕尺寸&nbsp; <br>&nbsp;///设置客户区大小并广播消息，这样所有软件也就随之更改显示&nbsp; <br>&nbsp;SystemParametersInfo(SPI_SETWORKAREA,&nbsp;0,&nbsp;(void*)&amp;rcWorkArea,&nbsp;SPIF_SENDCHANGE);&nbsp;&nbsp; <br>&nbsp; <br><strong>&nbsp;请问如何修改字形缓存的容量？&nbsp; <br></strong>[HKEY_LOCAL_MACHINE\System\GDI\GLYPHCACHE]&nbsp; <br>"limit"=dword:0400&nbsp; <br>&nbsp; <br><strong>&nbsp;如何得到从WINCE启动开始到现在的时间？</strong>&nbsp; <br>调用API&nbsp;GetTickCount，得到的值为32位整数，单位为毫秒。&nbsp; <br><strong>&nbsp; <br>&nbsp;如何调用WINCE的软键盘？</strong>&nbsp; <br>调用API&nbsp;SipShowIM(SIPF_ON)，前提是内核加入了软键盘组件。&nbsp; <br>&nbsp; <br><strong>&nbsp;基于HIVE的注册表，如何在系统关闭前保存注册表的数据到文件system.hv？</strong>&nbsp; <br>调用API&nbsp;RegFlushKey函数。&nbsp; <br>&nbsp; <br><strong>&nbsp;使用VirtualAlloc和VirtualCopy的时候需要注意哪些事项？</strong>&nbsp; <br>1、VirtualAlloc的作用是申请虚拟地址空间，这肯定不是最终的目的，最终目的可能是申请物理内存、映射寄存器、提交文件等。没有一个目的会在意虚拟地址空间的位置，所以尽量传递参数1为0，也就是让WINCE自动分配虚拟地址空间。VirtualAlloc分配地址空间实际上是以64KB为单位，所以要指定申请的虚拟空间的首地址的话，参数1应该为64KB的整数倍，申请的长度也应该为64KB的整数倍，即使你不需要那么大。&nbsp; <br>2、VirtualCopy的主要作用是映射物理地址空间，如果参数2为物理地址，那么最后一个参数要添加PAGE_PHYSICAL，参数2必须是256的整数倍。如果参数2为虚拟地址（0x80000000以上），那么最后一个参数就不要添加PAGE_PHYSICAL，WINCE内核会根据这个虚拟地址找到对应的物理地址。&nbsp; <br>&nbsp; <br><strong>&nbsp;驱动程序和应用程序之间传递数据时何时调用MapPtrToProcess？&nbsp; <br></strong>因为设备管理器负责加载驱动程序DLL，这意味着当应用程序调用驱动程序接口函数的时候，WINCE内核会将调用驱动程序接口函数的线程转移到设备管理器的进程空间然后执行具体的驱动程序代码，应用程序和设备管理器处于两个进程空间，这就造成设备管理器无法访问应用程序传递的指针（虚拟地址），所以当我们在应用程序中传递指针给流驱动程序接口函数时，WINCE内核从中作了一个地址映射，例如ReadFile、WriteFile、DeviceIoControl函数的参数凡是指针都经过了映射才传递给驱动程序，所以很多驱动程序开发者并不了解其中的奥秘就可以编程了。但是如果参数是一个指向一个结构体的指针，而结构体里包括一个或多个指针，那么WINCE内核并不负责映射，所以就需要开发者在驱动程序接口函数中调用API函数MapPtrToProcess来映射地址。例如：pPointer_retval&nbsp;=&nbsp;MapPtrToProcess(pPointer,&nbsp;GetCallerProcess());&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何判断可插拔的设备是否存在？</strong>&nbsp; <br>1、通过查找注册表的值。凡是由API&nbsp;ActivateDeviceEx加载的驱动程序都在[HKEY_LOCAL_MACHINE\Drivers\Active]键下有注册键，通过查找&#8220;name&#8221;或者其它键值就能够找到。设备管理器就调用这个API。如果是PCI设备，在注册表[HLM\Drivers\BuiltIn\PCI\Instance]下查找关键字，例如[HLM\Drivers\BuiltIn\PCI\Instance\WaveDev1]，说明音频驱动已经加载。&nbsp; <br>2、调用驱动程序接口函数，根据返回值或者执行结果来判断。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何做到通过串口过来的一个信号启动自己开发的应用程序？&nbsp; <br></strong>创建一个线程负责等待串口过来的信号，调用API&nbsp;SetCommMask设置要等待的信号种类，具体可以等待的信号种类参见参数2的说明。然后再调用API&nbsp;WaitCommEvent函数等待这个信号，接收之后再调用API&nbsp;CreateProcess启动应用程序。&nbsp; <br>&nbsp; <br><strong>&nbsp;在WINCE中如何只能启动应用程序的一个实例？</strong>&nbsp; <br>常用的两种办法：&nbsp; <br>1、如果应用程序实例创建了窗口，可通过API&nbsp;FindWindow函数通过窗口类名和窗口标题名称来查找，前提是系统内不会出现窗口名称重复的情况。&nbsp; <br>2、应用程序初始化的时候创建一个事件或互斥等内核对象，因为内核对象是由内核创建，名称在系统内唯一。&nbsp; <br>&nbsp; <br><strong>&nbsp;能不能自己编辑一个数字签名文件导入到手机上，这样就可以用这个签名签自己的程序了？</strong>&nbsp; <br>WINCE的内核签名机制的用途是限制非法的可执行模块EXE、DLL等在设备上运行。要求内核的加载模块用公钥验证请求加载的EXE、DLL的签名是否合法，而这个公钥是在定制内核的时候加进去的，所以除内核的定制者以外的人无法修改这个验证机制。&nbsp; <br>&nbsp; <br>&nbsp;我按照版主的文章《加密WINCE系统》里操作，提示错误如下：&nbsp; <br>Error&nbsp;80090016&nbsp;during&nbsp;CryptSignHash&nbsp;1!&nbsp; <br>Error&nbsp;signing&nbsp;hash&nbsp; <br>这是因为传递了无效的钥容器名称，使CryptoAPI调用失败。应该在使用signfile工具之前创建一个钥容器，在桌面Windows中调用API&nbsp;CryptAcquireContext创建一个指定名称的钥容器，接着再创建一个签名密钥对，这时再使用signfile工具就可以了。我在文章里写成-kfulinlin是因为我创建钥容器的时候没有指定名称，系统就采用当前登录的用户名为容器名。&nbsp; <br>&nbsp; <br>&nbsp;<strong>编译错误：CVTRES&nbsp;:&nbsp;fatal&nbsp;error&nbsp;CVT1102:&nbsp;out&nbsp;of&nbsp;memory;&nbsp;42&nbsp;bytes&nbsp;required&nbsp;？</strong>&nbsp; <br>多数情况下出现这种错误是因EVC的bug而起，应该在安装EVC之后就立刻安装EVC的SP补丁。另外为了避开BUG，使用EVC编程应该养成一些习惯，比如定期备份工程所有文件，每次编译时采用Clean&nbsp;+&nbsp;Rebuild&nbsp;All，正调试时不要关闭模拟器等等。&nbsp; <br>&nbsp; <br><strong>&nbsp;在WINCE下是否能够得到某一进程使用的物理内存总量？</strong>&nbsp; <br>目前没发现有这样一个API能够得到指定进程使用的物理内存总量。只有GlobalMemoryStatus能够得到整个系统使用的物理内存总量。&nbsp; <br>&nbsp; <br><strong>&nbsp;应用程序如何控制lcd的亮度？如何获得电池的电量？</strong>&nbsp; <br>从常见的平台如Geode、三星ARM系列来看，的确在驱动方面没有统一的控制LCD或者其它种类屏幕亮度的接口函数，所以只能根据具体平台提供的接口来做。从帮助文档来看微软的带有DirectDraw功能的显示驱动程序的确有标准的增加亮度的接口函数，关于背景光参见标题为&#8220;Enabling&nbsp;a&nbsp;Backlight&#8221;的帮助文档。&nbsp; <br>获得电池电量有标准的接口函数GetSystemPowerStatusEx，前提是驱动程序和硬件都要支持。&nbsp; <br>&nbsp; <br>&nbsp;<strong>WINCE的socket函数好像不支持发送/接收超时？</strong>&nbsp; <br>是的，最早版本的WINCE支持选项SO_RCVTIMEO、SO_SNDTIMEO，后来却不支持了。&nbsp; <br>&nbsp; <br>&nbsp;<strong>WINCE下如何设置窗口最大化和最小化？</strong>&nbsp; <br>WINCE的帮助文档在介绍API&nbsp;ShowWindow函数的参数时指出SW_MAXIMIZE,&nbsp;SW_MINIMIZE,&nbsp;SW_RESTORE,&nbsp;SW_SHOWDEFAULT,&nbsp;SW_SHOWMAXIMIZED,&nbsp;SW_SHOWMINIMIZED,&nbsp;SW_SHOWMINNOACTIVE都不被支持，但实际上并不完全是这样，具体来说：&nbsp; <br>SW_MAXIMIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;比原来窗口大，但不是最大化&nbsp; <br>SW_MINIMIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译成功，但是不起作用&nbsp; <br>SW_SHOWMAXIMIZED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最大化&nbsp; <br>SW_SHOWMINIMIZED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译出错&nbsp; <br>SW_RESTORE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;能恢复&nbsp; <br>SW_SHOWDEFAULT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译出错&nbsp; <br>SW_SHOWMINNOACTIVE&nbsp;&nbsp;&nbsp;编译出错&nbsp; <br>SW_HIDE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;能够隐藏&nbsp; <br>&nbsp; <br><strong>&nbsp;如何用程序调用控制面板的触摸屏校对程序？</strong>&nbsp;&nbsp; <br>两种办法：&nbsp; <br>1、调用API&nbsp;TouchCalibrate函数&nbsp; <br>2、调用CreateProcess，参数1为L"\\windows\\ctlpnl.exe"，参数2为L"cplmain.cpl,9"。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何获得U盘或者其它类型的存储器总容量和剩余可用容量？</strong>&nbsp; <br>调用API&nbsp;GetStoreInfo得到扇区数、每扇区字节数，相乘即是总容量。调用API&nbsp;GetDiskFreeSpaceEx得到剩余可用容量。&nbsp; <br>&nbsp; <br>&nbsp;<strong>三星2440头文件定义#define&nbsp;IIC_BASE&nbsp;0xB1400000&nbsp;//&nbsp;54000000，datasheet是54000000，那么怎么转成0xB1400000？</strong>&nbsp; <br>物理地址映射方法分为两种，一种静态映射另一种为动态映射。在OEMAddressTable中定义了物理地址与虚拟地址的映射关系属于静态映射，用VirtualCopy映射属于动态映射，采用哪种办法都可以。问题中提到的属于静态映射，2440的BSP在map.a文件中定义了IIC控制寄存器的物理起始地址和对应的虚拟地址如下：&nbsp; <br>DCD&nbsp;0x91400000,&nbsp;0x54000000,&nbsp;1&nbsp;;&nbsp; <br>在OEMAddressTable中定义的虚拟地址范围在0x8000&nbsp;0000—0x9FFF&nbsp;FFFF，这部分可缓存，适合内核程序和应用程序使用，同时WINCE内核在0xA000&nbsp;0000—0xBFFF&nbsp;FFFF中映射了另一份，指向了同样的物理地址，这部分不可缓存，适合驱动程序使用。三星ARM处理器带有L1级高速缓存，可缓存会提高执行效率。对于特殊的设备寄存器适合映射到不可缓存的虚拟地址。&nbsp; <br>当驱动程序调用VirtualCopy对0xB1400000地址读写时，WINCE自动将这个地址减去0x2000&nbsp;0000，也就是0x91400000，对应的物理地址就是0x54000000，也就是IIC控制寄存器的物理起始地址。&nbsp; <br>&nbsp; <br>&nbsp;<strong>基于RAM的注册表如何保存数据？</strong>&nbsp; <br>调用API&nbsp;RegCopyFile备份注册表。调用API&nbsp;RegRestoreFile恢复注册表，然后调用KernelIoControl热启动使恢复生效。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何隐藏和显示winCE下标准外壳的任务栏？</strong>&nbsp; <br>&nbsp;HANDLE&nbsp;&nbsp;hTaskBar&nbsp;=&nbsp;FindWindow(L"HHTaskBar",&nbsp;NULL);&nbsp;&nbsp; <br>&nbsp;ShowWindow(hTaskBar,&nbsp;SW_HIDE);&nbsp;&nbsp; <br>&nbsp;ShowWindow(hTaskBar,&nbsp;SW_SHOWNORMAL);&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;<strong>如果能让WINCE的IE浏览器播放flash动画？</strong>&nbsp; <br>播放flash需要Macromedia&nbsp;Flash&nbsp;Player&nbsp;SDK，参见http://www.adobe.com/products/flashplayer_sdk/。这和real&nbsp;player相似，都需要WINCE平台的SDK，都需要申请。&nbsp; <br>&nbsp; <br>&nbsp;<strong>WINCE下内核模式和用户模式有什么区别？</strong>&nbsp; <br>为了使读者能够详细了解WINCE的地址映射原理还有两种模式，在这里我分几个部分说明：&nbsp; <br>1、WINCE内核nk.exe的任务是管理操作系统核心功能。按照OEMAddressTable的映射要求，所有物理地址都映射到0x80000000以上，所以对于内核程序nk.exe和内核模式下的线程来说，只要访问0x80000000以上的有效虚拟地址经MMU就能够访问物理地址，无需再映射是内核模式的一个特点。内核模式的第二个特点是没有地址访问限制，内核模式线程可以访问任何有效虚拟地址，所谓有效虚拟地址是指有实际事物对应。&nbsp; <br>2、用户模式线程只能访问0x80000000以下的虚拟地址空间，WINCE6.0之前版本的内核为每个进程划分32MB的地址空间，在不调用特殊函数的情况下不能相互访问，这样的设计使得WINCE系统更安全、更稳定，限制访问地址是用户模式的第一个特点。第二个特点就是需要多一层映射，如果线程要访问物理内存的话需要先映射到0x80000000以上，再经MMU访问物理内存地址。&nbsp; <br>WINCE的线程具有转移性（参考API&nbsp;GetCallerProcess的说明，有一个很好的例子），当应用程序的线程调用API或者调用驱动程序接口函数时，该线程会转移到gwes.exe、device.exe、filesys.exe等进程中执行，转移是由WINCE内核操作的，它会修改线程的上下文，记录线程的当前进程、调用者进程、拥有者进程三个值。&nbsp; <br>3、如果在定制内核的时候选择了&#8220;Full&nbsp;Kernel&nbsp;Mode&#8221;，那么在这个内核上运行的所有线程都处于内核模式，即使调用SetKMode(FALSE)后线程仍然具有内核模式的特点，能够访问任何有效的虚拟地址。假设现有一个64MB&nbsp;RAM的WINCE产品，RAM映射从0x80000000到0x84000000，如果线程处于内核模式，它就直接可以访问这个范围的虚拟地址：&nbsp; <br>在OnButton1()中编写&nbsp; <br>DWORD&nbsp;oldMode&nbsp;=&nbsp;SetKMode(FALSE);&nbsp; <br>volatile&nbsp;int&nbsp;*piTemp&nbsp;=&nbsp;(volatile&nbsp;int*)(0x20000000+0x84000000-0x00019000);&nbsp;///或者(0x84000000-0x00019000)&nbsp; <br>*piTemp&nbsp;=&nbsp;12345;&nbsp; <br>在OnButton2()中编写&nbsp; <br>DWORD&nbsp;oldMode&nbsp;=&nbsp;SetKMode(FALSE);&nbsp; <br>volatile&nbsp;int&nbsp;*piTemp&nbsp;=&nbsp;(volatile&nbsp;int*)(0x20000000+0x84000000-0x00019000);&nbsp;///或者(0x84000000-0x00019000)&nbsp; <br>int&nbsp;iTemp&nbsp;=&nbsp;*piTemp;&nbsp; <br>先只执行OnButton1()然后关闭程序，再重启程序然后执行OnButton2()，iTemp仍然等于12345。结果说明了两点：内核模式线程可以直接访问0x80000000以上的有效虚拟地址；我们写到RAM中的数据没有丢失，说明虚拟地址有效。&nbsp; <br>如果在定制内核的时候没有选择&#8220;Full&nbsp;Kernel&nbsp;Mode&#8221;，那么在这个内核上运行的所有线程都处于用户模式。可以调用SetKMode(TRUE)使调用线程暂时处于内核模式，还是原来的假设环境，我再举个例子：&nbsp; <br>在OnButton1()中编写&nbsp; <br>DWORD&nbsp;oldMode&nbsp;=&nbsp;SetKMode(TRUE);&nbsp; <br>volatile&nbsp;int&nbsp;*piTemp&nbsp;=&nbsp;(volatile&nbsp;int*)(0x20000000+0x84000000-0x00019000);&nbsp;///或者(0x84000000-0x00019000)&nbsp; <br>*piTemp&nbsp;=&nbsp;12345;&nbsp; <br>在用户模式下，如果不调用SetKMode(TRUE)，那么执行*piTemp&nbsp;=&nbsp;12345一定会弹出对话框，提示地址访问非法，如果调用SetKMode(TRUE)就不会提示地址访问非法，而且在OnButton2()中仍然能得到12345这个值。&nbsp; <br>通过这两个例子我相信读者能够完全了解两种模式的区别了。&nbsp; <br>4、WINCE提供了两个函数SetKMode和SetProcPermissions，其中SetKMode能够把调用线程切换到内核模式，还可以切换回用户模式。SetProcPermissions&nbsp;+&nbsp;GetCurrentPermissions添加当前进程访问权限给调用线程，SetProcPermissions&nbsp;(0xFFFFFFFF)能让调用线程访问所有进程空间，但是调用线程仍然处于用户模式。SetKMode和SetProcPermissions函数使得用户模式的特点不那么明晰。&nbsp; <br>如上所说一个应用程序的线程可能转移到其它两个进程地址空间中读写数据，而每一个线程在被创建的时候只有访问创建它的进程地址空间的权限，所以驱动程序开发者必须在驱动程序读写数据前调用SetKMode或者SetProcPermissions增加调用此函数的线程访问其它进程空间的权限。如果一个应用程序的线程只转移到一个进程地址空间，一般为设备管理器进程device.exe，这种情况下不必增加线程访问其它进程空间的权限，但如果驱动程序本身创建了一个线程，那还是要调用SetKMode或者SetProcPermissions增加新的线程访问其它进程的权限的，因为驱动程序创建线程时，当前进程为设备管理器，所以新线程只具有访问设备管理器进程空间的权限，而不具备访问应用程序进程空间的权限。&nbsp; <br>5、可能一个编写过简单的流驱动的初学者会很疑惑，因为开发一个简单的流驱动程序根本不需要调用这些函数，也没有调用过MapPtrToProcess，那是因为如果标准流驱动接口函数的参数为指针（ReadFile、WriteFile、DeviceIoControl参数都有指针），WINCE内核会自动映射指针包含的地址，但仅此而已，其余任何情况都要求开发者自行处理，比如流接口函数的参数是一个指向结构体的指针PA，而结构体中包括指针PB，PB指针就必须在流接口函数中映射，映射后才能访问，否则就会造成地址访问非法。所以结构体中每个指针都要映射。&nbsp; <br>为了让读者能了解其中的原因，我举个例子：&nbsp; <br>假设设备管理器被加载到Slot4，应用程序A被加载到Slot&nbsp;8，A只有一个主线程T，T开始执行，按照WINCE的规定，正获得CPU的进程必须映射到Slot0，那么在执行代码的时候A的所有虚拟地址都被减去一个偏移值，也就是8&#215;0x02000000，A调用DeviceIoControl，传递一个指向一个结构体的指针B，而这个结构体中包含一个指针C，指针C包含的地址假设为0x00030000，当执行DeviceIoControl时WINCE把设备管理器的进程地址空间映射到Slot0，因为放在注册表[HKLM\Drivers\BuiltIn]下的驱动程序是由设备管理器加载的，自然驱动程序的代码段被加载到设备管理器进程空间，但是线程仍然是T，此时T的当前所在进程为设备管理器（CurrentProcess），A变成了T的调用者进程（CallerProcess），T自动具有了访问调用者进程空间的权限。这时访问Slot0中的虚拟地址其实质就是访问设备管理器的进程地址空间，要把地址加上一个偏移值，也就是4&#215;0x02000000，所以DeviceIoControl访问指针C包含的地址时本应该加上8&#215;0x02000000，却加上4&#215;0x02000000，结果地址并不是设备管理器的合法区域，系统就会提示地址访问非法。而如果做了一个映射，指针C包含的地址就会被加一个正确的偏移值，使地址处于A的地址空间Slot&nbsp;8中，T此时具有访问A进程空间的权限，访问到正确的虚拟地址当然会得到正确的数据了。&nbsp; <br>&nbsp; <br><strong>&nbsp;为什么WINCE目录下的例子用build+sysgen能够编译成EXE文件，而我添加的例子就不能编译呢？</strong>&nbsp; <br>如果这个例子是一个应用程序，那么肯定包括代码文件（.h&nbsp;.c&nbsp;.cpp）和资源文件（.rc和其它资源文件），build工具根据source文件内容把代码文件编译成lib文件，资源文件编译成.res文件，sysgen工具根据makefile文件内容将source文件中列出的需要链接的各个库文件合并成一个EXE文件。所以说关键在于makefile文件，WINCE目录下凡是能够用build+sysgen编译的都在makefile中有如何链接的设置，而我们添加的例子当然没有在makefile中找到如何链接的设置，nmake工具就会提示不知道如何创建。&nbsp; <br>&nbsp; <br>&nbsp;<strong>pcienum.exe干什么用的？</strong>&nbsp; <br>如果你要开发某一个PCI设备的驱动程序，首先要知道这个PCI设备的信息（如VendorID、DeviceID、BaseClass、SubClass）和PCI总线的信息。运行这个pcienum.exe就能得到相关信息。pcienum.exe提供了源码，位置\Public\Common\Oak\Drivers\Ceddk\Test\Pcienum。&nbsp; <br>&nbsp; <br>&nbsp;<strong>wince下如何让操作系统进入待机模式？又如何把它激活？</strong>&nbsp; <br>通过注册表就可以设置，前提是你的驱动和硬件都支持。注册表项参见标题为&#8220;GWES&nbsp;Suspend&nbsp;Time-outs&#8221;的帮助文档。&nbsp; <br>[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;"BattPowerOff"=dword:300&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;"ExtPowerOff"=dword:0&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;"WakeupPowerOff"=dword:60&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;"ScreenPowerOff"=dword:0&nbsp; <br>&nbsp; <br>&nbsp;<strong>现有一个GPRS模块，如何通过GPRS连接到Internet？</strong>&nbsp; <br>1、先在内核中加入WAN下面的几个组件，如RAS/PPP、TAPI。WINCE采用unimodem驱动，所以不必担心没有Modem驱动的支持。&nbsp; <br>2、WINCE启动后新建一个拨号连接，比如名称叫&#8220;gprs1&#8221;，输入用户名、密码、电话号码。电话号码不同，所采用的模式不一样，例如&#8220;*99#&#8221;是GPRS模式，&#8220;17201&#8221;是普通的数据模式，速度差很多，价钱也差很多。&nbsp; <br>3、开始连接，连接过程会在对话框中显示，直到显示&#8220;连接成功&#8221;。&nbsp; <br>4、打开浏览器或者自己开发的通讯软件测试网络连接情况。&nbsp; <br>5、关闭连接。&nbsp; <br>6、保存[HKEY_CURRENT_USER\Comm\RasBook\gprs1]下的所有数据，添加到project.reg中，重新编译后内核中就有了一个拨号连接&#8220;gprs1&#8221;。&nbsp; <br>7、调用RAS函数可以修改拨号连接&#8220;gprs1&#8221;的参数，如用户名、密码、电话号码，但是不能修改硬件设置，如波特率、串口、数据位、停止位等。RAS函数还能够拨号、挂断。为了修改波特率可以多保存几个拨号连接，也可以直接调用TAPI开发拨号软件，另外WINCE自带的拨号连接是有源码的，位置在\PUBLIC\COMMON\OAK\DRIVERS\NETSAMP\CONNMC。&nbsp; <br>&nbsp; <br>&nbsp;<strong>采用基于HIVE的注册表如何删除用户保存在注册表中的数据，恢复到出厂时的注册表？</strong>&nbsp; <br>用户修改的数据保存在user.hv文件中，直接删除一定失败，所以不能通过删除文件实现恢复出厂设置。微软考虑到了这个问题，在WINCE启动过程中filesys.exe加载注册表时会调用OEMIoControl函数并传递一个IOCTL，这个IOCTL在pkfuncs.h中定义如下：&nbsp; <br>#define&nbsp;&nbsp;IOCTL_HAL_GET_HIVE_CLEAN_FLAG&nbsp;&nbsp;CTL_CODE(FILE_DEVICE_HAL,&nbsp;49,&nbsp;METHOD_BUFFERED,&nbsp;FILE_ANY_ACCESS)&nbsp; <br>filesys.exe会分别传递参数HIVECLEANFLAG_SYSTEM和HIVECLEANFLAG_USERS，如果返回值为TRUE那么filesys.exe清除原来的注册表文件，如果返回值为FALSE那么filesys.exe保留原来的注册表文件。默认WINCE并没有实现这个IOCTL，所以OEM要删除注册表文件就必须先编写这个IOCTL代码。代码的例子可参考标题为&#8220;IOCTL_HAL_GET_HIVE_CLEAN_FLAG&#8221;的帮助文档。另外必须在ioctl.h和ioctl.c两个文件中编写该代码。在ioctl.c文件中找到const&nbsp;OAL_IOCTL_HANDLER&nbsp;g_oalIoCtlTable[]，添加IOCTL和对应的处理函数。要进一步了解这个全局数组，参见标题为&#8220;IOCTL&nbsp;Library&#8221;的帮助文档。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何在不删除必要组件的前提下减小内核文件长度？</strong>&nbsp; <br>要减小内核文件长度首先要在使用PB的定制内核向导中选择自定义，也就是说对于每个组件都由自己来选择，而不是选择PB的标准配置。但减小内核文件长度最有效最直接的办法是缩小字体，尤其对于东亚字体，采用字体压缩技术并且选择合理的字库文件将明显缩小文件长度。&nbsp; <br>1、在定制内核时选择AGFA&nbsp;AC3&nbsp;Font&nbsp;Compression组件。SYSGEN变量为SYSGEN_AGFA_FONT。&nbsp; <br>2、参考标题为&#8220;East&nbsp;Asian&nbsp;Font&nbsp;Versions&#8221;的帮助文档，从中选择你需要的字库文件加到内核中，从文档可以看出加AC3压缩比不加压缩在文件长度方面差距很大。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何得到WAV文件播放的总时间？</strong>&nbsp; <br>1、直接读取wav文件头信息，从文件起始地址偏移28个字节长度为4个字节保存的是每秒钟播放的字节数，从文件起始地址偏移40个字节长度为4个字节保存的是声音数据的总的字节数，相除就是播放时间。&nbsp; <br>2、调用IGraphBuilder::RenderFile打开一个wav文件，然后通过IGraphBuilder得到IMediaSeeking指针，再调用IMediaSeeking::GetDuration得到总的时间（结果要除以10000000），IMediaSeeking::GetCurrentPosition得到当前播放时间。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何在Dialog-Based程序中加入menubar？</strong>&nbsp; <br>先调用CommandBar_Create再调用CommandBar_InsertMenubar。&nbsp; <br>&nbsp; <br>&nbsp;<strong>请问MultiByteToWideChar与_T、L、TEXT的区别？</strong>&nbsp; <br>MultiByteToWideChar函数转换的对象可以是常量也可以是变量。其它只能转换常量。_T和TEXT会根据当前系统是否定义_UNICODE宏来决定是否转换，而L就是转换成宽字符，当然也包括其他类型常量的转换。&nbsp; <br>&nbsp; <br>&nbsp;<strong>在用UBS线缆通过ActiveSync同步有效的情况下，如何插上USB线缆后WINCE自动与PC同步？</strong>&nbsp; <br>1、新建一个拨号连接，假设名称为&#8220;usb1&#8221;，选择连接类型为&#8220;直接连接&#8221;，并在连接设备里选择通过USB线缆连接。&nbsp; <br>2、将注册表[HKEY_CURRENT_USER\Comm\RasBook\usb1]下的数据添加到project.reg或者platform.reg中。&nbsp; <br>3、在[HKEY_CURRENT_USER\ControlPanel\Comm]下添加如下：&nbsp; <br>"AutoCnct"=dword:1&nbsp;&nbsp;///直接连接&nbsp; <br>"Cnct"="usb1"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///连接名称&nbsp; <br>4、重新编译内核。为了节省编译时间也可以在内核工程下搜索*.reg文件，将2、3步骤中的注册表数据添加其中，然后直接make&nbsp;image。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何通过进程句柄来获得该进程的主窗口句柄？</strong>&nbsp; <br>好像没有API能够通过进程句柄直接获得主窗口的句柄，因为并非每个应用程序都带UI。但是可以反过来，先枚举当前系统所有主窗口，然后根据每个窗口的句柄调用GetWindowThreadProcessId函数得到进程的ID，再调用OpenProcess得到进程句柄，与现有的进程句柄比较。&nbsp; <br>&nbsp; <br>&nbsp;我做的显示驱动DLL已经编译成功了，但是在加载显示驱动的过程中弹出话框，提示如下：&nbsp; <br>unhandled&nbsp;exception&nbsp;&nbsp;in&nbsp;&nbsp;gwes.exe&nbsp;&nbsp;(0xc0000005&nbsp;access&nbsp;violation)&nbsp; <br>提示的错误——地址访问非法，表明你的驱动程序代码并没有在读写数据前添加SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数让线程能够访问任何进程的地址空间。你可以调用&nbsp;IsBadReadPtr和IsBadWritePtr函数检测地址是否能够合法访问。编写和gwes有关的驱动程序应该首先调用SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数，这是一个好习惯。&nbsp; <br>&nbsp; <br><strong>&nbsp;请问在嵌入式系统中如何设置GPRS拔号用的APN？</strong>&nbsp; <br>对一个拨号连接比如&#8220;我的连接&#8221;单击鼠标右键，在弹出的菜单中选择&#8220;属性&#8221;，然后单击&#8220;配置&#8221;—&#8220;拨号选项&#8221;，在&#8220;附加设置&#8221;中添加AT命令如&#8220;+cgdcont=1,"ip","cmnet"&#8221;。&#8220;cmnet&#8221;位置即为APN。&nbsp; <br>&nbsp; <br>&nbsp;<strong>WINCE的IP&nbsp;Phone功能如何？</strong>&nbsp; <br>WINCE的voip需要c-s-c结构，既需要服务器的中转，而skype采用第三代p2p技术就不需要中转，但是在gprs下也做不到语音流畅。skype有pocket&nbsp;pc版本，但是无线方面需要wlan或者cdma。&nbsp; <br>&nbsp; <br>&nbsp;<strong>三星ARM平台如何定义自己的中断ID？</strong>&nbsp; <br>以S3C2410为例，在oalintr.h文件中定义中断ID，也称SYSINTR，例如&nbsp;#define&nbsp;SYSINTR_MYINT&nbsp;&nbsp;(SYSINTR_FIRMWARE+20)，最大值不能超过SYSINTR_FIRMWARE+23。然后在armint.c文件中找到OEMInterruptHandler函数，用if&nbsp;(IntPendVal&nbsp;==&nbsp;INTSRC_XXX)&nbsp;判断当前发生的中断源号，然后返回SYSINTR_MYINT。内核分别调用OEMInterruptDisable（禁止当前中断）、OEMInterruptDone（中断处理结束）、OEMInterruptEnable（当前中断有效）三个函数，参数都为中断ID，在这三个函数中用&nbsp;case&nbsp;SYSINTR_MYINT判断当前要处理的中断。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何开发软件从PC端复制文件到基于WINCE的设备？</strong>&nbsp; <br>调用RAPI（Remote&nbsp;Application&nbsp;Programming&nbsp;Interface）函数，此函数集由桌面计算机调用，由基于WINCE的设备执行。一旦连接上就可以在桌面计算机端调用RAPI。通过注册表还可以限制RAPI能够访问目录的范围。具体参考RAPI和RDP（远程桌面协议）。&nbsp; <br>&nbsp; <br>&nbsp;<strong>请问如何对NandFlash分区、格式化？</strong>&nbsp; <br>你看看WINCE420\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cpp，在Eboot中先要调用BP_LowLevelFormat(&nbsp; <br>DWORD&nbsp;dwStartBlock,&nbsp;DWORD&nbsp;dwNumBlocks,&nbsp;DWORD&nbsp;dwFlags)再flash的一个区域建立空的MBR,然后连续两次调用BP_OpenPartition(DWORD&nbsp;dwStartSector,&nbsp;DWORD&nbsp;dwNumSectors,&nbsp;DWORD&nbsp;dwPartType,&nbsp;BOOL&nbsp;fActive,&nbsp;DWORD&nbsp;dwCreationFlags)函数来建立BINFS和FAT分区。建好后，将nk.bin烧入binfs分区中。&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;<strong>要做个弹出对话框具有&nbsp;always&nbsp;on&nbsp;top&nbsp;属性，如何实现？</strong>&nbsp; <br>调用SetWindowPos(..&nbsp;,&nbsp;HWND_TOPMOST,&nbsp;....,&nbsp;SWP_NOACTIVATE)。&nbsp; <br>&nbsp; <br>&nbsp;<strong>s3c2410＋WINCE下网络PING一会就断，如何解决？</strong>&nbsp; <br>原因在于中断处理程序把已经产生的中断标志清除掉了，这样就丢失一次中断。因为原驱动里配置中断为上升沿触发，一次中断丢失就导致不会再产生中断信号跳变，因为只有在中断服务中读取了cs8900的&nbsp;Interrupt&nbsp;status&nbsp;queue寄存器后，才会产生下一次中断！解决办法：&nbsp; <br>1、在cfw.c文件中全局定义BOOL&nbsp;Inited&nbsp;=&nbsp;FALSE&nbsp; <br>2、修改OEMInterruptEnable()中case&nbsp;SYSINTR_ETHER:&nbsp;下面的语句为：&nbsp; <br>if(Inited&nbsp;==&nbsp;FALSE)&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>s2410IOP-&gt;rEINTPEND&nbsp;=&nbsp;0x200;&nbsp;&nbsp; <br>s2410INT-&gt;rSRCPND&nbsp;=&nbsp;BIT_EINT8_23;&nbsp;&nbsp; <br>if&nbsp;(s2410INT-&gt;rINTPND&nbsp;&amp;&nbsp;BIT_EINT8_23)&nbsp;&nbsp; <br>s2410INT-&gt;rINTPND&nbsp;=&nbsp;BIT_EINT8_23;&nbsp;&nbsp; <br>Inited&nbsp;=&nbsp;TRUE;&nbsp;&nbsp; <br>}&nbsp;&nbsp; <br>s2410IOP-&gt;rEINTMASK&nbsp;&amp;=&nbsp;~0x200;&nbsp;&nbsp; <br>s2410INT-&gt;rINTMSK&nbsp;&amp;=&nbsp;~BIT_EINT8_23;&nbsp;&nbsp; <br>break；&nbsp;&nbsp; <br>注：本解决办法转载于http://stoned.blogchina.com/stoned/3083045.html，非我本人研究成果。&nbsp; <br>&nbsp; <br>&nbsp;<strong>已经搜索到文件，如何用CListBox以图标形式显示出来？</strong>&nbsp; <br>CListCtrl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ListCtrl;&nbsp; <br>CImageList&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ImageList;&nbsp; <br>ImageList.Create(IDB_BITMAP,&nbsp;48,&nbsp;2,&nbsp;RGB(0,0,0));&nbsp; <br>ListCtrl.SetImageList(&amp;ImageList,&nbsp;LVSIL_NORMAL);&nbsp; <br>ListCtrl.InsertItem(iListIndex,&nbsp;strItem,&nbsp;1);&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何改变控制面板中电源属性对话框的尺寸?</strong>&nbsp;&nbsp; <br>1、需要修改对话框的尺寸是因为对话框是以资源方式加载的，不会根据当前系统显示分辨率而自我调节尺寸。&nbsp; <br>2、安装WINCE后有一些组件（feature）的资源文件*.res就已经有了，如果你不改变，那么build内核的时候PB只是把这些.res复制到工程目录下，然后与*.obj合并成EXE、DLL、CPL。所以修改了.rc文件里面的对话框尺寸后要重新编译.rc文件为.res文件，然后再覆盖原来WINCE自带的.res文件。&nbsp; <br>3、改变对话框尺寸有两种办法：一种方法是更改系统字体字号，系统字体的字号变化会影响对话框的尺寸，但是缺点是所有系统字体有关的UI都会改变。另一种是在.rc文件中调整对话框尺寸，然后编译成.res文件，再将.res复制到对应的语言目录里，比如目录名为0804（中文），再执行Rebuild命令重新编译内核，或者执行sysgen＋build。在研究中我发现.res文件虽然能够直接用EVC打开、修改、保存，但是和其它Obj链接成EXE、DLL、CPL后并不能运行，所以还是建议读者用CE自带的rc工具编译最好。读者可在PB的命令行中键入&#8220;rc&nbsp;/?&#8221;了解rc.exe工具的用途和参数。&nbsp; <br>&nbsp; <br>&nbsp;<strong>使用EVC&nbsp;build之后连接模拟器的时候，提示download&nbsp;file等了一会又出现download&nbsp;failed？</strong>&nbsp; <br>一般这样的问题从下面几个步骤解决：&nbsp; <br>1、如果之前能启动模拟器而现在不能，那么先clean然后重启计算机再build。&nbsp; <br>2、如果开发的主机为WINXP+SP2，可能存在与EVC模拟器不兼容的情况，检查C:\boot.ini，将/noexecute=optin改为/execute=optin。&nbsp; <br>3、检查你的模拟器是否能运行，假设你正用的SDK名称为MYSDK，单击菜单tools—configure&nbsp;platform&nbsp;manager，选择MYSDK—MYSDK&nbsp;emulator，再单击properties—test，看看模拟器是否能够启动，如果能启动那问题就不大。&nbsp; <br>4、单击菜单build—update&nbsp;remote&nbsp;output&nbsp;files，看看模拟器是否能够启动。&nbsp; <br>5、如果上述办法均不行，关闭EVC然后重新建立一个新的工程，编译，看看模拟器是否能够启动，如果能启动说明原来工程出了问题，最好恢复原工程的备份。&nbsp; <br>&nbsp; <br>&nbsp;<strong>如何设置能够自动拨号、禁止自动拨号？</strong>&nbsp; <br>在[HKEY_LOCAL_MACHINE\Comm\Autodial]下是自动拨号的注册表设置。&nbsp; <br>Enabled=DWORD:1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///是否能够自动拨号&nbsp; <br>FailRetryWaitMS=DWORD&nbsp;&nbsp;&nbsp;&nbsp;///如果失败再次拨号的等待时间&nbsp; <br>RasEntryName1=&nbsp;REG_SZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///自动拨号采用的拨号连接名称&nbsp; <br>更多细节请参考标题为&#8220;Auto&nbsp;Dial&nbsp;Registry&nbsp;Settings&#8221;的帮助文档。&nbsp;
<img src ="http://www.cppblog.com/kuronca/aggbug/49332.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2008-05-09 17:24 <a href="http://www.cppblog.com/kuronca/archive/2008/05/09/49332.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> (转)成为嵌入式程序员应知道的10个基本问题 </title><link>http://www.cppblog.com/kuronca/archive/2008/05/09/49330.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Fri, 09 May 2008 09:22:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2008/05/09/49330.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/49330.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2008/05/09/49330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/49330.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/49330.html</trackback:ping><description><![CDATA[C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年，我既参加也组织了许多这种测试，在这过程中我意识到这些测试能为面试者和被面试者提供许多有用信息，此外，撇开面试的压力不谈，这种测试也是相当有趣的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从被面试者的角度来讲，你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗？这是个愚蠢的问题吗？如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗？这标志着出题者也许花时间在微机上而不是在嵌入式系统上。如果上述任何问题的答案是"是"的话，那么我知道我得认真考虑我是否应该去做这份工作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从面试者的角度来讲，一个测试也许能从多方面揭示应试者的素质：最基本的，你能了解应试者C语言的水平。不管怎么样，看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择，还是只是瞎蒙呢？当应试者在某个问题上卡住时是找借口呢，还是表现出对问题的真正的好奇心，把这看成学习的机会呢？我发现这些信息与他们的测试成绩一样有用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有了这些想法，我决定出一些真正针对嵌入式系统的考题，希望这些令人头痛的考题能给正在找工作的人一点帮助。这些问题都是我这些年实际碰到的。其中有些题很难，但它们应该都能给你一点启迪。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个测试适于不同水平的应试者，大多数初级水平的应试者的成绩会很差，经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好，每个问题没有分配分数，如果选择这些考题为你所用，请自行按你的意思分配分数。<br>&nbsp;<br><strong>&nbsp;预处理器（Preprocessor）</strong><br>&nbsp;<br>&nbsp;1 . 用预处理指令#define 声明一个常数，用以表明1年中有多少秒（忽略闰年问题）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL<br>&nbsp;我在这想看到几件事情：<br>&nbsp;1) #define 语法的基本知识（例如：不能以分号结束，括号的使用，等等）<br>&nbsp;2)懂得预处理器将为你计算常数表达式的值，因此，直接写出你是如何计算一年中有多少秒而不是计算出实际的值，是更清晰而没有代价的。<br>&nbsp;3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。<br>&nbsp;4) 如果你在你的表达式中用到UL（表示无符号长整型），那么你有了一个好的起点。记住，第一印象很重要。<br>&nbsp;<br>&nbsp;2 . 写一个"标准"宏MIN ，这个宏输入两个参数并返回较小的一个。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #define MIN(A,B) （（A） &lt;= (B) ? (A) : (B)) <br>&nbsp;这个测试是为下面的目的而设的：<br>&nbsp;1) 标识#define在宏中应用的基本知识。这是很重要的。因为在&nbsp; 嵌入(inline)操作符 变为标准C的一部分之前，宏是方便产生嵌入代码的唯一方法，对于嵌入式系统来说，为了能达到要求的性能，嵌入代码经常是必须的方法。<br>&nbsp;2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码，了解这个用法是很重要的。<br>&nbsp;3) 懂得在宏中小心地把参数用括号括起来<br>&nbsp;4) 我也用这个问题开始讨论宏的副作用，例如：当你写下面的代码时会发生什么事？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; least = MIN(*p++, b);<br>&nbsp;<br>&nbsp;3. 预处理器标识#error的目的是什么？<br>&nbsp;如果你不知道答案，请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子，那么应试者最好希望自己不要知道答案。<br>&nbsp;<br>&nbsp;<br><strong>&nbsp;死循环（Infinite loops）</strong><br>&nbsp;<br>&nbsp;4. 嵌入式系统中经常要用到无限循环，你怎么样用C编写死循环呢？<br>&nbsp;这个问题用几个解决方案。我首选的方案是：<br>&nbsp;<br>&nbsp;while(1)<br>&nbsp;{<br>&nbsp;<br>&nbsp;}<br>&nbsp;<br>&nbsp;一些程序员更喜欢如下方案：<br>&nbsp;<br>&nbsp;for(;;)<br>&nbsp;{<br>&nbsp;<br>&nbsp;}<br>&nbsp;<br>&nbsp;这个实现方式让我为难，因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案，我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是："我被教着这样做，但从没有想到过为什么。"这会给我留下一个坏印象。<br>&nbsp;<br>&nbsp;第三个方案是用 goto<br>&nbsp;Loop:<br>&nbsp;...<br>&nbsp;goto Loop;<br>&nbsp;应试者如给出上面的方案，这说明或者他是一个汇编语言程序员（这也许是好事）或者他是一个想进入新领域的BASIC/FORTRAN程序员。<br>&nbsp;<br>&nbsp;<br>&nbsp;<strong>数据声明（Data declarations）</strong> <br>&nbsp;<br>&nbsp;5. 用变量a给出下面的定义<br>&nbsp;a) 一个整型数（An integer） <br>&nbsp;b)一个指向整型数的指针（ A pointer to an integer） <br>&nbsp;c)一个指向指针的的指针，它指向的指针是指向一个整型数（ A pointer to a pointer to an intege）r <br>&nbsp;d)一个有10个整型数的数组（ An array of 10 integers） <br>&nbsp;e) 一个有10个指针的数组，该指针是指向一个整型数的。（An array of 10 pointers to integers） <br>&nbsp;f) 一个指向有10个整型数数组的指针（ A pointer to an array of 10 integers） <br>&nbsp;g) 一个指向函数的指针，该函数有一个整型参数并返回一个整型数（A pointer to a function that takes an integer as an argument and returns an integer） <br>&nbsp;h) 一个有10个指针的数组，该指针指向一个函数，该函数有一个整型参数并返回一个整型数（ An array of ten pointers to functions that take an integer argument and return an integer ）<br>&nbsp;<br>&nbsp;答案是： <br>&nbsp;a) int a; // An integer <br>&nbsp;b) int *a; // A pointer to an integer <br>&nbsp;c) int **a; // A pointer to a pointer to an integer <br>&nbsp;d) int a[10]; // An array of 10 integers <br>&nbsp;e) int *a[10]; // An array of 10 pointers to integers <br>&nbsp;f) int (*a)[10]; // A pointer to an array of 10 integers <br>&nbsp;g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer <br>&nbsp;h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 人们经常声称这里有几个问题是那种要翻一下书才能回答的问题，我同意这种说法。当我写这篇文章时，为了确定语法的正确性，我的确查了一下书。但是当我被面试的时候，我期望被问到这个问题（或者相近的问题）。因为在被面试的这段时间里，我确定我知道这个问题的答案。应试者如果不知道所有的答案（或至少大部分答案），那么也就没有为这次面试做准备，如果该面试者没有为这次面试做准备，那么他又能为什么出准备呢？<br>&nbsp;<br>&nbsp;<strong>Static</strong> <br>&nbsp;<br>&nbsp;6. 关键字static的作用是什么？<br>&nbsp;这个简单的问题很少有人能回答完全。在C语言中，关键字static有三个明显的作用：<br>&nbsp;1)在函数体，一个被声明为静态的变量在这一函数被调用过程中维持其值不变。<br>&nbsp;2) 在模块内（但在函数体外），一个被声明为静态的变量可以被模块内所用函数访问，但不能被模块外其它函数访问。它是一个本地的全局变量。<br>&nbsp;3) 在模块内，一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是，这个函数被限制在声明它的模块的本地范围内使用。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 大多数应试者能正确回答第一部分，一部分能正确回答第二部分，同是很少的人能懂得第三部分。这是一个应试者的严重的缺点，因为他显然不懂得本地化数据和代码范围的好处和重要性。<br>&nbsp;<br>&nbsp;<br><strong>&nbsp;Const</strong> <br>&nbsp;<br>&nbsp;7．关键字const有什么含意？<br>&nbsp;&nbsp;&nbsp;&nbsp; 我只要一听到被面试者说："const意味着常数"，我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法，因此ESP(译者：Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章，只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案，但我接受它作为一个正确的答案。（如果你想知道更详细的答案，仔细读一下Saks的文章吧。）<br>&nbsp;&nbsp;&nbsp; 如果应试者能正确回答这个问题，我将问他一个附加的问题：<br>&nbsp;&nbsp;&nbsp; 下面的声明都是什么意思？<br>&nbsp;<br>&nbsp;const int a;<br>&nbsp;int const a;<br>&nbsp;const int *a;<br>&nbsp;int * const a;<br>&nbsp;int const * a const;<br>&nbsp;<br>&nbsp;/******/<br>&nbsp;&nbsp;&nbsp; 前两个的作用是一样，a是一个常整型数。第三个意味着a是一个指向常整型数的指针（也就是，整型数是不可修改的，但指针可以）。第四个意思a是一个指向整型数的常指针（也就是说，指针指向的整型数是可以修改的，但指针是不可修改的）。最后一个意味着a是一个指向常整型数的常指针（也就是说，指针指向的整型数是不可修改的，同时指针也是不可修改的）。如果应试者能正确回答这些问题，那么他就给我留下了一个好印象。顺带提一句，也许你可能会问，即使不用关键字 const，也还是能很容易写出功能正确的程序，那么我为什么还要如此看重关键字const呢？我也如下的几下理由：<br>&nbsp;1) 关键字const的作用是为给读你代码的人传达非常有用的信息，实际上，声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾，你就会很快学会感谢这点多余的信息。（当然，懂得用const的程序员很少会留下的垃圾让别人来清理的。）<br>&nbsp;2) 通过给优化器一些附加的信息，使用关键字const也许能产生更紧凑的代码。<br>&nbsp;3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数，防止其被无意的代码修改。简而言之，这样可以减少bug的出现。<br>&nbsp;<br>&nbsp;<br><strong>&nbsp;Volatile</strong> <br>&nbsp;<br>&nbsp;8. 关键字volatile有什么含意?并给出三个不同的例子。<br>&nbsp;&nbsp;&nbsp; 一个定义为volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。精确地说就是，优化器在用到这个变量时必须每次都小心地重新读取这个变量的值，而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子：<br>&nbsp;1) 并行设备的硬件寄存器（如：状态寄存器）<br>&nbsp;2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)<br>&nbsp;3) 多线程应用中被几个任务共享的变量<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道，所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。<br>&nbsp;&nbsp;&nbsp; 假设被面试者正确地回答了这是问题（嗯，怀疑是否会是这样），我将稍微深究一下，看一下这家伙是不是直正懂得volatile完全的重要性。<br>&nbsp;1)一个参数既可以是const还可以是volatile吗？解释为什么。<br>&nbsp;2); 一个指针可以是volatile 吗？解释为什么。<br>&nbsp;3); 下面的函数有什么错误：<br>&nbsp;<br>&nbsp;int square(volatile int *ptr)<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return *ptr * *ptr;<br>&nbsp;}<br>&nbsp;<br>&nbsp;下面是答案：<br>&nbsp;1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。<br>&nbsp;2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。<br>&nbsp;3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码：<br>&nbsp;<br>&nbsp;int square(volatile int *ptr) <br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; int a,b;<br>&nbsp;&nbsp;&nbsp;&nbsp; a = *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp; b = *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp; return a * b;<br>&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 由于*ptr的值可能被意想不到地该变，因此a和b可能是不同的。结果，这段代码可能返不是你所期望的平方值！正确的代码如下：<br>&nbsp;<br>&nbsp;long square(volatile int *ptr) <br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp;&nbsp; a = *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp; return a * a;<br>&nbsp;}<br>&nbsp;<br><strong>&nbsp;位操作（Bit manipulation）</strong> <br>&nbsp;<br>&nbsp;9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a 的bit 3。在以上两个操作中，要保持其它位不变。<br>&nbsp;&nbsp; &nbsp;对这个问题有三种基本的反应<br>&nbsp;1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。<br>&nbsp;2) 用bit fields。Bit fields是被扔到C语言死角的东西，它保证你的代码在不同编译器之间是不可移植的，同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序，它用到了bit fields因此完全对我无用，因为我的编译器用其它的方式来实现bit fields的。从道德讲：永远不要让一个非嵌入式的家伙粘实际硬件的边。<br>&nbsp;3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法，是应该被用到的方法。最佳的解决方案如下：<br>&nbsp;<br>&nbsp;#define BIT3 (0x1 &lt;&lt; 3)<br>&nbsp;static int a;<br>&nbsp;<br>&nbsp;void set_bit3(void) <br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; a |= BIT3;<br>&nbsp;}<br>&nbsp;void clear_bit3(void) <br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; a &amp;= ~BIT3;<br>&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp; &nbsp;一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数，这也是可以接受的。我希望看到几个要点：说明常数、|=和&amp;=~操作。<br>&nbsp;<br>&nbsp;<br>&nbsp;<strong>访问固定的内存位置（Accessing fixed memory ***s）</strong> <br>&nbsp;<br>&nbsp;10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中，要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。<br>&nbsp;&nbsp;&nbsp; 这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换（typecast）为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下：<br>&nbsp;&nbsp;&nbsp;&nbsp; int *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp; ptr = (int *)0x67a9;<br>&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0xaa55;<br>&nbsp;<br>&nbsp; A more obscure approach is: <br>&nbsp;一个较晦涩的方法是：<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; *(int * const)(0x67a9) = 0xaa55;<br>&nbsp;<br>&nbsp;即使你的品味更接近第二种方案，但我建议你在面试时使用第一种方案。<br>&nbsp;<br><strong>&nbsp;中断（Interrupts）</strong> <br>&nbsp;<br>&nbsp;11. 中断是嵌入式系统中重要的组成部分，这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是，产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)，请评论一下这段代码的。<br>&nbsp;<br>&nbsp;__interrupt double compute_area (double radius) <br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; double area = PI * radius * radius;<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("\nArea = %f", area);<br>&nbsp;&nbsp;&nbsp;&nbsp; return area;<br>&nbsp;}<br>&nbsp;<br>&nbsp;这个函数有太多的错误了，以至让人不知从何说起了：<br>&nbsp;1)ISR 不能返回一个值。如果你不懂这个，那么你不会被雇用的。<br>&nbsp;2) ISR 不能传递参数。如果你没有看到这一点，你被雇用的机会等同第一项。<br>&nbsp;3) 在许多的处理器/编译器中，浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈，有些处理器/编译器就是不允许在ISR中做浮点运算。此外，ISR应该是短而有效率的，在ISR中做浮点运算是不明智的。<br>&nbsp;4) 与第三点一脉相承，printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点，我不会太为难你的。不用说，如果你能得到后两点，那么你的被雇用前景越来越光明了。<br>&nbsp;<br>&nbsp;<br><strong>&nbsp;代码例子（Code examples）</strong><br>&nbsp;<br>&nbsp;12 . 下面的代码输出是什么，为什么？<br>&nbsp;<br>&nbsp;void foo(void)<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; unsigned int a = 6;<br>&nbsp;&nbsp;&nbsp;&nbsp; int b = -20;<br>&nbsp;&nbsp;&nbsp;&nbsp; (a+b &gt; 6) ? puts("&gt; 6") : puts("&lt;= 6");<br>&nbsp;}<br>&nbsp;&nbsp;&nbsp; 这个问题测试你是否懂得C语言中的整数自动转换原则，我发现有些开发者懂得极少这些东西。不管如何，这无符号整型问题的答案是输出是 "&gt;6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数，所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题，你也就到了得不到这份工作的边缘。<br>&nbsp;<br>&nbsp;13. 评价下面的代码片断：<br>&nbsp;<br>&nbsp;unsigned int zero = 0;<br>&nbsp;unsigned int compzero = 0xFFFF; <br>&nbsp;/*1's complement of zero */<br>&nbsp;<br>&nbsp;对于一个int型不是16位的处理器为说，上面的代码是不正确的。应编写如下：<br>&nbsp;<br>&nbsp;unsigned int compzero = ~0;<br>&nbsp;<br>&nbsp;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里，好的嵌入式程序员非常准确地明白硬件的细节和它的局限，然而PC机程序往往把硬件作为一个无法避免的烦恼。<br>&nbsp;到了这个阶段，应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好，那么这个测试就在这里结束了。但如果显然应试者做得不错，那么我就扔出下面的追加问题，这些问题是比较难的，我想仅仅非常优秀的应试者能做得不错。提出这些问题，我希望更多看到应试者应付问题的方法，而不是答案。不管如何，你就当是这个娱乐吧...<br>&nbsp;<br>&nbsp;<br>&nbsp;<strong>动态内存分配（Dynamic memory al***）</strong> <br>&nbsp;<br>&nbsp;14. 尽管不像非嵌入式计算机那么常见，嵌入式系统还是有从堆（heap）中动态分配内存的过程的。那么嵌入式系统中，动态分配内存可能发生的问题是什么？<br>&nbsp;&nbsp;&nbsp; 这里，我期望应试者能提到内存碎片，碎片收集的问题，变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了（主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释），所有回过头看一下这些杂志吧！让应试者进入一种虚假的安全感觉后，我拿出这么一个小节目：<br>&nbsp;&nbsp;&nbsp; 下面的代码片段的输出是什么，为什么？<br>&nbsp;<br>&nbsp;char *ptr;<br>&nbsp;if ((ptr = (char *)malloc(0)) == NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp; puts("Got a null pointer");<br>&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp; puts("Got a valid pointer");<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc，得到了一个合法的指针之后，我才想到这个问题。这就是上面的代码，该代码的输出是"Got a valid pointer"。我用这个来开始讨论这样的一问题，看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要，但解决问题的方法和你做决定的基本原理更重要些。<br>&nbsp;<br><strong>&nbsp;Typedef</strong> <br>&nbsp; <br>&nbsp;15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如，思考一下下面的例子：<br>&nbsp;<br>&nbsp;#define dPS struct s *<br>&nbsp;typedef struct s * tPS;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢？（如果有的话）为什么？<br>&nbsp;&nbsp;&nbsp; 这是一个非常微妙的问题，任何人答对这个问题（正当的原因）是应当被恭喜的。答案是：typedef更好。思考下面的例子：<br>&nbsp;<br>&nbsp;dPS p1,p2;<br>&nbsp;tPS p3,p4;<br>&nbsp;<br>&nbsp;第一个扩展为<br>&nbsp;<br>&nbsp;struct s * p1, p2;<br>&nbsp;.<br>&nbsp;&nbsp;&nbsp; 上面的代码定义p1为一个指向结构的指，p2为一个实际的结构，这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。<br>&nbsp;<br>&nbsp;<br>&nbsp;<br><strong>&nbsp;晦涩的语法</strong><br>&nbsp;<br>&nbsp;16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗，如果是它做些什么？<br>&nbsp;<br>&nbsp;int a = 5, b = 7, c;<br>&nbsp;c = a+++b;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 这个问题将做为这个测验的一个愉快的结尾。不管你相不相信，上面的例子是完全合乎语法的。问题是编译器如何处理它？水平不高的编译作者实际上会争论这个问题，根据最处理原则，编译器应当能处理尽可能所有合法的用法。因此，上面的代码被处理成：<br>&nbsp;<br>&nbsp;c = a++ + b;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 因此, 这段代码持行后a = 6, b = 7, c = 12。<br>&nbsp;&nbsp;&nbsp; 如果你知道答案，或猜出正确答案，做得好。如果你不知道答案，我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格，代码的可读性，代码的可修改性的好的话题。<br>&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 好了，伙计们，你现在已经做完所有的测试了。这就是我出的C语言测试题，我怀着愉快的心情写完它，希望你以同样的心情读完它。如果是认为这是一个好的测试，那么尽量都用到你的找工作的过程中去吧。天知道也许过个一两年，我就不做现在的工作，也需要找一个。<br>
<img src ="http://www.cppblog.com/kuronca/aggbug/49330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2008-05-09 17:22 <a href="http://www.cppblog.com/kuronca/archive/2008/05/09/49330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>水煮TCPMP   (转)</title><link>http://www.cppblog.com/kuronca/archive/2007/11/23/37188.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Fri, 23 Nov 2007 02:28:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/11/23/37188.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/37188.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/11/23/37188.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/37188.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/37188.html</trackback:ping><description><![CDATA[<p>去年年底帮别人做一个项目，了解了一下TCPMP，觉得这个软件的结构写得很好就做了些记录，今天偶然翻出来看看觉得可能对正在研究这个软件的人有点帮助就贴出来。如果转载请注明出处，谢谢。</p>
<p>TCPMP是一个功能强大开放式的开源多媒体播放器，<br>播放器主要由核心框架模块（common工程）和解码器分离器插件组成。<br>TCPMP的插件非常多，、libmad我们联合几个最常用的插件（ffmpeg、splitter）来说明，其中interface插件实现TCPMP的界面，由于他和媒体播放没有什么关系，这部分可以完全被替换掉，替换成自己的界面。<br>ffmpeg工程是系统主要的音视频解码模块，ffmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统，但是可以在大多数操作系统中编译和使用。ffmpeg支持MPEG、DivX、MPEG4、AC3、DV、FLV等40多种编码，AVI、MPEG、OGG、Matroska、ASF等90多种解码。很多开源播放器都用到了ffmpeg。但是ffmpeg程序解码效率不是很高，系统仅仅使用了FFmpeg的部分解码功能。<br>ffmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录。其中libavcodec用于存放各个encode/decode模块，libavformat用于存放muxer/demuxer模块，libavutil用于存放内存操作等常用模块。本系统的媒体文件分离器有单独的splitter模块完成所以不需要libavformat子目录。ffmpeg目录下libavcodec、libavutil保留子目录。</p>
<p>libmad工程用于MP3文件解码，该工程包含两个功能模块，一个负责解析MP3文件格式，包括MPEG1音频文件 (MP1,MP2,MP3,MPA)，读取每一帧音频数据；另一个负责解码MPEG1音频数据，解码代码在libmad子目录中。<br>libmad是一个开源的高精度 MPEG1音频解码库，支持 MPEG-1（Layer I, Layer II 和 LayerIII，也就是 MP3）。libmad提供 24-bit 的 PCM 输出，完全是定点计算，非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API，就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中，可以看到绝大部分该库的数据结构和 API 等。libmad是用的fixed-integer，通过整数模拟小数计算的，精度只能保证到小数点后第9位（大于0的最小值 0.00000000372529），虽然解码精度会有损失，但是极大提高了解码效率，特别是在嵌入式设备上也可以实现高码率MP3文件的解码。</p>
<p>splitter工程用于解析多种音视频文件格式。可以解析的文件格式包括：ASF媒体文件，视频文件 (AVI,DIVX)，Windows波形文件 (WAV,RMP)，MPEG电影文件 (MPEG,MPG,MPV)，MPEG4文件 (MP4,3GP,M4A,M4B,K3G)。以上格式可以被解析但是数据编码不一定能正确解码，需要依赖系统的解码器。</p>
<p>common工程是核心模块，是一个开放的集数据输入、转换、音/视频解码、信号输出等功能为一体的完整的多媒体播放框架。这个框架自身不包含任何的Decode和Split功能，这些功能由插件实现，核心模块以一个树状结构管理所有的功能模块和插件模块，实现数据Render功能，对输入、转换、输出流程的控制，接受播放过程中的操作和对事件进行处理，同时也实现系统运行中经常使用的一些共用函数，比如解码过程中经常使用的逆离散余弦变换，内存操作，界面中需要使用的多语言字符处理等。common工程的主目录下主要有：blit、dyncode、overlay、pcm、softidct、win32、zlib等子目录。其中blit和overlay存放是视频信号渲染模块，pcm存放PCM音频信号转换模块，softidct存放逆离散余弦变换函数，win32存放内存操作等常用模块，dyncode这个目录的代码比较晦涩，存放的是程序运行是动态生成代码模块，针对不同的CPU指令集，PCM数据数据声道和采样率不同，视频渲染数据格式和色深等不同情况动态生成不同的优化代码（这段代码非常精彩，不能不让人佩服TCPMP作者的高超水平）。核心模块有一个上下文对象context，该对象在初始化函数bool_t Context_Init(&#8230;&#8230;)中候创建了一个该对象实例。该对象实例记录管理各个功能模块，用户界面可以通过该对象和核心模块交互，管理控制播放过程。<br>&nbsp;Context对象说明：<br>typedef struct context<br>{<br>&nbsp;int Version;&nbsp;//版本信息<br>&nbsp;uint32_t ProgramId; <br>&nbsp;const tchar_t* ProgramName;&nbsp;&nbsp;//应用程序名称<br>&nbsp;const tchar_t* ProgramVersion;&nbsp;//程序版本号，字符串<br>&nbsp;const tchar_t* CmdLine;&nbsp;&nbsp;&nbsp;//程序命令行信息<br>&nbsp;void* Wnd;&nbsp;&nbsp;&nbsp;//视频渲染窗口句柄<br>&nbsp;void* NodeLock;&nbsp;&nbsp;//功能模块访问临界区互斥变量<br>&nbsp;array Node;&nbsp;&nbsp;&nbsp;//功能模块数据对象数组<br>&nbsp;array NodeClass;&nbsp;&nbsp;//功能模块定义对象数组，按照系统逻辑关系组织<br>&nbsp;array NodeClassPri;&nbsp;//功能模块定义对象数组，按照系统逻辑关系和模块优先级排列<br>&nbsp;array NodeModule;&nbsp;&nbsp;//外部插件模块数组<br>&nbsp;int LoadModuleNo;&nbsp;&nbsp;//当前正在装载的外部插件序号<br>&nbsp;void* LoadModule;<br>&nbsp;array StrTable[2];&nbsp;&nbsp;//字符串资源数组，字符串分为<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//给底层使用的标准字符串资源和<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//给界面使用的显示字符串资源，两种资源用两个数组表示<br>&nbsp;array StrBuffer;<br>&nbsp;array StrModule;&nbsp;&nbsp;//未使用<br>&nbsp;void* StrLock;&nbsp;&nbsp;//字符串数组访问临界区互斥变量<br>&nbsp;uint32_t Lang;&nbsp;&nbsp;&nbsp;//当前使用语言标志<br>&nbsp;int CodePage;&nbsp;&nbsp;&nbsp;//当前使用代码页标志<br>&nbsp;struct pcm_soft* PCM;&nbsp;//PCM音频信号转换模块<br>&nbsp;struct blitpack* Blit;&nbsp;//视频信号渲染模块<br>&nbsp;struct node* Platform;&nbsp;//得到平台相关信息<br>&nbsp;struct node* Advanced;&nbsp;//得到播放模块高级信息<br>&nbsp;struct node* Player;&nbsp;&nbsp;//播放控制模块<br>&nbsp;notify Error;&nbsp;&nbsp;&nbsp;//信息错误回调函数<br>&nbsp;//屏幕旋转信息，在某些系统中屏幕可以旋转90度或180度<br>&nbsp;int (*HwOrientation)(void*);<br>&nbsp;void *HwOrientationContext;<br>&nbsp;bool_t TryDynamic;&nbsp;&nbsp;//未使用<br>&nbsp;int SettingsPage;&nbsp;&nbsp;&nbsp;//未使用<br>&nbsp;size_t StartUpMemory;&nbsp;&nbsp;//可以使用的有效内存数<br>&nbsp;bool_t InHibernate; &nbsp;&nbsp;//是否进入休眠状态<br>&nbsp;bool_t WaitDisable;&nbsp;&nbsp;&nbsp;//未使用<br>&nbsp;int FtrId;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//未使用<br>&nbsp;bool_t LowMemory;&nbsp;&nbsp;//可以使用的有效内存数是否小于系统要求的最低要求<br>&nbsp;//动态代码生成中间状态及数据<br>&nbsp;bool_t CodeFailed;<br>&nbsp;bool_t CodeMoveBack;<br>&nbsp;bool_t CodeDelaySlot;<br>&nbsp;void* CodeLock;<br>&nbsp;void* CodeInstBegin;<br>&nbsp;void* CodeInstEnd;<br>&nbsp;int NextCond;<br>&nbsp;bool_t NextSet;<br>&nbsp;bool_t NextByte;<br>&nbsp;bool_t NextHalf;<br>&nbsp;bool_t NextSign;<br>&nbsp;<br>&nbsp;uint32_t* FlushCache;&nbsp;&nbsp;//未使用<br>&nbsp;void* CharConvertUTF8;&nbsp;//未使用<br>&nbsp;void* CharConvertCustom;&nbsp;//未使用<br>&nbsp;int CustomCodePage;&nbsp;&nbsp;//未使用<br>&nbsp;void* CharConvertAscii;&nbsp;//未使用<br>&nbsp;void* Application;<br>&nbsp;void* Logger;&nbsp;&nbsp;&nbsp;&nbsp;//未使用<br>&nbsp;bool_t KeepDisplay;&nbsp;&nbsp;//是否保持背光长亮<br>&nbsp;int DisableOutOfMemory;&nbsp;//未使用</p>
<p>} context;<br>核心模块上下文指针可以通过全局函数获得context* Context();<br>初始化上下文对象的全局函数是bool_t Context_Init(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine,void* Application);其中Name参数为应用程序名称，Version为版本信息字符串。<br>释放上下文对象的全局函数是void Context_Done();。<br>void Context_Wnd(void*);函数将视频播放窗口句柄初始化给设备上下文。</p>
<p>功能模块包含定义对象和数据对象，定义对象描述功能模块相互间的逻辑结构，数据对象记录模块属性和方法。<br>所有的功能模块结构按一个树状结构来组织，结构关系如下，NODE是整个结构的根结点，其下为子节点，节点按类型可分为实节点，全局节点，设置节点，抽象节点。<br>#define CF_SIZE&nbsp;&nbsp;&nbsp;0x00FFFFFF<br>#define CF_GLOBAL&nbsp;&nbsp;0x01000000<br>#define CF_SETTINGS&nbsp;&nbsp;0x02000000<br>#define CF_ABSTRACT&nbsp;&nbsp;0x08000000<br>抽象节点没有对应的对象实例，类似C++的抽象基类，为了按照逻辑关系组织系统结构而存在，例如NODE就是抽象节点。全局节点全局只有一个对象的实例，如播放控制模块PLAYER_ID。设置节点表示和系统播放设置相关，比如声音均衡器模块EQUALIZER_ID，颜色控制模块COLOR_ID。实节点与抽象节点不同，指可以生成对象实例的节点，实节点没有特殊标识，一般以数据对象占用内存大小表示是否是一个实节点，创建节点时要根据该信息分配内存单元，实节点也可以有子节点，例如：MMS_ID的父节点是HTTP_ID。全局节点，设置节点和实节点可以相互组合，比如播放控制节点同时是全局节点，设置节点和实节点。节点名称后带_ID的就是实节点，否则就是抽象节点。</p>
<p>&nbsp;&nbsp;&nbsp; NODE （根节点）<br>&nbsp;&nbsp;&nbsp; ├─FLOW （流控制模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─CODEC （解码模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─EQUALIZER_ID （声音均衡器模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─VBUFFER_ID （视频缓冲模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─DMO （DirectX Media Object）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; ├─WMV_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; ├─WMS_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; ├─WMVA_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; ├─WMA_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; └─WMAV_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─FFMPEG VIDEO （FFMpeg 解码模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; └─LIBMAD_ID （Libmad Mp3解码模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─OUT （信号渲染模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─AOUT （音频信号渲染）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; ├─NULLAUDIO_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; │&nbsp; └─WAVEOUT_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; └─VOUT （视频信号渲染）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─NULLVIDEO_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; └─OVERLAY<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─IDCT （离散余弦解码模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; └─SOFTIDCT_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; └─CODECIDCT（离散余弦解码模块，函数比IDCT要少）<br>&nbsp;&nbsp;&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; └─MPEG1_ID<br>&nbsp;&nbsp;&nbsp; ├─MEDIA （媒体文件格式编码解析模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─FORMAT （格式解析模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; └─FORMATBASE<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─RAWAUDIO<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │&nbsp; └─MP3_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─RAWIMAGE<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─ASF_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─AVI_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─MP4_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─MPG_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ├─NSV_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; └─WAV_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─PLAYLIST （播放列表模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─ASX_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; ├─M3U_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; │&nbsp; └─PLS_ID<br>&nbsp;&nbsp;&nbsp; │&nbsp; └─STREAMPROCESS （数据流处理模块）<br>&nbsp;&nbsp;&nbsp; ├─STREAM （数据输入模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─MEMSTREAM_ID （内存数据流模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; ├─FILE_ID （文件IO模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; └─HTTP_ID （网络数据获取模块）<br>&nbsp;&nbsp;&nbsp; ├─TIMER （定时器模块）<br>&nbsp;&nbsp;&nbsp; │&nbsp; └─SYSTIMER_ID<br>&nbsp;&nbsp;&nbsp; ├─ASSOCIATION_ID （文件扩展名自动关联模块）<br>&nbsp;&nbsp;&nbsp; ├─ADVANCED_ID （高级设置模块）<br>&nbsp;&nbsp;&nbsp; ├─COLOR_ID （颜色控制模块）<br>&nbsp;&nbsp;&nbsp; ├─PLATFORM_ID （平台信息模块）<br>&nbsp;&nbsp;&nbsp; ├─XSCALEDRIVER_ID （Intel XScale CPU 信息模块）<br>&nbsp;&nbsp;&nbsp; ├─PLAYER_ID （播放控制模块）<br>&nbsp;&nbsp;&nbsp; └─PLAYER_BUFFER_ID （播放缓冲模块）<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 节点树状结构由若干个静态定义对象(nodedef)实例实现，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef struct nodedef<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;Flags;<br>&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;Class;<br>&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;ParentClass;<br>&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;Priority;<br>&nbsp;&nbsp;&nbsp;nodecreate&nbsp;&nbsp;Create;<br>&nbsp;&nbsp;&nbsp;nodedelete&nbsp;&nbsp;Delete;<br>&nbsp;&nbsp;} nodedef;<br>&nbsp;Flags表示当前节点的类型：抽象、实节点、全局、设置。<br>&nbsp;Class表示当前节点的标识，如MEDIA_CLASS或ASF_ID等等。<br>&nbsp;ParentClass表示当前节点的父节点标识，如SYSTIMER_ID对象的父节点是TIMER_CLASS。<br>&nbsp;Priority表示当前节点优先级。<br>&nbsp;Create和Delete是两个函数指针，表示该节点的创建函数和销毁函数。<br>&nbsp;如播放控制模块的结构定义是<br>&nbsp;static const nodedef Player =<br>&nbsp;{<br>&nbsp;&nbsp;sizeof(player_base)|CF_GLOBAL|CF_SETTINGS,<br>&nbsp;&nbsp;PLAYER_ID,<br>&nbsp;&nbsp;NODE_CLASS,<br>&nbsp;&nbsp;PRI_MAXIMUM+600,<br>&nbsp;&nbsp;(nodecreate)Create,<br>&nbsp;&nbsp;(nodedelete)Delete,<br>&nbsp;};<br>&nbsp;<br>&nbsp;绝大多数节点都有一个对应的数据对象，记录该节点的数据和方法，每一个子节点对象都是以父节点对象作为该节点一个元素，类似C++的封装继承机制。如果子节点的父节点没有数据对象，该节点可以从node节点直接继承。每一个节点都可以看成Node节点的直接或间接子节点，所以所有节点头以一个相同的node结构开头，子节点可能还有自己的属性，在继承父对象后就是子节点自己的元素。<br>&nbsp;&nbsp;&nbsp; typedef struct node<br>&nbsp;{<br>&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;Class;<br>&nbsp;&nbsp;nodeenum&nbsp;Enum;<br>&nbsp;&nbsp;nodeget&nbsp;&nbsp;Get;<br>&nbsp;&nbsp;nodeset&nbsp;&nbsp;Set;<br>&nbsp;} node;<br>&nbsp;Class表示该对象的标识，如PLAYER_ID。<br>&nbsp;Enum是一个函数指针，指向一个函数用于枚举当前节点的属性。<br>&nbsp;Get是一个函数指针，得到当前节点某一属性值。<br>&nbsp;Set是一个函数指针，设置当前节点的某一属性数值。<br>&nbsp;<br>&nbsp;节点的属性值数据特性在一个static const datatable xxxParams[] = {&#8230;&#8230;};的静态数组里定义。<br>&nbsp;typedef struct datatable<br>&nbsp;{<br>&nbsp;&nbsp;int&nbsp;No;<br>&nbsp;&nbsp;int&nbsp;Type;<br>&nbsp;&nbsp;int Flags;<br>&nbsp;&nbsp;int Format1;<br>&nbsp;&nbsp;int&nbsp;Format2;<br>&nbsp;} datatable; <br>&nbsp;<br>&nbsp;No表示属性的标识，如播放控制模块的#define PLAYER_PLAY 0x32 就表示控制播放器播放或暂停。<br>&nbsp;Type表示属性的数据类型，可用值在node.h中定义。<br>&nbsp;Flags是属性数据的标志，表示该数据是不是只读数据，是否有最大最小值等等，可用值在node.h中定义，如果该标志包含DF_SETUP同时不包含DF_NOSAVE和DF_RDONLY属性，该属性会被记录在注册表中，下次启动时用注册表的数据初始化该属性。<br>&nbsp;Format1和Format2是可选标志与Flags配合使用，比如如果Flags表示该属性存在最大最小值，Format1和Format2可以分别表示最小和最大数值。<br>&nbsp;<br>&nbsp;在在系统上下文对象中有两个元素记录节点信息array Node;和array NodeClass;，array是数组数据类型，Node是节点数据对象的数组，NodeClass节点对象的数组，按照系统逻辑关系组织。<br>&nbsp;创建节点时传入nodedef对象到节点创建函数，函数会根据nodedef信息生成对应nodeclass对象添加到NodeClass数组，同时根据nodedef信息分配数据对象的内存空间。在该节点的Create函数里面再初始化该节点的数据对象。</p>
<p>在所有功能模块中和界面加交互的主要就是播放控制模块struct node* Player;使用方法如下：<br>context* p = Context();<br>player* myplayer = NULL;<br>if(p) myplayer = (player*)(p-&gt;Player);<br>控制播放参数使用Set(void* This,int No,const void* Data,int Size);函数，第一个参数是播放模块指针，第二个参数是控制代码，即要进行什么操作，第三个参数是需要赋值给控制代码的数值，最后一个参数是所赋数值的占用内存的大小。<br>例如开始播放的代码是：<br>myplayer-&gt;Set(myplayer,PLAYER_PLAY,1,sizeof(int));<br>PLAYER_PLAY为控制代码，表示当前控制的是播放暂停功能，数值为1表示播放为0表示暂停。<br>得到某一控制属性使用Get(void* This,int No,void* Data,int Size);函数，参数含义和Set函数相同。<br>控制代码是一组宏，定义在player.h文件中。比较重要的控制参数有<br>// play or pause (bool_t) <br>#define PLAYER_PLAY&nbsp;&nbsp;&nbsp;0x32<br>// position in fraction (fraction)<br>#define PLAYER_PERCENT&nbsp;&nbsp;0x25<br>// position in time (tick_t) <br>#define PLAYER_POSITION&nbsp;&nbsp;0x28<br>// current format (format*)<br>#define PLAYER_FORMAT&nbsp;&nbsp;0x2B<br>// current file in playlist (int) <br>#define PLAYER_LIST_CURRENT&nbsp;0x2F<br>// current file index (suffled) in playlist (int) <br>#define PLAYER_LIST_CURRIDX&nbsp;0xA2<br>// fullscreen mode (bool_t)<br>#define PLAYER_FULLSCREEN&nbsp;0x3E<br>// stop<br>#define PLAYER_STOP&nbsp;&nbsp;&nbsp;0xB2<br>// skin viewport rectangle (rect)<br>#define PLAYER_SKIN_VIEWPORT 0x3C<br>播放控制模块所有可用参数见static const datatable PlayerParams[]结构。</p>
<p>添加一个媒体文件到播放模块使用int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title);<br>第一个参数为播放模块指针，第二个参数是添加到播放模块文件队列的序号，如果是使文件成为第一个文件该参数设为0，第三个参数是媒体文件的目录和名称，第四个参数为媒体文件标题，该参数可以忽略。</p>
<p>核心模块也管理多语言字符串，使用函数const tchar_t* LangStr(int Class, int Id);和const tchar_t* LangStrDef(int Class, int Id)可以得到对应字符串，系统字符串资源有两种，标准字符串和特殊字符集字符串。标准字符串资源文件是工程目录下的lang_std.txt文件，该文件字符串为ASCII字符，可与其他代码页字符兼容。该文件记录的是核心模块运行时需要使用的字符串，Decode和Splite模块可以处理的编码格式和文件格式也在这个文件中记录，例如lang_std.txt文件中的<br>MP3_0001=audio/mpeg<br>MP3_0002=mp1:A;mp2:A;mp3:A;mpa:A<br>MP3_0200=acodec/0x0055<br>纪录了MP3文件分离器对应的文件类型、扩展名和文件特征码。<br>要得到标准字符串使用函数LangStrDef，第一个参数表示字符类别，第二个参数表示字符ID。界面相关的是特殊字符集的字符串，使用函数LangStr，第一个参数表示字符类别，第二个参数表示字符ID。关于字符串资源文件结构含义将在以后的文档中说明。<a href="http://blog.csdn.net/navi_dx/archive/2007/11/14/1885780.aspx">http://blog.csdn.net/navi_dx/archive/2007/11/14/1885780.aspx</a></p>
<img src ="http://www.cppblog.com/kuronca/aggbug/37188.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-11-23 10:28 <a href="http://www.cppblog.com/kuronca/archive/2007/11/23/37188.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码书写细节</title><link>http://www.cppblog.com/kuronca/archive/2007/08/28/31048.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Tue, 28 Aug 2007 12:36:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/08/28/31048.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/31048.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/08/28/31048.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/31048.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/31048.html</trackback:ping><description><![CDATA[<p>好久，没来这里写些什么了。这两个月一直忙着工作，今天忽然闲下来，想想把这两个月碰到的一些细节问题与大家共享下。好，老样子先看段代码: <br>在贴代码前，我先做个声明：代码在windows平台下运行。做UI的朋友经常用到DrawText()这个API，它里面有个参数是关于文字对齐方式的. 我们写控件输出信息时，都考虑增加这么类型的变量来控制文字信息输出，最典型的就是按钮。一般情况下，我们无需检测该变量，直接引用就好。但有时候为了让界面更人性化，我们需要检测该变量，并做些调整。好，现在看这段代码：<br>&nbsp;它根据对齐属性是否包含 DT_LEFT，而做一些工作<br>void OnPaint()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。。。。。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。。。。。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp; m_uAlign&nbsp; &amp; &nbsp;DT_LEFT )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//代码 段 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoA();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoB();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( m_uAlign&nbsp; &amp;&nbsp; DT_LEFT&nbsp; ==&nbsp; DT_LEFT )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//代码 段&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoA();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoB();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( (m_uAlign&nbsp; &amp;&nbsp; DT_LEFT ) ==&nbsp; DT_LEFT )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//代码 段&nbsp;3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoA();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DoB();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。。。。。<br>}<br><br>上面三个代码段的执行过程为：1：永远执行 DoB() 函数体；<br>&nbsp;&nbsp;2：如果 m_uAlign&nbsp;最末位是零 ，则执行 DoB()函数体;&nbsp;&nbsp;&nbsp;否则则执行 DoA()函数体；</p>
3：只要 m_uAlign 包含 标志位 DT_LEFT ，则执行 DoA()函数体;&nbsp;&nbsp;&nbsp;其他则执行 DoB()函数体；<br><br>&nbsp;貌似，1和2 都不是我们想要的。只有3是正确的。造成这种现象的原因是什么？<br>1：&nbsp; 这个不容易查出来，我们一贯的写法都这样怎么会不对呢？ 其实，我们看看他的定义就知道了 #define DT_LEFT&nbsp; 0x00000000&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;呵呵，缘由出来了；<br>2&nbsp;：有了1 ，2 就更简单了，就是运算符优先级的问题；<br>3；正确；它演示了良好的代码风格；<br><br>总结，其实这不算大问题，都是细节上的问题。可是细节上的问题，往往是最不容易察觉的，就算你发现问题，可是想对代码段的定位更是困难，他们是隐形的虫子。为此，我们有必要养成良好的代码书写习惯和风格。对今天遇到的问题，我总结两条：<br>a)&nbsp; 在进行位标志检测时，一定要将检测结果与原值比较； <br>b) 在你不确定运算符的优先级时，请一定要用括号括起来；<br>今天，就写道这里。希望这两条对大家有帮助。以后碰到类似问题，能想到就好！ 
<img src ="http://www.cppblog.com/kuronca/aggbug/31048.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-08-28 20:36 <a href="http://www.cppblog.com/kuronca/archive/2007/08/28/31048.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WTL ---Good Boy!</title><link>http://www.cppblog.com/kuronca/archive/2007/05/30/25132.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Wed, 30 May 2007 08:44:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/05/30/25132.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/25132.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/05/30/25132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/25132.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/25132.html</trackback:ping><description><![CDATA[<p style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;唉，说来惭愧。记得在前面说过，打算在五一期间研究下PPC上怎么给程序换肤，结果计划不如变化：拜见未来的岳父岳母花了3天，同学结婚花了一天。回来后，也没啥心思搞了，不过大概还是知道些东西。要通过hook技术达到换肤的方法，还是比较困难地啊。好了，过去的不说了。不过，以后我有机会还是会继续搞搞（现阶段懂的还是少啊）。<br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在切入正题：WTL 。什么是WTL？WTL 的全称为 Windows Template Library， 是微软ATL开发组成员Nenad Stefanovic先生在<span style="COLOR: red">ATL Windowing</span>机制上发展起来的一整套GUI框架。它<span style="COLOR: red">运用模板（template）技术</span>组织和创建GUI对象，构成了精制的面向对象框架，使面向对象与模板达成了精制的融合。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面就是WTL的定义，准确地描述了什么是WTL。请注意我用红色标记的两个地方，那可以说是WTL的精髓。首先，WTL其实基于ATL的，它是ATL面向GUI编程的扩展；其次，WTL运用模板技术，这样构建的程序精致短小。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 需要指出的是WTL目前不被微软官方支持也没有开发文档，不过能够在其官方网站下到sdk。WTL貌似在民间非常受欢迎，用过的人都说好, 甚至有人说会超过MFC。现在最新版的WTL是8.0 beta2版，07年5月28日才发布的。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对比WTL和MFC，他们大部分是相通的。对于一个有过MFC编程经验的人来说，熟悉WTL还是比较容易的。下面是他们的比较<br>
<table borderColor=#000080 cellSpacing=1 cellPadding=7 width=612 border=1>
    <tbody>
        <tr>
            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top bgColor=#000080><font color=#ffffff>Feature </font>
            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%" bgColor=#000080><font color=#ffffff>MFC </font>
            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%" bgColor=#000080><font color=#ffffff>WTL </font>
            <tr>
                <td style="FONT-SIZE: 9pt; WIDTH: 254px; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体; HEIGHT: 33px" vAlign=top>Stand-alone library
                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No (built on ATL)
                <tr>
                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>AppWizard support
                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                    <tr>
                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>ClassWizard support
                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No
                        <tr>
                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Officially supported by Microsoft
                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No (Supported by volunteers inside MS)
                            <tr>
                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Support for OLE Documents
                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No
                                <tr>
                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Support for Views
                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                    <tr>
                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Support for Documents
                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No
                                        <tr>
                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Basic Win32 &amp; Common Control Wrappers
                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                            <tr>
                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Advanced Common Control Wrappers (Flat scrollbar, IP Address, Pager Control, etc.)
                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No
                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                <tr>
                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Command Bar support (including bitmapped context menus)
                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No (MFC does provide dialog bars)
                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                    <tr>
                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>CString
                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                        <tr>
                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>GDI wrappers
                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                            <tr>
                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Helper classes (CRect, Cpoint, etc.)
                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                <tr>
                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Property Sheets/Wizards
                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                    <tr>
                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>SDI, MDI support
                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                        <tr>
                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Multi-SDI support
                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No
                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                            <tr>
                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>MRU Support
                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                <tr>
                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Docking Windows/Bars
                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No
                                                                                    <tr>
                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Splitters
                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                        <tr>
                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>DDX
                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes (not as extensive as MFC)
                                                                                            <tr>
                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Printing/Print Preview
                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                <tr>
                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Scrollable Views
                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                    <tr>
                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Custom Draw/Owner Draw Wrapper
                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No
                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                        <tr>
                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Message/Command Routing
                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                            <tr>
                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Common Dialogs
                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                                <tr>
                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>HTML Views
                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                                    <tr>
                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Single Instance Applications
                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No
                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">No
                                                                                                                        <tr>
                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>UI Updating
                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">Yes
                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                                            <tr>
                                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Template-based
                                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">No
                                                                                                                                <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">Yes
                                                                                                                                <tr>
                                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Size of a statically linked do-nothing SDI application with toolbar, status bar, and menu
                                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">228KB +<br>MSVCRT.DLL (288KB)
                                                                                                                                    <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">24k (with /OPT:NOWIN98)<br>(+ MSVCRT.DLL if you use CString)
                                                                                                                                    <tr>
                                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Size of a dynamically linked do-nothing SDI application with toolbar, status bar, and menu
                                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">24KB +<br>MFC42.DLL (972KB) +<br>MSVCRT.DLL (288KB)
                                                                                                                                        <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">N/A
                                                                                                                                        <tr>
                                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top>Runtime Dependencies
                                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="28%">CRT (+ MFC42.DLL, if dynamically linked)
                                                                                                                                            <td style="FONT-SIZE: 9pt; LINE-HEIGHT: 13pt; FONT-FAMILY: 宋体" vAlign=top width="30%">None (CRT if you use CString)</td>
                                                                                                                                        </tr>
                                                                                                                                    </tbody>
                                                                                                                                </table>
                                                                                                                                <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;听说从WTL7.5开始支持PPC上编程，这可让我大为开心。有机会试试用WTL在PPC上写个程序。</p>
<img src ="http://www.cppblog.com/kuronca/aggbug/25132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-05-30 16:44 <a href="http://www.cppblog.com/kuronca/archive/2007/05/30/25132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用户层下拦截系统api的原理与实现 (转载) </title><link>http://www.cppblog.com/kuronca/archive/2007/04/28/23126.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Sat, 28 Apr 2007 09:23:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/04/28/23126.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/23126.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/04/28/23126.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/23126.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/23126.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一篇关于API拦截的文章，其中也用到远程线程注入， 觉着不错就转载过来&nbsp;&nbsp;<a href='http://www.cppblog.com/kuronca/archive/2007/04/28/23126.html'>阅读全文</a><img src ="http://www.cppblog.com/kuronca/aggbug/23126.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-04-28 17:23 <a href="http://www.cppblog.com/kuronca/archive/2007/04/28/23126.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>又是一年“五一黄金周”</title><link>http://www.cppblog.com/kuronca/archive/2007/04/26/22868.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Thu, 26 Apr 2007 03:29:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/04/26/22868.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/22868.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/04/26/22868.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/22868.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/22868.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 又是一年&#8220;五一黄金周&#8221;，&#8220;五一&#8221;一到感觉一年去了一半，时间飞逝啊！当初也不知道谁提出这么个概念，搞得全国上下、祖国各地、大江南北都人声鼎沸，车水马龙（不知道成语用的对不？<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emembarrassed.gif" align=absMiddle border=0>）好一派繁荣景象，这让我想起一个比喻：就说小时候的马蜂窝吧，平时看不出来马蜂多，可是经小孩们一捣鼓那出来的可是黑压压的一片。把全国同胞比作马蜂是有点不合适啊，是谁又把全国同胞给捣鼓的出家门？<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 唉，不管那么多了。以前出去玩，回来感觉就是花钱买罪受，还要冒着被人骗的风险（说风险有点过了<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emwink.gif" align=absMiddle border=0>），所以今年不凑那份热闹，老实在家呆着。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计划都定下来：继续研究些专业方面东东。前段时间研究过程序换皮肤，准备想搞到PPC上看看，让PPC界面更美观。看到不少的换皮肤大致原理是：先设钩子钩住界面主线程，然后在控件创建的时候捕捉控件，然后再子类化。这个在PC上没问题，可是到PPC上碰到关键的一步不能实现。那就是PPC上不支持钩子函数，这就成为一个大的障碍：没法钩住主线程，就无法子类化。原本想过在App的PreTranslateMessage中过滤子控件的wm_create消息，从而实现子类化可是失败了。我觉得解决问题关键核心是：除了钩子外，还有什么方法能在程序收到消息前劫获消息。这将是我在&#8220;五一黄金周&#8221;主要研究的东东，不过不知道自己能否成功。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果换皮肤研究进行顺利的话，准备把前段时间写的&nbsp;用oledb 访问sqlmobile数据的类完善下。现在这些类仅仅提供了基本操作，打算丰富让这些类的操作。不过由于种种原因，我将不提供源码，到时候可能以 dll形式或者com形式发布。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后说两句，现阶段制定计划是这样，到时候不知道会执行的怎样？有太多不可预知因素，反正尽力吧！<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<img src ="http://www.cppblog.com/kuronca/aggbug/22868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-04-26 11:29 <a href="http://www.cppblog.com/kuronca/archive/2007/04/26/22868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Do What You think !</title><link>http://www.cppblog.com/kuronca/archive/2007/04/02/21097.html</link><dc:creator>零宇</dc:creator><author>零宇</author><pubDate>Mon, 02 Apr 2007 08:23:00 GMT</pubDate><guid>http://www.cppblog.com/kuronca/archive/2007/04/02/21097.html</guid><wfw:comment>http://www.cppblog.com/kuronca/comments/21097.html</wfw:comment><comments>http://www.cppblog.com/kuronca/archive/2007/04/02/21097.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kuronca/comments/commentRss/21097.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kuronca/services/trackbacks/21097.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先恭喜自己的博客终于开张，其实有这个想法已经很久拉！可是由于种种原因（主要是自己懒<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/face1.gif" align=absMiddle border=0>，还是毛主席的哲学思想分析的透彻：事物的内因主导事物的发展方向！&nbsp;），一直导致想法始终没能走出我的脑瓜瓜！！现在不同拉， 因为脑瓜瓜里面多拉一条新的想法：Do What You think !！<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;写第一篇文章，实在没想到好的文章标题！在眼球环转屏幕时，看到博客标题，wha？？由于刚开博客太兴奋？？这么好的一句话，怎么就没想起来！！朋友，欢迎来到我的博客，每次的到来让我们一起 Do What You think !！<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其次，很荣幸加入这个大家庭。唉，自己文笔实在不好，始终想不出来写些啥！记得以前高中每次模拟考，语文我都进黑名单（150分卷子，70分都拿不到），这段实在写不出来，就这么凑或吧！<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后，说说标题的意思，其实也没什么深刻的含义。只是这句话，觉着吧对我作用满大的。有想法尽量去做！简单不? 标题意思也解释清楚了，我也该去轻松下！<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(you.HaveThink())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;you. DoThink();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}while( you.StillCanThink());
<img src ="http://www.cppblog.com/kuronca/aggbug/21097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kuronca/" target="_blank">零宇</a> 2007-04-02 16:23 <a href="http://www.cppblog.com/kuronca/archive/2007/04/02/21097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>