??xml version="1.0" encoding="utf-8" standalone="yes"?> TCPMP是一个功能强大开攑ּ的开源多媒体播放器, libmad工程用于MP3文g解码Q该工程包含两个功能模块Q一个负责解析MP3文g格式Q包括MPEG1音频文g (MP1,MP2,MP3,MPA)Q读取每一帧音频数据;另一个负责解码MPEG1音频数据Q解码代码在libmad子目录中?br>libmad是一个开源的高精?MPEG1音频解码库,支持 MPEG-1QLayer I, Layer II ?LayerIIIQ也是 MP3Q。libmad提供 24-bit ?PCM 输出Q完全是定点计算Q非帔R合没有点支持的^C使用。?libmad 提供的一pd APIQ就可以非常单地实现 MP3 数据解码工作。在 libmad 的源代码文g目录下的 mad.h 文g中,可以看到l大部分该库的数据结构和 API {。libmad是用的fixed-integerQ通过整数模拟数计算的,_ֺ只能保证到小数点后第9位(大于0的最?0.00000000372529Q,虽然解码_ֺ?x)有损失Q但是极大提高(sh)(jin)解码效率Q特别是在嵌入式讑֤上也可以实现高码率MP3文g的解码?/p>
splitter工程用于解析多种韌频文件格式。可以解析的文g格式包括QASF媒体文gQ视频文?(AVI,DIVX)QW(xu)indows波Ş文g (WAV,RMP)QMPEG?sh)?jing)文g (MPEG,MPG,MPV)QMPEG4文g (MP4,3GP,M4A,M4B,K3G)。以上格式可以被解析但是数据~码不一定能正确解码Q需要依赖系l的解码器?/p>
common工程是核?j)模块,是一个开攄集数据输入、{换、音/视频解码、信可出等功能Z体的完整的多媒体播放框架。这个框架自w不包含M的Decode和Split功能Q这些功能由插g实现Q核?j)模块以一个树(wi)状结构管理所有的功能模块和插件模块,实现数据Render功能Q对输入、{换、输出流E的控制Q接受播放过E中的操作和对事件进行处理,同时也实现系l运行中l常使用的一些共用函敎ͼ比如解码q程中经怋用的逆离散余弦变换,内存操作Q界面中需要用的多语a字符处理{。common工程的主目录下主要有Qblit、dyncode、overlay、pcm、softidct、win32、zlib{子目录。其中blit和overlay存放是视频信h染模块,pcm存放PCM音频信号转换模块Qsoftidct存放逆离散余弦变换函敎ͼwin32存放内存操作{常用模块,dyncodeq个目录的代码比较晦涩,存放的是E序q行是动态生成代码模块,针对不同的CPU指o(h)集,PCM数据数据声道和采L(fng)不同Q视频渲染数据格式和色深{不同情况动态生成不同的优化代码Q这D代码非常精彩,不能不让Z服TCPMP作者的高超水^Q。核?j)模块有一个上下文对象contextQ该对象在初始化函数bool_t Context_Init(……)中候创Z(jin)一个该对象实例。该对象实例记录理各个功能模块Q用L(fng)面可以通过该对象和核心(j)模块交互Q管理控制播放过E?br> Context对象说明Q?br>typedef struct context } context; 功能模块包含定义对象和数据对象,定义对象描述功能模块怺间的逻辑l构Q数据对象记录模块属性和Ҏ(gu)?br>所有的功能模块l构按一个树(wi)状结构来l织Q结构关pd下,NODE是整个结构的根结点,其下为子节点Q节Ҏ(gu)cd可分为实节点Q全局节点Q设|节点,抽象节点?br>#define CF_SIZE 0x00FFFFFF NODE Q根节点Q?br> ├─FLOW Q流控制模块Q?br> ?nbsp; ├─CODEC Q解码模块)(j) 在所有功能模块中和界面加交互的主要就是播放控制模块struct node* Player;使用Ҏ(gu)如下Q?br>context* p = Context(); d一个媒体文件到播放模块使用int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title); 核心(j)模块也管理多语言字符Ԍ使用函数const tchar_t* LangStr(int Class, int Id);和const tchar_t* LangStrDef(int Class, int Id)可以得到对应字符Ԍpȝ字符串资源有两种Q标准字W串和特D字W集字符丌Ӏ标准字W串资源文g是工E目录下的lang_std.txt文gQ该文g字符串ؓ(f)ASCII字符Q可与其他代码页字符兼容。该文g记录的是核心(j)模块q行旉要用的字符ԌDecode和Splite模块可以处理的编码格式和文g格式也在q个文g中记录,例如lang_std.txt文g中的
两种办法Q?nbsp;
1、在[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]下添加注册键?nbsp;
2、在应用E序中调用ActivateDeviceEx?nbsp;
在一些文件中用分h表示注释Q例如下面的内容
; @CESYSGEN IF SERVERS_MODULES_HTTPD
; @CESYSGEN ENDIF
?#8220;CESYSGEN...”前加?#8220;@”Q有没有什么特别的含义Q?nbsp;
在WINCE的一些文件中Q用“;”作ؓ(f)注释q在注释文字中用@CESYSGEN作ؓ(f)标记Q后面接条g语句。Cefilter.exe工具负责按照条g来筛选文件内容,所以不要轻易地删除包含@CESYSGEN的注释语句?nbsp;
通过串口建立ActiveSync联接,串口U用三线的可以吗?
不可以,因ؓ(f)用串口同步时要用到其余口的状态?nbsp;
WINCE是否支持MAPIQ?/strong>
不支持。WINCE自带的pmail.exe软g也不是很好用。徏议自开发邮件收发Y件。如果需要购买WINCE下邮件收发Y件可以联pL?nbsp;
如何旋{屏幕昄的内容?
例子代码如下Q前提是昄驱动E序支持旋{Q:(x)
DEVMODE devmode = {0};
devmode.dmSize = sizeof(DEVMODE);
devmode.dmDisplayOrientation = DMDO_90; ///垂直模式
devmode.dmFields = DM_DISPLAYORIENTATION;
ChangeDisplaySettingsEx(NULL, &devmode, NULL, 0, NULL); ///改变昄的设|?nbsp;
CRect rcWorkArea(0, 0, 320, 240); ///整个屏幕寸
///讄客户区大ƈq播消息Q这h有Y件也随之更Ҏ(gu)C?nbsp;
SystemParametersInfo(SPI_SETWORKAREA, 0, (void*)&rcWorkArea, SPIF_SENDCHANGE);
请问如何修改字Ş~存的容量?
[HKEY_LOCAL_MACHINE\System\GDI\GLYPHCACHE]
"limit"=dword:0400
如何得到从WINCE启动开始到现在的时_(d)
调用API GetTickCountQ得到的gؓ(f)32位整敎ͼ单位为毫U?nbsp;
如何调用WINCE的Y键盘Q?/strong>
调用API SipShowIM(SIPF_ON)Q前提是内核加入?jin)Y键盘lg?nbsp;
ZHIVE的注册表Q如何在pȝ关闭前保存注册表的数据到文gsystem.hvQ?/strong>
调用API RegFlushKey函数?nbsp;
使用VirtualAlloc和VirtualCopy的时候需要注意哪些事?
1、VirtualAlloc的作用是甌虚拟地址I间Q这肯定不是最l的目的Q最l目的可能是甌物理内存、映寄存器、提交文件等。没有一个目的会(x)在意虚拟地址I间的位|,所以尽量传递参??Q也是让WINCE自动分配虚拟地址I间。VirtualAlloc分配地址I间实际上是?4KB为单位,所以要指定甌的虚拟空间的首地址的话Q参?应该?4KB的整数倍,甌的长度也应该?4KB的整数倍,即你不需要那么大?nbsp;
2、VirtualCopy的主要作用是映射物理地址I间Q如果参?为物理地址Q那么最后一个参数要dPAGE_PHYSICALQ参?必须?56的整数倍。如果参?拟地址Q?x80000000以上Q,那么最后一个参数就不要dPAGE_PHYSICALQW(xu)INCE内核?x)根据这个虚拟地址扑ֈ对应的物理地址?nbsp;
驱动E序和应用程序之间传递数据时何时调用MapPtrToProcessQ?nbsp;
因ؓ(f)讑֤理器负责加载驱动程序DLLQ这意味着当应用程序调用驱动程序接口函数的时候,W(xu)INCE内核?x)将调用驱动E序接口函数的线E{Ud讑֤理器的q程I间然后执行具体的驱动程序代码,应用E序和设备管理器处于两个q程I间Q这造成讑֤理器无法访问应用程序传递的指针Q虚拟地址Q,所以当我们在应用程序中传递指针给驱动程序接口函数时QW(xu)INCE内核从中作了(jin)一个地址映射Q例如ReadFile、WriteFile、DeviceIoControl函数的参数凡是指针都l过?jin)映才传递给驱动E序Q所以很多驱动程序开发者ƈ不了(jin)解其中的奥秘可以编E了(jin)。但是如果参数是一个指向一个结构体的指针,而结构体里包括一个或多个指针Q那么WINCE内核q不负责映射Q所以就需要开发者在驱动E序接口函数中调用API函数MapPtrToProcess来映地址。例如:(x)pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());
如何判断可插拔的讑֤是否存在Q?/strong>
1、通过查找注册表的倹{凡是由API ActivateDeviceEx加蝲的驱动程序都在[HKEY_LOCAL_MACHINE\Drivers\Active]键下有注册键Q通过查找“name”或者其它键值就能够扑ֈ。设备管理器p用这个API。如果是PCI讑֤Q在注册表[HLM\Drivers\BuiltIn\PCI\Instance]下查扑օ键字Q例如[HLM\Drivers\BuiltIn\PCI\Instance\WaveDev1]Q说明音频驱动已l加载?nbsp;
2、调用驱动程序接口函敎ͼҎ(gu)q回值或者执行结果来判断?nbsp;
如何做到通过串口q来的一个信号启动自己开发的应用E序Q?nbsp;
创徏一个线E负责等待串口过来的信号Q调用API SetCommMask讄要等待的信号U类Q具体可以等待的信号U类参见参数2的说明。然后再调用API WaitCommEvent函数{待q个信号Q接收之后再调用API CreateProcess启动应用E序?nbsp;
在WINCE中如何只能启动应用程序的一个实例?
常用的两U办法:(x)
1、如果应用程序实例创Z(jin)H口Q可通过API FindWindow函数通过H口cd和窗口标题名U来查找Q前提是pȝ内不?x)出现窗口名U重复的情况?nbsp;
2、应用程序初始化的时候创Z个事件或互斥{内核对象,因ؓ(f)内核对象是由内核创徏Q名U在pȝ内唯一?nbsp;
能不能自q辑一个数字签名文件导入到手机上,q样可以用q个{{自qE序?jin)?/strong>
WINCE的内核签名机制的用途是限制非法的可执行模块EXE、DLL{在讑֤上运行。要求内核的加蝲模块用公钥验证请求加载的EXE、DLL的签名是否合法,而这个公钥是在定制内核的时候加q去的,所以除内核的定制者以外的人无法修改这个验证机制?nbsp;
我按照版ȝ文章《加密WINCEpȝ》里操作Q提C错误如下:(x)
Error 80090016 during CryptSignHash 1!
Error signing hash
q是因ؓ(f)传递了(jin)无效的钥容器名称QCryptoAPI调用p|。应该在使用signfile工具之前创徏一个钥容器Q在桌面Windows中调用API CryptAcquireContext创徏一个指定名U的钥容器,接着再创Z个签名密钥对Q这时再使用signfile工具可以了(jin)。我在文章里写成-kfulinlin是因为我创徏钥容器的时候没有指定名Uͼpȝ采用当前登录的用户名ؓ(f)容器名?nbsp;
~译错误QCVTRES : fatal error CVT1102: out of memory; 42 bytes required Q?/strong>
多数情况下出现这U错误是因EVC的bug而vQ应该在安装EVC之后qd装EVC的SP补丁。另外ؓ(f)?jin)避开BUGQ用EVC~程应该L一些习(fn)惯,比如定期备䆾工程所有文Ӟ每次~译旉用Clean + Rebuild AllQ正调试时不要关闭模拟器{等?nbsp;
在WINCE下是否能够得到某一q程使用的物理内存总量Q?/strong>
目前没发现有q样一个API能够得到指定q程使用的物理内存总量。只有GlobalMemoryStatus能够得到整个pȝ使用的物理内存总量?nbsp;
应用E序如何控制lcd的亮度?如何获得甉|的电(sh)量?
从常见的q_如Geode、三星ARMpd来看Q的在驱动斚w没有l一的控制LCD或者其它种cdq亮度的接口函数Q所以只能根据具体^台提供的接口来做。从帮助文档来看微Y的带有DirectDraw功能的显C驱动程序的有标准的增加亮度的接口函数Q关于背景光参见标题?sh)?#8220;Enabling a Backlight”的帮助文档?nbsp;
获得甉|?sh)量有标准的接口函数GetSystemPowerStatusExQ前提是驱动E序和硬仉要支持?nbsp;
WINCE的socket函数好像不支持发?接收时Q?/strong>
是的Q最早版本的WINCE支持选项SO_RCVTIMEO、SO_SNDTIMEOQ后来却不支持了(jin)?nbsp;
WINCE下如何设|窗口最大化和最化Q?/strong>
WINCE的帮助文档在介绍API ShowWindow函数的参数时指出SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE都不被支持,但实际上q不完全是这P具体来说Q?nbsp;
SW_MAXIMIZE 比原来窗口大Q但不是最大化
SW_MINIMIZE ~译成功Q但是不起作?nbsp;
SW_SHOWMAXIMIZED 最大化
SW_SHOWMINIMIZED ~译出错
SW_RESTORE 能恢?nbsp;
SW_SHOWDEFAULT ~译出错
SW_SHOWMINNOACTIVE ~译出错
SW_HIDE 能够隐藏
如何用程序调用控刉板的触摸屏校对程序?
两种办法Q?nbsp;
1、调用API TouchCalibrate函数
2、调用CreateProcessQ参?为L"\\windows\\ctlpnl.exe"Q参?为L"cplmain.cpl,9"?nbsp;
如何获得U盘或者其它类型的存储器d量和剩余可用定wQ?/strong>
调用API GetStoreInfo得到扇区数、每扇区字节敎ͼ怹x(chng)d量。调用API GetDiskFreeSpaceEx得到剩余可用定w?nbsp;
三星2440头文件定?define IIC_BASE 0xB1400000 // 54000000Qdatasheet?4000000Q那么怎么转成0xB1400000Q?/strong>
物理地址映射Ҏ(gu)分ؓ(f)两种Q一U静(rn)态映另一Uؓ(f)动态映。在OEMAddressTable中定义了(jin)物理地址与虚拟地址的映关pd于静(rn)态映,用VirtualCopy映射属于动态映,采用哪种办法都可以。问题(sh)提到的属于静(rn)态映,2440的BSP在map.a文g中定义了(jin)IIC控制寄存器的物理起始地址和对应的虚拟地址如下Q?nbsp;
DCD 0x91400000, 0x54000000, 1 ;
在OEMAddressTable中定义的虚拟地址范围?x8000 0000?x9FFF FFFFQ这部分可缓存,适合内核E序和应用程序用,同时WINCE内核?xA000 0000?xBFFF FFFF中映了(jin)另一份,指向?jin)同L(fng)物理地址Q这部分不可~存Q适合驱动E序使用。三星ARM处理器带有L1U高速缓存,可缓存(sh)(x)提高执行效率。对于特D的讑֤寄存器适合映射C可缓存的虚拟地址?nbsp;
当驱动程序调用VirtualCopy?xB1400000地址dӞW(xu)INCE自动这个地址减去0x2000 0000Q也是0x91400000Q对应的物理地址是0x54000000Q也是IIC控制寄存器的物理起始地址?nbsp;
ZRAM的注册表如何保存数据Q?/strong>
调用API RegCopyFile备䆾注册表。调用API RegRestoreFile恢复注册表,然后调用KernelIoControl热启动恢复生效?nbsp;
如何隐藏和显CwinCE下标准外壳的d栏?
HANDLE hTaskBar = FindWindow(L"HHTaskBar", NULL);
ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOWNORMAL);
如果能让WINCE的IE览器播放flash动画Q?/strong>
播放flash需要Macromedia Flash Player SDKQ参见http://www.adobe.com/products/flashplayer_sdk/。这和real player怼Q都需要WINCEq_的SDKQ都需要申诗?nbsp;
WINCE下内核模式和用户模式有什么区别?
Z(jin)使读者能够详l了(jin)解WINCE的地址映射原理q有两种模式Q在q里我分几个部分说明Q?nbsp;
1、WINCE内核nk.exe的Q务是理操作pȝ核心(j)功能。按照OEMAddressTable的映要求,所有物理地址都映到0x80000000以上Q所以对于内核程序nk.exe和内核模式下的线E来_(d)只要讉K0x80000000以上的有效虚拟地址lMMUp够访问物理地址Q无需再映是内核模式的一个特炏V内核模式的W二个特Ҏ(gu)没有地址讉K限制Q内核模式线E可以访问Q何有效虚拟地址Q所谓有效虚拟地址是指有实际事物对应?nbsp;
2、用h式线E只能访?x80000000以下的虚拟地址I间QW(xu)INCE6.0之前版本的内ؓ(f)每个q程划分32MB的地址I间Q在不调用特D函数的情况下不能相互访问,q样的设计得WINCEpȝ更安全、更E_Q限制访问地址是用h式的W一个特炏V第二个特点是需要多一层映,如果U程要访问物理内存的话需要先映射?x80000000以上Q再lMMU讉K物理内存地址?nbsp;
WINCE的线E具有{UL(参考API GetCallerProcess的说明,有一个很好的例子Q,当应用程序的U程调用API或者调用驱动程序接口函数时Q该U程?x){Udgwes.exe、device.exe、filesys.exe{进E中执行Q{UL由WINCE内核操作的,它会(x)修改U程的上下文Q记录线E的当前q程、调用者进E、拥有者进E三个倹{?nbsp;
3、如果在定制内核的时候选择?#8220;Full Kernel Mode”Q那么在q个内核上运行的所有线E都处于内核模式Q即使调用SetKMode(FALSE)后线E仍然具有内核模式的特点Q能够访问Q何有效的虚拟地址。假讄有一?4MB RAM的WINCE产品QRAM映射?x80000000?x84000000Q如果线E处于内核模式,它就直接可以讉Kq个范围的虚拟地址Q?nbsp;
在OnButton1()中编?nbsp;
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或?0x84000000-0x00019000)
*piTemp = 12345;
在OnButton2()中编?nbsp;
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或?0x84000000-0x00019000)
int iTemp = *piTemp;
先只执行OnButton1()然后关闭E序Q再重启E序然后执行OnButton2()QiTemp仍然{于12345。结果说明了(jin)两点Q内核模式线E可以直接访?x80000000以上的有效虚拟地址Q我们写到RAM中的数据没有丢失Q说明虚拟地址有效?nbsp;
如果在定制内核的时候没有选择“Full Kernel Mode”Q那么在q个内核上运行的所有线E都处于用户模式。可以调用SetKMode(TRUE)使调用线E暂时处于内核模式,q是原来的假讄境,我再举个例子Q?nbsp;
在OnButton1()中编?nbsp;
DWORD oldMode = SetKMode(TRUE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或?0x84000000-0x00019000)
*piTemp = 12345;
在用h式下Q如果不调用SetKMode(TRUE)Q那么执?piTemp = 12345一定会(x)弹出对话框,提示地址讉K非法Q如果调用SetKMode(TRUE)׃?x)提C地址讉K非法Q而且在OnButton2()中仍然能得到12345q个倹{?nbsp;
通过q两个例子我怿读者能够完全了(jin)解两U模式的区别?jin)?nbsp;
4、WINCE提供?jin)两个函数SetKMode和SetProcPermissionsQ其中SetKMode能够把调用线E切换到内核模式Q还可以切换回用h式。SetProcPermissions + GetCurrentPermissionsd当前q程讉K权限l调用线E,SetProcPermissions (0xFFFFFFFF)能让调用U程讉K所有进E空_(d)但是调用U程仍然处于用户模式。SetKMode和SetProcPermissions函数使得用户模式的特点不那么明晰?nbsp;
如上所说一个应用程序的U程可能转移到其它两个进E地址I间中读写数据,而每一个线E在被创建的时候只有访问创建它的进E地址I间的权限,所以驱动程序开发者必d驱动E序d数据前调用SetKMode或者SetProcPermissions增加调用此函数的U程讉K其它q程I间的权限。如果一个应用程序的U程只{Ud一个进E地址I间Q一般ؓ(f)讑֤理器进Edevice.exeQ这U情况下不必增加U程讉K其它q程I间的权限,但如果驱动程序本w创Z(jin)一个线E,那还是要调用SetKMode或者SetProcPermissions增加新的U程讉K其它q程的权限的Q因为驱动程序创建线E时Q当前进Eؓ(f)讑֤理器,所以新U程只具有访问设备管理器q程I间的权限,而不具备讉K应用E序q程I间的权限?nbsp;
5、可能一个编写过单的驱动的初学者会(x)很疑惑,因ؓ(f)开发一个简单的驱动程序根本不需要调用这些函敎ͼ也没有调用过MapPtrToProcessQ那是因为如果标准流驱动接口函数的参Cؓ(f)指针QReadFile、WriteFile、DeviceIoControl参数都有指针Q,W(xu)INCE内核?x)自动映指针包含的地址Q但仅此而已Q其余Q何情况都要求开发者自行处理,比如接口函数的参数是一个指向结构体的指针PAQ而结构体中包括指针PBQPB指针必d接口函C映射Q映后才能讉KQ否则就?x)造成地址讉K非法。所以结构体中每个指针都要映?nbsp;
Z(jin)让读者能?jin)解其中的原因,我D个例子:(x)
假设讑֤理器被加蝲到Slot4Q应用程序A被加载到Slot 8QA只有一个主U程TQT开始执行,按照WINCE的规定,正获得CPU的进E必L到Slot0Q那么在执行代码的时候A的所有虚拟地址都被减去一个偏Ud|也就?×0x02000000QA调用DeviceIoControlQ传递一个指向一个结构体的指针BQ而这个结构体中包含一个指针CQ指针C包含的地址假设?x00030000Q当执行DeviceIoControl时WINCE把设备管理器的进E地址I间映射到Slot0Q因为放在注册表[HKLM\Drivers\BuiltIn]下的驱动E序是由讑֤理器加载的Q自焉动程序的代码D被加蝲到设备管理器q程I间Q但是线E仍然是TQ此时T的当前所在进Eؓ(f)讑֤理器(CurrentProcessQ,A变成?jin)T的调用者进E(CallerProcessQ,T自动h?jin)访问调用者进E空间的权限。这时访问Slot0中的虚拟地址其实质就是访问设备管理器的进E地址I间Q要把地址加上一个偏Ud|也就?×0x02000000Q所以DeviceIoControl讉K指针C包含的地址时本应该加上8×0x02000000Q却加上4×0x02000000Q结果地址q不是设备管理器的合法区域,pȝ׃(x)提示地址讉K非法。而如果做?jin)一个映,指针C包含的地址׃(x)被加一个正的偏移|使地址处于A的地址I间Slot 8中,T此时h讉KAq程I间的权限,讉K到正的虚拟地址当然?x)得到正的数据了(jin)?nbsp;
Z么WINCE目录下的例子用build+sysgen能够~译成EXE文gQ而我d的例子就不能~译呢?
如果q个例子是一个应用程序,那么肯定包括代码文gQ?h .c .cppQ和资源文gQ?rc和其它资源文Ӟ(j)Qbuild工具Ҏ(gu)source文g内容把代码文件编译成lib文gQ资源文件编译成.res文gQsysgen工具Ҏ(gu)makefile文g内容source文g中列出的需要链接的各个库文件合q成一个EXE文g。所以说关键在于makefile文gQW(xu)INCE目录下凡是能够用build+sysgen~译的都在makefile中有如何链接的设|,而我们添加的例子当然没有在makefile中找到如何链接的讄Qnmake工具׃(x)提示不知道如何创建?nbsp;
pcienum.exeq什么用的?
如果你要开发某一个PCI讑֤的驱动程序,首先要知道这个PCI讑֤的信息(如VendorID、DeviceID、BaseClass、SubClassQ和PCIȝ的信息。运行这个pcienum.exep得到相关信息。pcienum.exe提供?jin)源码,位置\Public\Common\Oak\Drivers\Ceddk\Test\Pcienum?nbsp;
wince下如何让操作pȝq入待机模式Q又如何把它Ȁz?
通过注册表就可以讄Q前提是你的驱动和硬仉支持。注册表参见标题(sh)ؓ(f)“GWES Suspend Time-outs”的帮助文档?nbsp;
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]
"BattPowerOff"=dword:300
"ExtPowerOff"=dword:0
"WakeupPowerOff"=dword:60
"ScreenPowerOff"=dword:0
现有一个GPRS模块Q如何通过GPRSq接到InternetQ?/strong>
1、先在内怸加入WAN下面的几个组Ӟ如RAS/PPP、TAPI。WINCE采用unimodem驱动Q所以不必担?j)没有Modem驱动的支持?nbsp;
2、WINCE启动后新Z个拨可接,比如名称?#8220;gprs1”Q输入用户名、密码、电(sh)话号码。电(sh)话号码不同,所采用的模式不一P例如“*99#”是GPRS模式Q?#8220;17201”是普通的数据模式Q速度差很多,价钱也差很多?nbsp;
3、开始连接,q接q程?x)在对话框中昄Q直到显C?#8220;q接成功”?nbsp;
4、打开览器或者自己开发的通讯软g试|络q接情况?nbsp;
5、关闭连接?nbsp;
6、保存[HKEY_CURRENT_USER\Comm\RasBook\gprs1]下的所有数据,d到project.reg中,重新~译后内怸有?jin)一个拨可?#8220;gprs1”?nbsp;
7、调用RAS函数可以修改拨号q接“gprs1”的参敎ͼ如用户名、密码、电(sh)话号码,但是不能修改g讄Q如波特率、串口、数据位、停止位{。RAS函数q能够拨受挂断。ؓ(f)?jin)修?gu)L特率可以多保存几个拨可接,也可以直接调用TAPI开发拨可YӞ另外WINCE自带的拨可接是有源码的Q位|在\PUBLIC\COMMON\OAK\DRIVERS\NETSAMP\CONNMC?nbsp;
采用ZHIVE的注册表如何删除用户保存在注册表中的数据Q恢复到出厂时的注册表?
用户修改的数据保存在user.hv文g中,直接删除一定失败,所以不能通过删除文g实现恢复出厂讄。微软考虑C(jin)q个问题Q在WINCE启动q程中filesys.exe加蝲注册表时?x)调用OEMIoControl函数q传递一个IOCTLQ这个IOCTL在pkfuncs.h中定义如下:(x)
#define IOCTL_HAL_GET_HIVE_CLEAN_FLAG CTL_CODE(FILE_DEVICE_HAL, 49, METHOD_BUFFERED, FILE_ANY_ACCESS)
filesys.exe?x)分别传递参数HIVECLEANFLAG_SYSTEM和HIVECLEANFLAG_USERSQ如果返回gؓ(f)TRUE那么filesys.exe清除原来的注册表文gQ如果返回gؓ(f)FALSE那么filesys.exe保留原来的注册表文g。默认WINCEq没有实现这个IOCTLQ所以OEM要删除注册表文g必d~写q个IOCTL代码。代码的例子可参考标题(sh)ؓ(f)“IOCTL_HAL_GET_HIVE_CLEAN_FLAG”的帮助文档。另外必dioctl.h和ioctl.c两个文g中编写该代码。在ioctl.c文g中找到const OAL_IOCTL_HANDLER g_oalIoCtlTable[]Q添加I(yng)OCTL和对应的处理函数。要q一步了(jin)解这个全局数组Q参见标题(sh)ؓ(f)“IOCTL Library”的帮助文档?nbsp;
如何在不删除必要lg的前提下减小内核文g长度Q?/strong>
要减内核文仉度首先要在用PB的定制内核向g选择自定义,也就是说对于每个lg都由自己来选择Q而不是选择PB的标准配|。但减小内核文g长度最有效最直接的办法是~小字体Q尤其对于东亚字体,采用字体压羃技术ƈ且选择合理的字库文件将明显~小文g长度?nbsp;
1、在定制内核旉择AGFA AC3 Font Compressionlg。SYSGEN变量为SYSGEN_AGFA_FONT?nbsp;
2、参考标题(sh)ؓ(f)“East Asian Font Versions”的帮助文档,从中选择你需要的字库文g加到内核中,从文档可以看出加AC3压羃比不加压~在文g长度斚w差距很大?nbsp;
如何得到WAV文g播放的L_(d)
1、直接读取wav文g头信息,从文件v始地址偏移28个字节长度ؓ(f)4个字节保存的是每U钟播放的字节数Q从文g起始地址偏移40个字节长度ؓ(f)4个字节保存的是声x(chng)据的ȝ字节敎ͼ盔R是播放旉?nbsp;
2、调用IGraphBuilder::RenderFile打开一个wav文gQ然后通过IGraphBuilder得到IMediaSeeking指针Q再调用IMediaSeeking::GetDuration得到ȝ旉Q结果要除以10000000Q,IMediaSeeking::GetCurrentPosition得到当前播放旉?nbsp;
如何在Dialog-BasedE序中加入menubarQ?/strong>
先调用CommandBar_Create再调用CommandBar_InsertMenubar?nbsp;
请问MultiByteToWideChar与_T、L、TEXT的区别?
MultiByteToWideChar函数转换的对象可以是帔R也可以是变量。其它只能{换常量。_T和TEXT?x)根据当前系l是否定义_UNICODE宏来军_是否转换Q而L是转换成宽字符Q当然也包括其他cd帔R的{换?nbsp;
在用UBSU缆通过ActiveSync同步有效的情况下Q如何插上USBU缆后WINCE自动与PC同步Q?/strong>
1、新Z个拨可接,假设名称?#8220;usb1”Q选择q接cd?#8220;直接q接”Qƈ在连接设备里选择通过USBU缆q接?nbsp;
2、将注册表[HKEY_CURRENT_USER\Comm\RasBook\usb1]下的数据d到project.reg或者platform.reg中?nbsp;
3、在[HKEY_CURRENT_USER\ControlPanel\Comm]下添加如下:(x)
"AutoCnct"=dword:1 ///直接q接
"Cnct"="usb1" ///q接名称
4、重新编译内核。ؓ(f)?jin)节省编译时间也可以在内核工E下搜烦(ch)*.reg文gQ将2?步骤中的注册表数据添加其中,然后直接make image?nbsp;
如何通过q程句柄来获得该q程的主H口句柄Q?/strong>
好像没有API能够通过q程句柄直接获得ȝ口的句柄Q因为ƈ非每个应用程序都带UI。但是可以反q来Q先枚D当前pȝ所有主H口Q然后根据每个窗口的句柄调用GetWindowThreadProcessId函数得到q程的IDQ再调用OpenProcess得到q程句柄Q与现有的进E句柄比较?nbsp;
我做的显C驱动DLL已经~译成功?jin),但是在加载显C驱动的q程中弹?gu)框,提示如下Q?nbsp;
unhandled exception in gwes.exe (0xc0000005 access violation)
提示的错误——地址讉K非法Q表明你的驱动程序代码ƈ没有在读写数据前dSetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数让线E能够访问Q何进E的地址I间。你可以调用 IsBadReadPtr和IsBadWritePtr函数(g)地址是否能够合法讉K。编写和gwes有关的驱动程序应该首先调用SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函数Q这是一个好?fn)惯?nbsp;
请问在嵌入式pȝ中如何设|GPRS拔号用的APNQ?/strong>
对一个拨可接比?#8220;我的q接”单击鼠标右键Q在弹出的菜单中选择“属?#8221;Q然后单?#8220;配置”?#8220;拨号选项”Q在“附加讄”中添加AT命o(h)?#8220;+cgdcont=1,"ip","cmnet"”?#8220;cmnet”位置即ؓ(f)APN?nbsp;
WINCE的IP Phone功能如何Q?/strong>
WINCE的voip需要c-s-cl构Q既需要服务器的中转,而skype采用W三代p2p技术就不需要中转,但是在gprs下也做不到语x(chng)畅。skype有pocket pc版本Q但是无U方面需要wlan或者cdma?nbsp;
三星ARMq_如何定义自己的中断IDQ?/strong>
以S3C2410ZQ在oalintr.h文g中定义中断IDQ也USYSINTRQ例?nbsp;#define SYSINTR_MYINT (SYSINTR_FIRMWARE+20)Q最大g能超qSYSINTR_FIRMWARE+23。然后在armint.c文g中找到OEMInterruptHandler函数Q用if (IntPendVal == INTSRC_XXX) 判断当前发生的中断源P然后q回SYSINTR_MYINT。内核分别调用OEMInterruptDisableQ禁止当前中断)(j)、OEMInterruptDoneQ中断处理结束)(j)、OEMInterruptEnableQ当前中断有效)(j)三个函数Q参数都Z断IDQ在q三个函C?nbsp;case SYSINTR_MYINT判断当前要处理的中断?nbsp;
如何开发Y件从PC端复制文件到ZWINCE的设备?
调用RAPIQRemote Application Programming InterfaceQ函敎ͼ此函数集由桌面计机调用Q由ZWINCE的设备执行。一旦连接上可以在桌面计算机端调用RAPI。通过注册表还可以限制RAPI能够讉K目录的范围。具体参考RAPI和RDPQ远E桌面协议)(j)?nbsp;
请问如何对NandFlash分区、格式化Q?/strong>
你看看WINCE420\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cppQ在Eboot中先要调用BP_LowLevelFormat(
DWORD dwStartBlock, DWORD dwNumBlocks, DWORD dwFlags)再flash的一个区域徏立空的MBR,然后q箋(hu)两次调用BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)函数来徏立BINFS和FAT分区。徏好后Q将nk.bin烧入binfs分区中?nbsp;
要做个弹出对话框h always on top 属性,如何实现Q?/strong>
调用SetWindowPos(.. , HWND_TOPMOST, ...., SWP_NOACTIVATE)?nbsp;
s3c2410QWINCE下网lPING一?x)就断,如何解决Q?/strong>
原因在于中断处理E序把已l生的中断标志清除掉了(jin)Q这样就丢失一ơ中断。因为原驱动里配|中断ؓ(f)上升沿触发,一ơ中断丢失就D不会(x)再生中断信可变,因ؓ(f)只有在中断服务中d?jin)cs8900?nbsp;Interrupt status queue寄存器后Q才?x)生下一ơ中断!解决办法Q?nbsp;
1、在cfw.c文g中全局定义BOOL Inited = FALSE
2、修改OEMInterruptEnable()中case SYSINTR_ETHER: 下面的语句ؓ(f)Q?nbsp;
if(Inited == FALSE)
{
s2410IOP->rEINTPEND = 0x200;
s2410INT->rSRCPND = BIT_EINT8_23;
if (s2410INT->rINTPND & BIT_EINT8_23)
s2410INT->rINTPND = BIT_EINT8_23;
Inited = TRUE;
}
s2410IOP->rEINTMASK &= ~0x200;
s2410INT->rINTMSK &= ~BIT_EINT8_23;
breakQ?nbsp;
注:(x)本解军_法{载于http://stoned.blogchina.com/stoned/3083045.htmlQ非我本人研I成果?nbsp;
已经搜烦(ch)到文Ӟ如何用CListBox以图标Ş式显C出来?
CListCtrl ListCtrl;
CImageList ImageList;
ImageList.Create(IDB_BITMAP, 48, 2, RGB(0,0,0));
ListCtrl.SetImageList(&ImageList, LVSIL_NORMAL);
ListCtrl.InsertItem(iListIndex, strItem, 1);
如何改变控制面板中电(sh)源属性对话框的尺?
1、需要修改对话框的尺寸是因ؓ(f)对话框是以资源方式加载的Q不?x)根据当前系l显C分辨率而自我调节尺寸?nbsp;
2、安装WINCE后有一些组ӞfeatureQ的资源文g*.res已l有?jin),如果你不改变Q那么build内核的时候PB只是把这?res复制到工E目录下Q然后与*.obj合ƈ成EXE、DLL、CPL。所以修改了(jin).rc文g里面的对话框寸后要重新~译.rc文g?res文gQ然后再覆盖原来WINCE自带?res文g?nbsp;
3、改变对话框寸有两U办法:(x)一U方法是更改pȝ字体字号Q系l字体的字号变化?x)?jing)响对话框的尺寸,但是~点是所有系l字体有关的UI都会(x)改变。另一U是?rc文g中调整对话框寸Q然后编译成.res文gQ再?res复制到对应的语言目录里,比如目录名ؓ(f)0804Q中文)(j)Q再执行Rebuild命o(h)重新~译内核Q或者执行sysgenQbuild。在研究中我发现.res文g虽然能够直接用EVC打开、修攏V保存,但是和其它Obj链接成EXE、DLL、CPL后ƈ不能q行Q所以还是徏议读者用CE自带的rc工具~译最好。读者可在PB的命令行中键?#8220;rc /?”?jin)解rc.exe工具的用途和参数?nbsp;
使用EVC build之后q接模拟器的时候,提示download file{了(jin)一?x)又出现download failedQ?/strong>
一般这L(fng)问题?sh)下面几个步骤解冻I(x)
1、如果之前能启动模拟器而现在不能,那么先clean然后重启计算机再build?nbsp;
2、如果开发的L为WINXP+SP2Q可能存在与EVC模拟器不兼容的情况,(g)查C:\boot.iniQ将/noexecute=optin改ؓ(f)/execute=optin?nbsp;
3、检查你的模拟器是否能运行,假设你正用的SDK名称为MYSDKQ单击菜单tools—configure platform managerQ选择MYSDK—MYSDK emulatorQ再单击properties—testQ看看模拟器是否能够启动Q如果能启动那问题就不大?nbsp;
4、单击菜单build—update remote output filesQ看看模拟器是否能够启动?nbsp;
5、如果上q办法均不行Q关闭EVC然后重新建立一个新的工E,~译Q看看模拟器是否能够启动Q如果能启动说明原来工程Z(jin)问题Q最好恢复原工程的备份?nbsp;
如何讄能够自动拨号、禁止自动拨P
在[HKEY_LOCAL_MACHINE\Comm\Autodial]下是自动拨号的注册表讄?nbsp;
Enabled=DWORD:1 ///是否能够自动拨号
FailRetryWaitMS=DWORD ///如果p|再次拨号的等待时?nbsp;
RasEntryName1= REG_SZ ///自动拨号采用的拨可接名U?nbsp;
更多l节请参考标题(sh)ؓ(f)“Auto Dial Registry Settings”的帮助文档?nbsp;
]]>
预处理器QPreprocessorQ?/strong>
1 . 用预处理指o(h)#define 声明一个常敎ͼ用以表明1q中有多秒Q忽略闰q问题)(j)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在q想看到几g事情Q?br> 1) #define 语法的基本知识(例如Q不能以分号l束Q括L(fng)使用Q等{)(j)
2)懂得预处理器ؓ(f)你计常数表辑ּ的|因此Q直接写Z是如何计一q中有多秒而不是计出实际的|是更清晰而没有代L(fng)?br> 3) 意识到这个表辑ּ一?6位机的整型数溢出-因此要用到长整型W号L,告诉~译器这个常数是的长整型数?br> 4) 如果你在你的表达式中用到ULQ表C无W号长整型)(j)Q那么你有了(jin)一个好的v炏V记住,W一印象很重要?br>
2 . 写一?标准"宏MIN Q这个宏输入两个参数q返回较?yu)的一个?br> #define MIN(A,B) Q(AQ?<= (B) ? (A) : (B))
q个试是ؓ(f)下面的目的而设的:(x)
1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作W?变(sh)ؓ(f)标准C的一部分之前Q宏是方便生嵌入代码的唯一Ҏ(gu)Q对于嵌入式pȝ来说Qؓ(f)?jin)能辑ֈ要求的性能Q嵌入代码经常是必须的方法?br> 2)三重条g操作W的知识。这个操作符存在C语言中的原因是它使得~译器能产生比if-then-else更优化的代码Q了(jin)解这个用法是很重要的?br> 3) 懂得在宏中小?j)地把参数用括号括v?br> 4) 我也用这个问题开始讨论宏的副作用Q例如:(x)当你写下面的代码时会(x)发生什么事Q?br> least = MIN(*p++, b);
3. 预处理器标识#error的目的是什么?
如果你不知道{案Q请看参考文?。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会(x)读C语言课本的附录去扑և象这U问题的{案。当然如果你不是在找一个书呆子Q那么应试者最好希望自׃要知道答案?br>
d@环(Infinite loopsQ?/strong>
4. 嵌入式系l中l常要用到无限@环,你怎么L(fng)C~写d@环呢Q?br> q个问题用几个解x(chng)案。我首选的Ҏ(gu)是:(x)
while(1)
{
}
一些程序员更喜Ƣ如下方案:(x)
for(;;)
{
}
q个实现方式让我为难Q因个语法没有确切表辑ֈ底怎么回事。如果一个应试者给?gu)个作为方案,我将用这个作Z个机?x)去探究他们q样做的基本原理。如果他们的基本{案是:(x)"我被教着q样做,但从没有惛_qؓ(f)什么?q会(x)l我留下一个坏印象?br>
W三个方案是?goto
Loop:
...
goto Loop;
应试者如l出上面的方案,q说明或者他是一个汇~语aE序员(q也许是好事Q或者他是一个想q入新领域的BASIC/FORTRANE序员?br>
数据声明QData declarationsQ?/strong>
5. 用变量al出下面的定?br> a) 一个整型数QAn integerQ?
b)一个指向整型数的指针( A pointer to an integerQ?
c)一个指向指针的的指针,它指向的指针是指向一个整型数Q?A pointer to a pointer to an integeQr
d)一个有10个整型数的数l( An array of 10 integersQ?
e) 一个有10个指针的数组Q该指针是指向一个整型数的。(An array of 10 pointers to integersQ?
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integersQ?
g) 一个指向函数的指针Q该函数有一个整型参数ƈq回一个整型数QA pointer to a function that takes an integer as an argument and returns an integerQ?
h) 一个有10个指针的数组Q该指针指向一个函敎ͼ该函数有一个整型参数ƈq回一个整型数Q?An array of ten pointers to functions that take an integer argument and return an integer Q?br>
{案是:(x)
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
Zl常声称q里有几个问题是那种要翻一下书才能回答的问题,我同意这U说法。当我写q篇文章ӞZ(jin)定语法的正性,我的查?jin)一下书。但是当我被面试的时候,我期望被问到q个问题Q或者相q的问题Q。因为在被面试的q段旉里,我确定我知道q个问题的答案。应试者如果不知道所有的{案Q或臛_大部分答案)(j)Q那么也没有ؓ(f)q次面试做准备,如果该面试者没有ؓ(f)q次面试做准备,那么他又能ؓ(f)什么出准备呢?
Static
6. 关键字static的作用是什么?
q个单的问题很少有h能回{完全。在C语言中,关键字static有三个明昄作用Q?br> 1)在函CQ一个被声明为静(rn)态的变量在这一函数被调用过E中l持其g变?br> 2) 在模块内Q但在函C外)(j)Q一个被声明为静(rn)态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量?br> 3) 在模块内Q一个被声明为静(rn)态的函数只可被这一模块内的其它函数调用。那是Q这个函数被限制在声明它的模块的本地范围内用?br>
大多数应试者能正确回答W一部分Q一部分能正回{第二部分,同是很少的h能懂得第三部分。这是一个应试者的严重的缺点,因ؓ(f)他显然不懂得本地化数据和代码范围的好处和重要性?br>
Const
7Q关键字const有什么含意?
我只要一听到被面试者说Q?const意味着常数"Q我q道我正在和一个业余者打交道。去qDan Saks已经在他的文章里完全概括?jin)const的所有用法,因此ESP(译者:(x)Embedded Systems Programming)的每一位读者应该非常熟(zhn)const能做什么和不能做什?如果你从没有d那篇文章Q只要能说出const意味着"只读"可以了(jin)。尽这个答案不是完全的{案Q但我接受它作ؓ(f)一个正的{案。(如果你想知道更详l的{案Q仔l读一下Saks的文章吧。)(j)
如果应试者能正确回答q个问题Q我问他一个附加的问题Q?br> 下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
/******/
前两个的作用是一Pa是一个常整型数。第三个意味着a是一个指向常整型数的指针Q也是Q整型数是不可修改的Q但指针可以Q。第四个意思a是一个指向整型数的常指针Q也是_(d)指针指向的整型数是可以修改的Q但指针是不可修改的Q。最后一个意味着a是一个指向常整型数的常指针(也就是说Q指针指向的整型数是不可修改的,同时指针也是不可修改的)(j)。如果应试者能正确回答q些问题Q那么他q我留下了(jin)一个好印象。顺带提一句,也许你可能会(x)问,即不用关键?constQ也q是能很Ҏ(gu)写出功能正确的程序,那么我ؓ(f)什么还要如此看重关键字const呢?我也如下的几下理由:(x)
1) 关键字const的作用是为给M代码的h传达非常有用的信息,实际上,声明一个参Cؓ(f)帔R是ؓ(f)?jin)告诉?jin)用户q个参数的应用目的。如果你曾花很多旉清理其它人留下的垃圾Q你׃(x)很快学会(x)感谢q点多余的信息。(当然Q懂得用const的程序员很少?x)留下的垃圾让别人来清理的。)(j)
2) 通过l优化器一些附加的信息Q用关键字const也许能生更紧凑的代码?br> 3) 合理C用关键字const可以使编译器很自然地保护那些不希望被改变的参敎ͼ防止其被无意的代码修攏V简而言之,q样可以减少bug的出现?br>
Volatile
8. 关键字volatile有什么含?q给Z个不同的例子?br> 一个定义ؓ(f)volatile的变量是说这变量可能?x)被意想不到地改变,q样Q编译器׃?x)去假设q个变量的g(jin)。精地说就是,优化器在用到q个变量时必Lơ都心(j)地重新读取这个变量的|而不是用保存在寄存器里的备份。下面是volatile变量的几个例子:(x)
1) q行讑֤的硬件寄存器Q如Q状态寄存器Q?br> 2) 一个中断服务子E序中会(x)讉K到的非自动变?Non-automatic variables)
3) 多线E应用中被几个Q务共享的变量
回答不出q个问题的h是不?x)被雇䄦的。我认ؓ(f)q是区分CE序员和嵌入式系l程序员的最基本的问题。搞嵌入式的家伙们经常同g、中断、RTOS{等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将?x)带来灾难?br> 假设被面试者正地回答?jin)这是问题(嗯,怀疑是否会(x)是这P(j)Q我稍微深I一下,看一下这家伙是不是直正懂得volatile完全的重要性?br> 1)一个参数既可以是constq可以是volatile吗?解释Z么?br> 2); 一个指针可以是volatile 吗?解释Z么?br> 3); 下面的函数有什么错误:(x)
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:(x)
1)是的。一个例子是只读的状态寄存器。它是volatile因ؓ(f)它可能被意想不到地改变。它是const因ؓ(f)E序不应该试囑֎修改它?br> 2); 是的。尽这q不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时?br> 3) q段代码有点变态。这D代码的目的是用来返指针*ptr指向值的qx(chng)Q但是,׃*ptr指向一个volatile型参敎ͼ~译器将产生cM下面的代码:(x)
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
׃*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,q段代码可能q不是你所期望的^方|正确的代码如下:(x)
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulationQ?/strong>
9. 嵌入式系lL要用户对变量或寄存器q行位操作。给定一个整型变量aQ写两段代码Q第一个设|a(chn)的bit 3Q第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变?br> 对这个问题有三种基本的反?br> 1)不知道如何下手。该被面者从没做qQ何嵌入式pȝ的工作?br> 2) 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同~译器之间是不可UL的,同时也保证了(jin)的你的代码是不可重用的。我最q不q看?Infineon为其较复杂的通信芯片写的驱动E序Q它用到?jin)bit fields因此完全Ҏ(gu)无用Q因为我的编译器用其它的方式来实现bit fields的。从道dԌ(x)永远不要让一个非嵌入式的家伙_实际硬件的辏V?br> 3) ?#defines ?bit masks 操作。这是一个有极高可移植性的Ҏ(gu)Q是应该被用到的Ҏ(gu)。最佳的解决Ҏ(gu)如下Q?br>
#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些h喜欢|和清除D定义一个掩码同时定义一些说明常敎ͼq也是可以接受的。我希望看到几个要点Q说明常数、|=?amp;=~操作?br>
讉K固定的内存(sh)|(Accessing fixed memory ***sQ?/strong>
10. 嵌入式系l经常具有要求程序员去访问某特定的内存(sh)|的特点。在某工E中Q要求设|一l对地址?x67a9的整型变量的gؓ(f)0xaa66。编译器是一个纯_的ANSI~译器。写代码d成这一d?br> q一问题?gu)试你是否知道?f)?jin)访问一l对地址把一个整型数强制转换QtypecastQؓ(f)一指针是合法的。这一问题的实现方式随着个h风格不同而不同。典型的cM代码如下Q?br> int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
A more obscure approach is:
一个较晦ӆ的方法是Q?br>
*(int * const)(0x67a9) = 0xaa55;
即你的品味更接q第二种Ҏ(gu)Q但我徏议你在面试时使用W一U方案?br>
中断QInterruptsQ?/strong>
11. 中断是嵌入式pȝ中重要的l成部分Q这D?jin)很多编译开发商提供一U扩展—让标准C支持中断。具代表事实是,产生?jin)一个新的关键字 __interrupt。下面的代码׃用了(jin)__interrupt关键字去定义?jin)一个中断服务子E序(ISR)Q请评论一下这D代码的?br>
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
q个函数有太多的错误?jin),以至让h不知从何说v?jin)?x)
1)ISR 不能q回一个倹{如果你不懂q个Q那么你不会(x)被雇用的?br> 2) ISR 不能传递参数。如果你没有看到q一点,你被雇用的机?x)等同第一V?br> 3) 在许多的处理?~译器中QQ点一般都是不可重入的。有些处理器/~译器需要让额处的寄存器入栈Q有些处理器/~译器就是不允许在ISR中做点q算。此外,ISR应该是短而有效率的,在ISR中做点q算是不明智的?br> 4) 与第三点一脉相承,printf()l常有重入和性能上的问题。如果你丢掉?jin)第三和W四点,我不?x)太为难你的。不用说Q如果你能得到后两点Q那么你的被雇用前景来光明了(jin)?br>
代码例子QCode examplesQ?/strong>
12 . 下面的代码输出是什么,Z么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
q个问题?gu)试你是否懂得C语言中的整数自动转换原则Q我发现有些开发者懂得极这些东ѝ不如何,q无W号整型问题的答案是输出?">6"。原因是当表辑ּ中存在有W号cd和无W号cd时所有的操作数都自动转换为无W号cd。因?20变成?jin)一个非常大的正整数Q所以该表达式计出的结果大?。这一点对于应当频J用到无W号数据cd的嵌入式pȝ来说是丰帔R要的。如果你{错?jin)这个问题,你也到了(jin)得不到q䆾工作的边~?br>
13. 评h(hun)下面的代码片断:(x)
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
对于一个int型不?6位的处理器ؓ(f)_(d)上面的代码是不正的。应~写如下Q?br>
unsigned int compzero = ~0;
q一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的l验里,好的嵌入式程序员非常准确地明白硬件的l节和它的局限,然而PC机程序往往把硬件作Z个无法避免的?ch)恼?br> C(jin)q个阶段Q应试者或者完全垂头气了(jin)或者信?j)满满志在必得。如果显然应试者不是很好,那么q个试在q里l束?jin)。但如果昄应试者做得不错,那么我就扔出下面的追加问题,q些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提?gu)些问题,我希望更多看到应试者应付问题的Ҏ(gu)Q而不是答案。不如何,你就当是q个׃?..
动态内存分配(Dynamic memory al***Q?/strong>
14. 管不像非嵌入式计算机那么常见,嵌入式系l还是有从堆QheapQ中动态分配内存的q程的。那么嵌入式pȝ中,动态分配内存可能发生的问题是什么?
q里Q我期望应试者能提到内存片Q碎片收集的问题Q变量的持行旉{等。这个主题已l在ESP杂志中被q泛地讨?jin)(主要?P.J. Plauger, 他的解释q远过我这里能提到的Q何解释)(j)Q所有回q头看一下这些杂志吧Q让应试者进入一U虚假的安全感觉后,我拿?gu)么一个小节目Q?br> 下面的代码片D늚输出是什么,Z么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
q是一个有的问题。最q在我的一个同事不l意?gl了(jin)函数mallocQ得C(jin)一个合法的指针之后Q我才想到这个问题。这是上面的代码,该代码的输出?Got a valid pointer"。我用这个来开始讨L(fng)一问题Q看看被面试者是否想到库例程q样做是正确。得到正的{案固然重要Q但解决问题的方法和你做军_的基本原理更重要些?br>
Typedef
15 Typedef 在C语言中频J用以声明一个已l存在的数据cd的同义字。也可以用预处理器做cM的事。例如,思考一下下面的例子Q?br>
#define dPS struct s *
typedef struct s * tPS;
以上两种情况的意N是要定义dPS ?tPS 作ؓ(f)一个指向结构s指针。哪U方法更好呢Q(如果有的话)(j)Z么?
q是一个非常微妙的问题QQ何h{对q个问题Q正当的原因Q是应当被恭喜的。答案是Qtypedef更好。思考下面的例子Q?br>
dPS p1,p2;
tPS p3,p4;
W一个扩展ؓ(f)
struct s * p1, p2;
.
上面的代码定义p1Z个指向结构的指,p2Z个实际的l构Q这也许不是你想要的。第二个例子正确地定义了(jin)p3 和p4 两个指针?br>
晦ӆ的语?/strong>
16 . C语言同意一些o(h)人震惊的l构,下面的结构是合法的吗Q如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
q个问题做个测验的一个愉快的l尾。不你怸怿Q上面的例子是完全合乎语法的。问题是~译器如何处理它Q水q不高的~译作者实际上?x)争个问题,?gu)最处理原则Q编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:(x)
c = a++ + b;
因此, q段代码持行后a = 6, b = 7, c = 12?br> 如果你知道答案,或猜出正答案,做得好。如果你不知道答案,我也不把q个当作问题。我发现q个问题的最大好处是q是一个关于代码编写风|代码的可L,代码的可修改性的好的话题?br>
好了(jin)Q伙计们Q你现在已经做完所有的试?jin)。这是我出的C语言试题,我怀着愉快的心(j)情写完它Q希望你以同L(fng)?j)情d它。如果是认ؓ(f)q是一个好的测试,那么量都用C的找工作的过E中d。天知道也许q个一两年Q我׃做现在的工作Q也需要找一个?br>
]]>
播放器主要由核心(j)框架模块Qcommon工程Q和解码器分d插gl成?br>TCPMP的插仉常多Q、libmad我们联合几个最常用的插Ӟffmpeg、splitterQ来说明Q其中interface插g实现TCPMP的界面,׃他和媒体播放没有什么关p,q部分可以完全被替换掉,替换成自q界面?br>ffmpeg工程是系l主要的韌频解码模块,ffmpeg是一个集录制、{换、音/视频~码解码功能Z体的完整的开源解x(chng)案。FFmpeg的开发是ZLinux操作pȝQ但是可以在大多数操作系l中~译和用。ffmpeg支持MPEG、DivX、MPEG4、AC3、DV、FLV{?0多种~码QAVI、MPEG、OGG、Matroska、ASF{?0多种解码。很多开源播攑֙都用C(jin)ffmpeg。但是ffmpegE序解码效率不是很高Q系l仅仅用了(jin)FFmpeg的部分解码功能?br>ffmpegȝ录下主要有libavcodec、libavformat和libavutil{子目录。其中libavcodec用于存放各个encode/decode模块Qlibavformat用于存放muxer/demuxer模块Qlibavutil用于存放内存操作{常用模块。本pȝ的媒体文件分d有单独的splitter模块完成所以不需要libavformat子目录。ffmpeg目录下libavcodec、libavutil保留子目录?/p>
{
int Version; //版本信息
uint32_t ProgramId;
const tchar_t* ProgramName; //应用E序名称
const tchar_t* ProgramVersion; //E序版本P字符?br> const tchar_t* CmdLine; //E序命o(h)行信?br> void* Wnd; //视频渲染H口句柄
void* NodeLock; //功能模块讉K临界Z斥变?br> array Node; //功能模块数据对象数组
array NodeClass; //功能模块定义对象数组Q按照系l逻辑关系l织
array NodeClassPri; //功能模块定义对象数组Q按照系l逻辑关系和模块优先排列
array NodeModule; //外部插g模块数组
int LoadModuleNo; //当前正在装蝲的外部插件序?br> void* LoadModule;
array StrTable[2]; //字符串资源数l,字符串分?br> //l底层用的标准字符串资源和
//l界面用的昄字符串资源,两种资源用两个数l表C?br> array StrBuffer;
array StrModule; //未?br> void* StrLock; //字符串数l访问(f)界区互斥变量
uint32_t Lang; //当前使用语言标志
int CodePage; //当前使用代码|?br> struct pcm_soft* PCM; //PCM音频信号转换模块
struct blitpack* Blit; //视频信号渲染模块
struct node* Platform; //得到q_相关信息
struct node* Advanced; //得到播放模块高信息
struct node* Player; //播放控制模块
notify Error; //信息错误回调函数
//屏幕旋{信息Q在某些pȝ中屏q可以旋?0度或180?br> int (*HwOrientation)(void*);
void *HwOrientationContext;
bool_t TryDynamic; //未?br> int SettingsPage; //未?br> size_t StartUpMemory; //可以使用的有效内存数
bool_t InHibernate; //是否q入休眠状?br> bool_t WaitDisable; //未?br> int FtrId; //未?br> bool_t LowMemory; //可以使用的有效内存数是否于pȝ要求的最低要?br> //动态代码生成中间状态及(qing)数据
bool_t CodeFailed;
bool_t CodeMoveBack;
bool_t CodeDelaySlot;
void* CodeLock;
void* CodeInstBegin;
void* CodeInstEnd;
int NextCond;
bool_t NextSet;
bool_t NextByte;
bool_t NextHalf;
bool_t NextSign;
uint32_t* FlushCache; //未?br> void* CharConvertUTF8; //未?br> void* CharConvertCustom; //未?br> int CustomCodePage; //未?br> void* CharConvertAscii; //未?br> void* Application;
void* Logger; //未?br> bool_t KeepDisplay; //是否保持背光长亮
int DisableOutOfMemory; //未?/p>
核心(j)模块上下文指针可以通过全局函数获得context* Context();
初始化上下文对象的全局函数是bool_t Context_Init(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine,void* Application);其中Name参数为应用程序名UͼVersion为版本信息字W串?br>释放上下文对象的全局函数是void Context_Done();?br>void Context_Wnd(void*);函数视频播攄口句柄初始化l设备上下文?/p>
#define CF_GLOBAL 0x01000000
#define CF_SETTINGS 0x02000000
#define CF_ABSTRACT 0x08000000
抽象节点没有对应的对象实例,cMC++的抽象基c,Z(jin)按照逻辑关系l织pȝl构而存在,例如NODE是抽象节点。全局节点全局只有一个对象的实例Q如播放控制模块PLAYER_ID。设|节点表C和pȝ播放讄相关Q比如声韛_衡器模块EQUALIZER_IDQ颜色控制模块COLOR_ID。实节点与抽象节点不同,指可以生成对象实例的节点Q实节点没有Ҏ(gu)标识Q一般以数据对象占用内存大小表示是否是一个实节点Q创Ҏ(gu)要根据该信息分配内存单元Q实节点也可以有子节点,例如QMMS_ID的父节点是HTTP_ID。全局节点Q设|节点和实节点可以相互组合,比如播放控制节点同时是全局节点Q设|节点和实节炏V节点名U后带_ID的就是实节点Q否则就是抽象节炏V?/p>
?nbsp; ?nbsp; ├─EQUALIZER_ID Q声韛_衡器模块Q?br> ?nbsp; ?nbsp; ├─VBUFFER_ID Q视频缓冲模块)(j)
?nbsp; ?nbsp; ├─DMO QDirectX Media ObjectQ?br> ?nbsp; ?nbsp; ?nbsp; ├─WMV_ID
?nbsp; ?nbsp; ?nbsp; ├─WMS_ID
?nbsp; ?nbsp; ?nbsp; ├─WMVA_ID
?nbsp; ?nbsp; ?nbsp; ├─WMA_ID
?nbsp; ?nbsp; ?nbsp; └─WMAV_ID
?nbsp; ?nbsp; ├─FFMPEG VIDEO QF(tun)FMpeg 解码模块Q?br> ?nbsp; ?nbsp; └─LIBMAD_ID QLibmad Mp3解码模块Q?br> ?nbsp; ├─OUT Q信h染模块)(j)
?nbsp; ?nbsp; ├─AOUT Q音频信h染)(j)
?nbsp; ?nbsp; ?nbsp; ├─NULLAUDIO_ID
?nbsp; ?nbsp; ?nbsp; └─WAVEOUT_ID
?nbsp; ?nbsp; └─VOUT Q视频信h染)(j)
?nbsp; ?nbsp; ├─NULLVIDEO_ID
?nbsp; ?nbsp; └─OVERLAY
?nbsp; ├─IDCT Q离散余弦解码模块)(j)
?nbsp; ?nbsp; └─SOFTIDCT_ID
?nbsp; └─CODECIDCTQ离散余弦解码模块,函数比IDCT要少Q?br> ?nbsp; └─MPEG1_ID
├─MEDIA Q媒体文件格式编码解析模块)(j)
?nbsp; ├─FORMAT Q格式解析模块)(j)
?nbsp; ?nbsp; └─FORMATBASE
?nbsp; ?nbsp; ├─RAWAUDIO
?nbsp; ?nbsp; ?nbsp; └─MP3_ID
?nbsp; ?nbsp; ├─RAWIMAGE
?nbsp; ?nbsp; ├─ASF_ID
?nbsp; ?nbsp; ├─AVI_ID
?nbsp; ?nbsp; ├─MP4_ID
?nbsp; ?nbsp; ├─MPG_ID
?nbsp; ?nbsp; ├─NSV_ID
?nbsp; ?nbsp; └─WAV_ID
?nbsp; ├─PLAYLIST Q播攑ֈ表模块)(j)
?nbsp; ?nbsp; ├─ASX_ID
?nbsp; ?nbsp; ├─M3U_ID
?nbsp; ?nbsp; └─PLS_ID
?nbsp; └─STREAMPROCESS Q数据流处理模块Q?br> ├─STREAM Q数据输入模块)(j)
?nbsp; ├─MEMSTREAM_ID Q内存数据流模块Q?br> ?nbsp; ├─FILE_ID Q文件IO模块Q?br> ?nbsp; └─HTTP_ID Q网l数据获取模块)(j)
├─TIMER Q定时器模块Q?br> ?nbsp; └─SYSTIMER_ID
├─ASSOCIATION_ID Q文件扩展名自动兌模块Q?br> ├─ADVANCED_ID Q高U设|模块)(j)
├─COLOR_ID Q颜色控制模块)(j)
├─PLATFORM_ID Q^C息模块)(j)
├─XSCALEDRIVER_ID QIntel XScale CPU 信息模块Q?br> ├─PLAYER_ID Q播放控制模块)(j)
└─PLAYER_BUFFER_ID Q播攄冲模块)(j)
节点?wi)状l构pq个?rn)态定义对?nodedef)实例实现Q?br> typedef struct nodedef
{
int Flags;
int Class;
int ParentClass;
int Priority;
nodecreate Create;
nodedelete Delete;
} nodedef;
Flags表示当前节点的类型:(x)抽象、实节点、全局、设|?br> Class表示当前节点的标识,如MEDIA_CLASS或ASF_ID{等?br> ParentClass表示当前节点的父节点标识Q如SYSTIMER_ID对象的父节点是TIMER_CLASS?br> Priority表示当前节点优先U?br> Create和Delete是两个函数指针,表示该节点的创徏函数和销毁函数?br> 如播放控制模块的l构定义?br> static const nodedef Player =
{
sizeof(player_base)|CF_GLOBAL|CF_SETTINGS,
PLAYER_ID,
NODE_CLASS,
PRI_MAXIMUM+600,
(nodecreate)Create,
(nodedelete)Delete,
};
l大多数节点都有一个对应的数据对象Q记录该节点的数据和Ҏ(gu)Q每一个子节点对象都是以父节点对象作ؓ(f)该节点一个元素,cMC++的封装承机制。如果子节点的父节点没有数据对象Q该节点可以从node节点直接l承。每一个节炚w可以看成Node节点的直接或间接子节点,所以所有节点头以一个相同的nodel构开_(d)子节点可能还有自q属性,在承父对象后就是子节点自己的元素?br> typedef struct node
{
int Class;
nodeenum Enum;
nodeget Get;
nodeset Set;
} node;
Class表示该对象的标识Q如PLAYER_ID?br> Enum是一个函数指针,指向一个函数用于枚丑ֽ前节点的属性?br> Get是一个函数指针,得到当前节点某一属性倹{?br> Set是一个函数指针,讄当前节点的某一属性数倹{?br>
节点的属性值数据特性在一个static const datatable xxxParams[] = {……};的静(rn)态数l里定义?br> typedef struct datatable
{
int No;
int Type;
int Flags;
int Format1;
int Format2;
} datatable;
No表示属性的标识Q如播放控制模块?define PLAYER_PLAY 0x32 pC控制播攑֙播放或暂停?br> Type表示属性的数据cdQ可用值在node.h中定义?br> Flags是属性数据的标志Q表C数据是不是只L据,是否有最大最值等{,可用值在node.h中定义,如果该标志包含DF_SETUP同时不包含DF_NOSAVE和DF_RDONLY属性,该属性会(x)被记录在注册表中Q下ơ启动时用注册表的数据初始化该属性?br> Format1和Format2是可选标志与Flags配合使用Q比如如果Flags表示该属性存在最大最|Format1和Format2可以分别表示最和最大数倹{?br>
在在pȝ上下文对象中有两个元素记录节点信息array Node;和array NodeClass;Qarray是数l数据类型,Node是节Ҏ(gu)据对象的数组QNodeClass节点对象的数l,按照pȝ逻辑关系l织?br> 创徏节点时传入nodedef对象到节点创建函敎ͼ函数?x)根据nodedef信息生成对应nodeclass对象d到NodeClass数组Q同时根据nodedef信息分配数据对象的内存空间。在该节点的Create函数里面再初始化该节点的数据对象?/p>
player* myplayer = NULL;
if(p) myplayer = (player*)(p->Player);
控制播放参数使用Set(void* This,int No,const void* Data,int Size);函数Q第一个参数是播放模块指针Q第二个参数是控制代码,卌q行什么操作,W三个参数是需要赋值给控制代码的数|最后一个参数是所赋数值的占用内存的大?br>例如开始播攄代码是:(x)
myplayer->Set(myplayer,PLAYER_PLAY,1,sizeof(int));
PLAYER_PLAY为控制代码,表示当前控制的是播放暂停功能Q数gؓ(f)1表示播放?表示暂停?br>得到某一控制属性用Get(void* This,int No,void* Data,int Size);函数Q参数含义和Set函数相同?br>控制代码是一l宏Q定义在player.h文g中。比较重要的控制参数?br>// play or pause (bool_t)
#define PLAYER_PLAY 0x32
// position in fraction (fraction)
#define PLAYER_PERCENT 0x25
// position in time (tick_t)
#define PLAYER_POSITION 0x28
// current format (format*)
#define PLAYER_FORMAT 0x2B
// current file in playlist (int)
#define PLAYER_LIST_CURRENT 0x2F
// current file index (suffled) in playlist (int)
#define PLAYER_LIST_CURRIDX 0xA2
// fullscreen mode (bool_t)
#define PLAYER_FULLSCREEN 0x3E
// stop
#define PLAYER_STOP 0xB2
// skin viewport rectangle (rect)
#define PLAYER_SKIN_VIEWPORT 0x3C
播放控制模块所有可用参数见static const datatable PlayerParams[]l构?/p>
W一个参Cؓ(f)播放模块指针Q第二个参数是添加到播放模块文g队列的序P如果是文g成ؓ(f)W一个文件该参数设ؓ(f)0Q第三个参数是媒体文件的目录和名UͼW四个参Cؓ(f)媒体文g标题Q该参数可以忽略?/p>
MP3_0001=audio/mpeg
MP3_0002=mp1:A;mp2:A;mp3:A;mpa:A
MP3_0200=acodec/0x0055
U录?jin)MP3文g分离器对应的文gcd、扩展名和文件特征码?br>要得到标准字W串使用函数LangStrDefQ第一个参数表C字W类别,W二个参数表C字WID。界面相关的是特D字W集的字W串Q用函数LangStrQ第一个参数表C字W类别,W二个参数表C字WID。关于字W串资源文gl构含义在以后的文档中说明?a >http://blog.csdn.net/navi_dx/archive/2007/11/14/1885780.aspx
]]>
在脓(chung)代码前,我先做个声明Q代码在windowsq_下运行。做UI的朋友经常用到DrawText()q个APIQ它里面有个参数是关于文字对齐方式的. 我们写控件输Z息时Q都考虑增加q么cd的变量来控制文字信息输出Q最典型的就是按钮。一般情况下Q我们无需(g)该变量Q直接引用就好。但有时候ؓ(f)?jin)让界面更h性化Q我们需要检该变量Qƈ做些调整。好Q现在看q段代码Q?br> 它根据对齐属性是否包?DT_LEFTQ而做一些工?br>void OnPaint()
{
。。。。。。?br> 。。。。。。?br>
if( m_uAlign & DT_LEFT ) //代码 D?1
DoA();
else
DoB();
if( m_uAlign & DT_LEFT == DT_LEFT ) //代码 D?nbsp;2
DoA();
else
DoB();
if( (m_uAlign & DT_LEFT ) == DT_LEFT ) //代码 D?nbsp;3
DoA();
else
DoB();
。。。。。。?br>}
上面三个代码D늚执行q程为:(x)1Q永q执?DoB() 函数体;
2Q如?m_uAlign 最末位是零 Q则执行 DoB()函数? 否则则执?DoA()函数体;
MFC | WTL | |
Stand-alone library | Yes | No (built on ATL) |
Yes | Yes | |
Yes | No | |
Yes | No (Supported by volunteers inside MS) | |
Yes | No | |
Yes | Yes | |
Yes | No | |
Yes | Yes | |
No | Yes | |
No (MFC does provide dialog bars) | Yes | |
Yes | Yes | |
Yes | Yes | |
Yes | Yes | |
Yes | Yes | |
Yes | Yes | |
No | Yes | |
Yes | Yes | |
Yes | No | |
Yes | Yes | |
Yes | Yes (not as extensive as MFC) | |
Yes | Yes | |
Yes | Yes | |
No | Yes | |
Yes | Yes | |
Yes | Yes | |
Yes | Yes | |
No | No | |
Yes | Yes | |
No | Yes | |
228KB + MSVCRT.DLL (288KB) | 24k (with /OPT:NOWIN98) (+ MSVCRT.DLL if you use CString) | |
24KB + MFC42.DLL (972KB) + MSVCRT.DLL (288KB) | N/A | |
CRT (+ MFC42.DLL, if dynamically linked) | None (CRT if you use CString) |