﻿<?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++博客-南山狒狒-文章分类-WinCE</title><link>http://www.cppblog.com/Jrong/category/9941.html</link><description>我梦江南好</description><language>zh-cn</language><lastBuildDate>Mon, 23 Mar 2009 02:37:23 GMT</lastBuildDate><pubDate>Mon, 23 Mar 2009 02:37:23 GMT</pubDate><ttl>60</ttl><item><title>[转]Windows CE 电源管理</title><link>http://www.cppblog.com/Jrong/articles/77541.html</link><dc:creator>iJrong</dc:creator><author>iJrong</author><pubDate>Mon, 23 Mar 2009 01:17:00 GMT</pubDate><guid>http://www.cppblog.com/Jrong/articles/77541.html</guid><wfw:comment>http://www.cppblog.com/Jrong/comments/77541.html</wfw:comment><comments>http://www.cppblog.com/Jrong/articles/77541.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Jrong/comments/commentRss/77541.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Jrong/services/trackbacks/77541.html</trackback:ping><description><![CDATA[<p><strong><font size=6>电源管理</font></strong><br>&nbsp;&nbsp;&nbsp; Windows CE是典型的使用电池供电的系统。这使得正确操作系统十分关键，应用程序大多数时间都不需要关注Windows CE 设备的电源损耗，但是在某些时候，你可能要注意这些损耗。<br>&nbsp;&nbsp;&nbsp; 当用户关闭了一个使用电池的Windows CE 设备，电源系统不会关闭PC电源，事实上，只是系统被挂起（译者注：这里就像有些PocketPC把关闭电源放在拔SIM卡的位置，拔出SIM卡才真正关闭电源。但是，目前包括Smartphone在内，因为硬件设备，比如CPU无法进入低功耗，所以为了省电，需要做到关闭应用处理器及大部分设备供电，然后需要唤醒时，再通过定时器或无线模块唤醒。所以不关闭电源的情况不是绝对的。）当用户打开设备电源，设备不会像PC一样重新启动，而是被唤醒，返回到与系统挂起前一样的状态。这样导致一个应用程序在唤醒后会像挂起前一样运行。事实上，应用程序根本不知道它被挂起，除非它明确地请求当系统挂起时通知它。从应用程序的角度看，电源管理有三种方式，查询电源状态，改变电源状态，和防止电源状态改变。<br><strong><font size=5>查询电源状态</font></strong><br>&nbsp;&nbsp;&nbsp; 要查询系统当前的电源状态，你必须调用<br>&nbsp;<br>DWORD GetSystemPowerStatusEx2 (PSYSTEM_POWER_STATUS_EX2 pSystemPowerStatusEx2, DWORD dwLen, BOOL fUpdate);<br>&nbsp;<br>函数带了三个参数：一个指向SYSTEM_POWER_ STATUS_EX2结构的指针，结构的长度，和一个布尔值，表示告诉操作系统是否应该查询电池驱动来得到最后的信息或者直接返回电池缓存中的信息。系统大约每5秒查询一次电池状态，因此，如果第三个差数是FALSE，得到的数据不会太旧。结构SYSTEM_POWER_STATUS_EX2被定义为<br>typedef struct _SYSTEM_POWER_STATUS_EX2 {<br>&nbsp;&nbsp;&nbsp; BYTE ACLineStatus;<br>&nbsp;&nbsp;&nbsp; BYTE BatteryFlag;<br>&nbsp;&nbsp;&nbsp; BYTE BatteryLifePercent;<br>&nbsp;&nbsp;&nbsp; BYTE Reserved1;<br>&nbsp;&nbsp;&nbsp; DWORD BatteryLifeTime;<br>&nbsp;&nbsp;&nbsp; DWORD BatteryFullLifeTime;<br>&nbsp;&nbsp;&nbsp; BYTE Reserved2;<br>&nbsp;&nbsp;&nbsp; BYTE BackupBatteryFlag;<br>&nbsp;&nbsp;&nbsp; BYTE BackupBatteryLifePercent;<br>&nbsp;&nbsp;&nbsp; BYTE Reserved3;<br>&nbsp;&nbsp;&nbsp; DWORD BackupBatteryLifeTime;<br>&nbsp;&nbsp;&nbsp; DWORD BackupBatteryFullLifeTime;<br>&nbsp;&nbsp;&nbsp; WORD BatteryVoltage;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; DWORD BatteryCurrent;<br>&nbsp;&nbsp;&nbsp; DWORD BatteryAverageCurrent;<br>&nbsp;&nbsp;&nbsp; DWORD BatteryAverageInterval;<br>&nbsp;&nbsp;&nbsp; DWORD BatterymAHourConsumed;<br>&nbsp;&nbsp;&nbsp; DWORD BatteryTemperature;<br>&nbsp;&nbsp;&nbsp; DWORD BackupBatteryVoltage;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp; BatteryChemistry;<br>} SYSTEM_POWER_STATUS_EX2;<br>&nbsp;&nbsp;&nbsp; 在我描述的这个巨大的结构之前，我必须告诫你，这个结构返回的数据精确程度和电池驱动一样。同样的结构被传给电池驱动来查询它的状态。Windows CE不验证电池驱动返回的数据。这个函数返回来的数据依赖于电池驱动，因此不同的系统有不同的变化。举个例子，许多系统在使用AC电源时不报告精确的电源级数；另一些系统则相反。应用程序使用GetSystemPowerStatusEx2来自动预防和检测系统是否可能运行应用程序。<br>&nbsp;&nbsp;&nbsp; 第一个区域，ACLineStatus，包含一个标志，表示系统是否连接到AC 电源。如果值是AC_LINE_OFFLINE，表示系统没有使用AC 电源；AC_LINE_ONLINE，表示系统使用了AC 电源；AC_LINE_BACKUP_POWER和AC_LINE_UNKNOWN，表示备用电源和未知电源。BatteryFlag区域，提供了一个总的标识，表示当前系统的电池状态，可以有以下值：<br>BATTERY_FLAG_HIGH<br>电池被充满或接近充满。<br>BATTERY_FLAG_LOW<br>电池还有一点剩余。<br>BATTERY_FLAG_CRITICAL<br>电池电量处在一个临界状态。<br>BATTERY_FLAG_CHARGING<br>电池当前正在充电。<br>BATTERY_FLAG_NO_BATTERY<br>系统无电池<br>BATTERY_FLAG_UNKNOWN<br>电池状态未知<br>BatteryLifePercent区域包含估计的电池电量能够维持的百分比。数值可能是0到100之间的一个，或用255表示百分比未知。BatteryLifeTime区域表示电池耗尽之前可以维持的秒数。如果该值不能估计，区域填入BATTERY_LIFE_UNKNOWN。BatteryFullLifeTime区域包含完全充满电池需要的时间。如果该值不能估计，填入BATTERY_LIFE_UNKNOWN。注意，在许多系统中，这些值可能难以测量。大多数OEM 厂商简单地在每个区域内填入BATTERY_LIFE_UNKNOWN。<br>接下来的第四个区域（不计算保留区域）重复了前面的表述，只不过是对系统备份电池来说。因为这些值大多数难以测量，许多系统简单地返回&#8220;unknown&#8221;给这些区域。<br>剩下的区域描述了电池和备用电池的电力状态，因为许多系统缺少测量这些值的能力，这些区域也被简单地默认为&#8220;unknown&#8221;。最后一个区域，BatteryChemistry，包含一个标志，表示系统中电池的类型。当前已定义的值包括<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_ALKALINE<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_NICD<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_NIMH<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_LION<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_LIPOLY<br>&#183;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BATTERY_CHEMISTRY_UNKNOWN<br><span><strong><font size=5>改变电源状态<br>&nbsp;&nbsp; </font></strong></span>应用程序能通过一系列的方式改变系统的电源状态。在基于Windows CE.NET系统的较新系统中，首选的方式是使用电源管理程序，在之后的章节将会讨论。可是无论如何，还有大量的基于早期Windows CE版本的系统以及Windows CE.NET不包含电源管理程序版本。对这些系统来说，下面的技术会很方便。<br><strong><font size=5>关闭电源</font></strong><br>&nbsp;&nbsp;&nbsp; 应用程序可以通过调用一个少有资料的GwesPowerOffSystem函数挂起系统。这个函数可以在大多数版本Windows CE中使用，但是最近才被公开。事实上，大多数SDK没有包含这个函数的原型，你可能要提供原型。这个函数定义为<br>&nbsp;<br>void GwesPowerOffSystem(void);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; GwesPowerOffSystem的使用很简单：简单调用，系统就会挂起。<br>&nbsp;&nbsp;&nbsp; 如果你想避免使用很少资料的函数，你可以通过简单地模拟用户按关闭按钮来关闭系统。你可以通过使用keybd_event函数很容易地允许你的应用程序挂起系统，如下：<br>keybd_event (VK_OFF, 0, KEYEVENTF_SILENT, 0);&nbsp;&nbsp;&nbsp; <br>keybd_event (VK_OFF, 0, KEYEVENTF_SILENT │ KEYEVENTF_KEYUP, 0);<br>这两个keybd_event调用模拟了按和释放电源按钮，电源按钮的虚拟键值是VK_OFF。执行前面的两行代码将挂起系统。因为虚拟键代码在执行时会由GWES表现，两个函数可能在系统挂起前有一些状态的表现（译者注：屏幕上会有关闭对话框之类的图像，和真实按下按钮的画面一样）。如果你的程序无法在keybd_event函数之前停止工作，添加一个Sleep调用来使应用程序暂停一些毫秒来让GWES真实地挂起系统。<br><span><font size=5><strong>关闭屏幕</strong></font></span><br>&nbsp;&nbsp;&nbsp; 如果系统有有色背光显示，主要的电源消耗不是CPU而是背光。在一些环境下，一个应用程序需要运行却不需要显示在屏幕上。一个例子是音乐播放器应用程序，当用户听音乐的时候，不关注屏幕。在这些情形下，有能力关闭背光将意味着提高电池寿命。<br>&nbsp;&nbsp;&nbsp; 当然，当用户想看屏幕时，任何关闭背光应用程序的需要一个简单的用户友好的方式来重新打开屏幕。同样，记得用户典型的想法是屏幕变黑时会认为被关闭了，因此要考虑这点。举个例子，一个用户可能在系统已经运行时试图打开系统电源，并且这样做了，却很意外地发现，设备电源被关闭了。同样，当系统在这种情况下关闭显示，它同时也关闭了触摸屏。这意味着你不能告诉用户敲击屏幕来打开。而是，你需要使用一些其他的事件，比如设置时间，任务完成，或用户按了一个按钮。最后，这里讨论的方式对大多数基于Windows CE 3.0或更新的版本比较有用，并且被Windows CE .NET 4.0中的电源管理程序所替代。对于较新的系统，先看看是否电源管理程序可用，然后通过它来控制屏幕。如果失败了，ExtEscape方式也许能行。<br>&nbsp;&nbsp;&nbsp; 在Windows CE中，显示的控制是通过Ext&shy;Escape函数。这是一个显示和打印机驱动的后门。Windows CE显示驱动支持许多设备转义代码（escape codes），这些被公布在Platform Builder中。对于我们的目的来说，只有两个转义代码被用到：SETPOWERMANAGEMENT来设置显示的电源状态和QUERYESCSUPPORT来查询是否SETPOWERMANAGEMENT被驱动支持。下面的例子打开或关闭系统显示通过显示驱动，并且支持完全的转义代码：<br>//&nbsp;Defines&nbsp;and&nbsp;structures&nbsp;taken&nbsp;from&nbsp;pwingdi.h&nbsp;in&nbsp;the&nbsp;Platform&nbsp;Builder<br>#define&nbsp;QUERYESCSUPPORT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8<br><span style="FONT-SIZE: 10.5pt">#define&nbsp;SETPOWERMANAGEMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6147<br></span><span style="FONT-SIZE: 10.5pt">#define&nbsp;GETPOWERMANAGEMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6148<br><br></span><span style="FONT-SIZE: 10.5pt">typedef&nbsp;enum&nbsp;_VIDEO_POWER_STATE&nbsp;{<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;VideoPowerOn&nbsp;=&nbsp;1,<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;VideoPowerStandBy,<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;VideoPowerSuspend,<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;VideoPowerOff<br></span><span style="FONT-SIZE: 10.5pt">}&nbsp;VIDEO_POWER_STATE,&nbsp;*PVIDEO_POWER_STATE;</span>&nbsp;<br><br><span style="FONT-SIZE: 10.5pt">typedef&nbsp;struct&nbsp;_VIDEO_POWER_MANAGEMENT&nbsp;{<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;ULONG&nbsp;Length;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;ULONG&nbsp;DPMSVersion;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;ULONG&nbsp;PowerState;<br></span><span style="FONT-SIZE: 10.5pt">}&nbsp;VIDEO_POWER_MANAGEMENT,&nbsp;*PVIDEO_POWER_MANAGEMENT;<br><br></span><span style="FONT-SIZE: 10.5pt">//----------------------------------------------------------------------<br></span><span style="FONT-SIZE: 10.5pt">//&nbsp;SetVideoPower&nbsp;-&nbsp;Turns&nbsp;on&nbsp;or&nbsp;off&nbsp;the&nbsp;display<br></span><span style="FONT-SIZE: 10.5pt">//<br></span><span style="FONT-SIZE: 10.5pt">int&nbsp;SetVideoPower&nbsp;(BOOL&nbsp;fOn)&nbsp;{<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;VIDEO_POWER_MANAGEMENT&nbsp;vpm;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;rc,&nbsp;fQueryEsc;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;HDC&nbsp;hdc;<br><br>&nbsp;</span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;the&nbsp;display&nbsp;dc.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;hdc&nbsp;=&nbsp;GetDC&nbsp;(NULL);<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;See&nbsp;if&nbsp;supported.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;fQueryEsc&nbsp;=&nbsp;SETPOWERMANAGEMENT;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;rc&nbsp;=&nbsp;ExtEscape&nbsp;(hdc,&nbsp;QUERYESCSUPPORT,&nbsp;sizeof&nbsp;(fQueryEsc),&nbsp;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(LPSTR)&amp;fQueryEsc,&nbsp;0,&nbsp;0);&nbsp;&nbsp;&nbsp;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(rc&nbsp;==&nbsp;0)&nbsp;{<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;No&nbsp;support,&nbsp;fail.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReleaseDC&nbsp;(NULL,&nbsp;hdc);<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Fill&nbsp;in&nbsp;the&nbsp;power&nbsp;management&nbsp;structure.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;vpm.Length&nbsp;=&nbsp;sizeof&nbsp;(vpm);<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;vpm.DPMSVersion&nbsp;=&nbsp;1;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(fOn)&nbsp;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vpm.PowerState&nbsp;=&nbsp;VideoPowerOn;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;else<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vpm.PowerState&nbsp;=&nbsp;VideoPowerOff;<br><br></span>&nbsp;<span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Tell&nbsp;the&nbsp;driver&nbsp;to&nbsp;turn&nbsp;on&nbsp;or&nbsp;off&nbsp;the&nbsp;display.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;rc&nbsp;=&nbsp;ExtEscape&nbsp;(hdc,&nbsp;SETPOWERMANAGEMENT,&nbsp;sizeof&nbsp;(vpm),&nbsp;<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(LPSTR)&amp;vpm,&nbsp;0,&nbsp;0);&nbsp;&nbsp;&nbsp;<br></span>&nbsp;<br><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Always&nbsp;release&nbsp;what&nbsp;you&nbsp;get.<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;ReleaseDC&nbsp;(NULL,&nbsp;hdc);<br></span><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br></span><span style="FONT-SIZE: 10.5pt">}</span><br><br>&nbsp;&nbsp;&nbsp; 前面的代码通过调用ExtEscape和QUERYESCSUPPORT命令来查询是否支持转移代码。被查询的命令首先交给输入缓冲，如果SETPOWERMANAGEMENT命令被支持，程序就填充VIDEO_POWER_MANAGEMENT结构并再次调用ExtEscape设置电源状态。<br>&nbsp;&nbsp;&nbsp; 虽然这些转义代码允许应用程序打开或关闭显示，Windows CE没有一个统一的方式来控制背光的亮度。每个系统都有它自己的OEM特有方式来控制背光亮度。如果将来有一种标准的背光亮度控制方式，它将很可能放在ExtEscape函数中。<br><span><font size=5><strong>打开系统电源<br></strong></font></span>&nbsp;&nbsp;&nbsp; 当系统被挂起，应用程序将不再运行，因此当系统唤醒时，应用程序看起来没有被控制。然而，有一些方式来唤醒一个挂起的设备。首先，一个应用程序通过给定一个时间，并使用11章提到的消息API（Notification API）做系统被唤醒的计划。在一般情况下，OEM厂商会分配一些中断条件，以便管理系统电源打开，或唤醒。这种方式的一个例子是一个系统当防止了一个同步架（synchronization cradle）时被唤醒。<br><strong><font size=5>防止系统关闭电源</font></strong><br>&nbsp;&nbsp;&nbsp; 相反的情况，防止系统挂起也是一个问题。Windows CE系统通常被设置为当一段时间没有用户输入就自动挂起。要防止自动挂起，一个应用程序可以周期性地调用一下函数：<br>void WINAPI SystemIdleTimerReset (void);<br>这个函数重设Windows CE用来监视用户输入的定时器。如果定时器到达预先的没有用户输入的间隔，系统会自动挂起。因为挂起超时值可以被改变，一个应用程序需要知道超时值，这样就要多一点调用SystemIdleTimerReset。系统维护三个超时值，这些都能够使用SystemParametersInfo来查询。传递给SystemParametersInfo的常量的不同表现，显示如下：<br>SPI_GETBATTERYIDLETIMEOUT<br>当系统运行在电池电源状态下，离用户最后输入的时间<br>SPI_GETEXTERNALIDLETIMEOUT<br>当系统运行在AC电源状态下，离用户最后输入的时间<br>SPI_GETWAKEUPIDLETIMEOUT<br>在系统再次挂起时离系统被自动唤醒的时间<br>要防止电源被自动挂起，你需要查询这三个值，并在最短时间内返回之前调用SystemIdleTimerReset。如果超时值被设置为0，表示超时值被禁止。<br><span><strong><font size=5>电源管理程序<br></font></strong></span>&nbsp;&nbsp;&nbsp; 一个新的，独立的电源管理组件在Windows CE .NET 4.0中被引入了。这个电源管理程序替代了许多GWES以前完成的函数。电源管理程序定义了一系列的电源状态，如D0，D1，D2，和D3。这些看起来神秘的名字被对应于一些友好的系统级别名称。<br>&nbsp;&nbsp;&nbsp; 对嵌入式系统来说，OEM厂商定义了系统的电源状态。例如，电源状态可能是打开（On），空闲（Idle）和挂起（Suspend）。其他电源状态也被定义了，像ScreenOff, InCradle, 和 OnBattery。<br>&nbsp;&nbsp;&nbsp; 从应用程序的观点看，新的电源管理程序提供了通知电源状态改变的能力以及通过一系列的函数统一改变电源状态的能力。<br>&nbsp;&nbsp;&nbsp; 系统的电源状态被定义在注册表中，SDK定义了PWRMGR_REG_KEY，以致你不得不知道注册表的字符串，但是当常量没定义的时间，电源管理程序注册数据被保留在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power。电源状态被定义作为子键，位于Key State。<br><span><font size=5><strong>电源通知<br></strong></font></span>&nbsp;&nbsp;&nbsp; 电源管理程序一个十分受欢迎的特点是，可以在系统电源状态改变时通知应用程序。这可以让应用程序从手动检测电源状态中解脱出来。一个应用程序可以通过调用RequestPowerNotifications请求电源管理程序当电源状态改变的时候发送一个通知给应用程序。电源管理程序会通过一个由应用程序前面建立的消息队列发送通知。<br>&nbsp;&nbsp;&nbsp; RequestPowerNotifications原型如下。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; HANDLE RequestPowerNotifications (HANDLE hMsgQ, DWORD Flags);<br>&nbsp;<br>第一个参数是一个应用程序在之前建立的消息队列的句柄。第二个参数是一系列参数，表示应用程序想接收的通知。<br>&nbsp;<br>PBT_TRANSITION<br>接受系统电源状态改变的通知。例如，当系统从On到Suspend。<br>PBT_RESUME<br>当系统resume的时候接收通知。<br>PBT_POWERSTATUSCHANGE <br>当系统在AC和电池之间切换的时候接收通知。<br>PBT_POWERINFOCHANGE<br>当系统电池级数变化时接收通知。<br>POWER_NOTIFY_ALL<br>接收所有的通知。<br>&nbsp;<br>RequestPowerNotifications函数返回一个电源通知的句柄，失败返回NULL。消息队列建立的时候必须使应用程序有读权限，因为应用程序将从消息队列中读取电源通知。<br>要接收通知，应用程序必须使用WaitForSingleObject来阻塞消息句柄。像第10章所讨论的，当通知被放在队列中时，句柄将被signaled。实际的通知将由结构POWER_BROADCAST表中被接收到。<br>typedef struct _POWER_BROADCAST {<br>&nbsp;&nbsp;&nbsp; DWORD Message;<br>&nbsp;&nbsp;&nbsp; DWORD Flags;<br>&nbsp;&nbsp;&nbsp; DWORD Length;<br>&nbsp;&nbsp;&nbsp; WCHAR SystemPowerState[1];<br>} POWER_BROADCAST, *PPOWER_BROADCAST;<br>&nbsp;<br>第一个要注意的是，这个结构长度是可变的。最后一个字段，SystemPowerState，是被定义为WCHARs类型，但是可以填上非字符串数据。第一个字段是通知自己的标识，这个字段可以填前面PBT_标志列表之一。Flags区可以包括以下标志，依赖于被接收的通知：<br>POWER_STATE_ON<br>系统处于on状态。<br>POWER_STATE_OFF<br>系统处于off状态。<br>POWER_STATE_CRITICAL<br>系统进入了一个临界off状态。<br>POWER_STATE_BOOT<br>系统正在启动。<br>POWER_STATE_IDLE<br>系统进入idle状态。<br>POWER_STATE_SUSPEND<br>系统被挂起。<br>POWER_STATE_RESET<br>系统被复位。<br>最后两个字段是相互关联的。Length字段是SystemPowerState字段数据的长度。SystemPowerState中包含的数据依赖于被发送的通知。对于PBT_TRANSITION通知来说，SystemPowerState字段包含一个新电源状态的标识字符串。这个字符串是以非0结尾的。为了结束字符串，使用Length字段来指出字符串的长度。注意，Length字段是以字节为单位的，当字符是双字节的Uncode字符时，需要获得字符串字符的长度，就需要用Length字段去除TCHAR的size。<br>对于PBT_POWERINFOCHANGE通知来说，SystemPowerState字段包含一个PPOWER_BROADCAST_POWER_INFO结构：<br>typedef struct _POWER_BROADCAST_POWER_INFO {<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwNumLevels;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwBatteryLifeTime;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwBatteryFullLifeTime;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwBackupBatteryLifeTime;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwBackupBatteryFullLifeTime;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bACLineStatus;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bBatteryFlag;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bBatteryLifePercent;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bBackupBatteryFlag;<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bBackupBatteryLifePercent;<br>} POWER_BROADCAST_POWER_INFO, *PPOWER_BROADCAST_POWER_INFO;<br>&nbsp;<br>注意，这里有一些字段的名字和函数十分相似于前面讨论的SYSTEM_POWER_STATUS_EX2结构。<br>&nbsp;<br><span><font size=5><strong>设置电源状态</strong></font></span><br>&nbsp;&nbsp;&nbsp; 电源管理程序提供的函数也允许应用程序来控制电源状态。有两个方式来控制电源。第一个方式是应用程序给定一个电源设定。第二个方式是应用程序请求电源状态不要低于给定的级别。<br>&nbsp;&nbsp;&nbsp; 一个应用程序通过调用函数SetSystemPowerState可以请求特定的电源状态。这个函数原型如下。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD SetSystemPowerState (LPCWSTR psState, DWORD StateFlags,&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; DWORD Options);<br>&nbsp;<br>电源状态可以被请求通过指定前两个参数。如果第一个参数是非零值，它指向一个字符串标识被请求的状态。这个字符串必须和注册表中列出的电源状态之一相匹配。<br>如果psState 为 NULL，第二个参数StateFlags，定义了请求的电源状态。这个参数是从POWER_STATE_ON直到POWER_STATE_RESET状态其中之一，这些在前面提到的POWER_BROADCAST结构有描述。<br>比较特别的是POWER_STATE_RESET标志。这个标志请求系统重起，使用SetSystemPowerState的方法重起比通过直接使用IOCTL_HAL_REBOOT命令来调用KernelIoControl的方法更好。调用 SetSystemPowerState 会让系统在重起设备之前任何还在缓冲中的数据保存到文件系统。<br>调用SetSystemPowerState是一个直接改变电源状态的方法。更巧妙的方法是通过调用SetPowerRequirement来请求系统维持应用程序所需最低限度的电源状态。SetSystemPowerState是假定应用程序知道所需状态，而调用SetPowerRequirement是允许系统对电源设定做优化以满足应用程序的需要。一个使用SetPowerRequirement会比较方便的例子是，一个使用串口的应用程序需要串口在进行通信时保持住电源状态。SetPowerRequirement被定义如下。<br>&nbsp;<br>HANDLE SetPowerRequirement (PVOID pvDevice,<br>&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; CEDEVICE_POWER_STATE DeviceState,<br>&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; ULONG DeviceFlags, PVOID pvSystemState,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ULONG StateFlags);<br>&nbsp;<br>第一个参数指定了应用程序需要维护电源状态的设备。DeviceState参数定义了设备的电源状态。CEDEVICE_POWER_STATE指定了状态范围是从D0（意味着设备是处于最大功耗状态）到D4（表示设备被关闭）（译者注：其实D0到D4的状态的具体表现，完全是由OEM厂商可自定义的，对应用程序开发者来说，比如是在D1关LCD背光还是在D2，都是不确定的，微软只给出标准定义，而不是实际定义）。DeviceFlags参数由两个标志合并而成：POWER_NAME，表示设备名有效；POWER_FORCE，表示设备应当维持当前状态甚至当系统挂起时。如果pvSystemState不为NULL，它表示只有对于在pvSystemState中已命名的电源请求才是有效的。设备可能无法更改请求的状态。<br>应用程序应当注销通过调用ReleasePowerRequirement来注销请求，原型如下。<br>&nbsp;<br>DWORD ReleasePowerRequirement (HANDLE hPowerReq);</p>
<p>这里唯一的参数是从SetPowerRequirement里返回的句柄。<br>在下一章，我将就Windows CE流设备驱动和服务，继续探讨有关系统的问题。尽管大多数应用程序开发者可能不需要写一些设备驱动或服务，但是知道它们是如何和程序一起工作对我们也是有启发的。让我们一起来看一看吧。</p>
<img src ="http://www.cppblog.com/Jrong/aggbug/77541.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Jrong/" target="_blank">iJrong</a> 2009-03-23 09:17 <a href="http://www.cppblog.com/Jrong/articles/77541.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Windows CE 内存管理</title><link>http://www.cppblog.com/Jrong/articles/77540.html</link><dc:creator>iJrong</dc:creator><author>iJrong</author><pubDate>Mon, 23 Mar 2009 01:16:00 GMT</pubDate><guid>http://www.cppblog.com/Jrong/articles/77540.html</guid><wfw:comment>http://www.cppblog.com/Jrong/comments/77540.html</wfw:comment><comments>http://www.cppblog.com/Jrong/articles/77540.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Jrong/comments/commentRss/77540.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Jrong/services/trackbacks/77540.html</trackback:ping><description><![CDATA[<p><strong><font size=6>内存管理</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你在写Windows CE 程序中遇到的最重要的问题，那一定是内存问题。一个WinCE 系统可能只有4MB 的RAM，这相对于个人电脑来说是十分少的，因为个人电脑的标准配置已经到了128MB 甚至更多。事实上，运行WinCE 的机器的内存十分缺乏，以至于有时候有必要在写程序的时候为节约内存而牺牲程序的整体性能。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 幸运的是，尽管WinCE系统的内存很小，但可用来管理内存的函数却十分完善。WinCE实现了Microsoft Windows XP和Microsoft Windows Me中可用到的几乎全部的Win32内存管理API。WinCE支持虚拟内存（virtual memory）分配，本地（local）和分离（separate）的堆（heaps），甚至还有（memory-mapped files）内存映射文件。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 像Windows XP一样，WinCE支持一个带有应用程序间内存保护功能的32位平面地址空间，但是WinCE是被设计来应用于不同场合，所以它底层的内存结构不同于Windows XP。这些不同能够影响到你如何设计一个WinCE 应用程序。在这一章中，我将讲述最基础的WinCE内存结构。我也将讲述包括WinCE中可用的内存分配方式中的不同点以及如何使用这些不同的内存类型来最小化你的程序的内存占有率。<br><strong><font size=5>内存基础</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对所有的电脑来说，系统地运行一个WinCE，需要ROM（只读存储器）和RAM（随机存储器）。但不论如何，在WinCE系统中，ROM和RAM的使用还是稍微有些不同于个人电脑环境。<br><strong><font size=5>关于RAM</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RAM在WinCE 系统中被分为两个区域：第一个是程序的存储区（program memory），也叫做系统堆（system heap）。第二个是对象存储区（object store）。这个对象存储区有点像一个永久的虚拟RAM磁盘。不同于PC上的旧式的虚拟RAM磁盘，对象存储区保留存储的文件甚至当系统被关闭以后。（脚注）这种安排的原因是WinCE 系统，例如Pocket PC代表性地具有一个主电池和一个备用电池。当用户更换主电池的时候，备用电池的工作是提供电源给RAM以便维持文件在对象存储区的存储。当用户按了重启键之后，WinCE核心就开始寻找在关闭系统前建立的对象存储区，如果找到的话就将继续使用它。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RAM中的另一个区域则用作程序存储区。程序存储区有点像个人电脑中的RAM，它为正在运行的应用程序保存堆和栈的内容。在对象存储区和程序存储区之间的分界线是可以通过移动它来改变的，用户可以在控制面板中找到改变这条分界线的设置。在可用内存降低的（low-memory）条件下，系统将会弹出对话框询问用户是否要将对象存储区RAM划分一些给程序存储区RAM以满足要运行的应用程序的需求。<br><strong><font size=5>关于ROM<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在个人电脑中，ROM是用来存储BIOS（基本输入输出系统）并且只有64－128KB。在WinCE系统中，ROM大小可以从4MB到32MB并且存放整个操作系统以及和系统捆绑在一起的应用程序。在这种情况下，ROM在WinCE系统中就好像一个只读的硬盘。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在一个WinCE系统中，存储在ROM之上的程序能够以现场执行（Execute in Place，XIP）的方式运行。换句话说，程序可以直接从ROM中执行而不必先加载到RAM中再执行。这种能力对小型系统来说，使之在两个方面具有巨大的优势。代码直接从ROM中执行意味着程序代码不会占据更有价值的RAM。同样，程序在执行前也不必先复制到RAM中，这样就只需要很少的时间来启动一个应用程序。不在ROM中，但是被包含在对象存储区（译者注：上文将对象存储区比作永久的RAM磁盘，故此处要说明，只有Intel力推的nor flash memroy类型才能以XIP方式执行，ROM其实也是一种nor flash memory类型）或闪存卡（Flash memory storage card）中的程序将不能以现场方式执行，它们将被复制到RAM中再执行。<br><strong><font size=5>关于虚拟内存<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WinCE 实现了系统的虚拟内存管理，在一个虚拟内存系统中，应用程序主要处理这个分离（译者注：物理上可能分离，但系统将它们联系起来），虚拟的地址空间，因此并不涉及到由硬件管理的物理内存。操作系统使用微处理器的内存管理单元来处理虚拟地址和物理地址间的实时转换。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种虚拟内存方法的优势能从MS-DOS系统复杂的地址空间看出来。一旦请求的RAM超过最初PC设计的640-KB限制，程序设计者将不得不作出像扩展内存一样的计划以便增加可用内存的数量。OS/2 1.x（译者注：IBM研制的操作系统）和Windows 3.0采用了一种基于段（segment-based）的虚拟内存系统来解决问题。应用程序使用虚拟内存不需要知道实际物理内存的位置，只要有内存可用就行。在这些系统中，虚拟内存以一种段的方式被实现了，即可移动的内存块（译者注：段其实就是内存分块）大小从16字节到64KB。64-KB的限制并不是由于段本身原因，而是由于Intel 80286的特性所致，这就是Windows3.x和OS/21.x的分段式虚拟内存系统结构。<br><strong><font size=5>分页存储<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span></span></font></strong>Intel 80386支持的段大小已经超过64KB，但是Microsoft和IBM开始设计OS/2 2.0，他们选择了一种不同的虚拟内存系统，随后也被386所支持，这就是分页式虚拟内存系统。在一个分页存储的系统中，最小的可被微处理器管理的单元是页（page）。对于Windows NT和OS/2 2.0系统来说，页大小都被设置为386处理器默认的4096字节。当一个应用程序存取一个页的时候，微处理器将转换该页的虚拟内存地址到实际的ROM或RAM中的物理页（译者注：这就是实现了地址映射和转换，将虚拟的和实际的存储单元一一对应），这一页同时被标记以便其他程序对该页的访问将被排斥。操作系统决定虚拟内存页是否有效，如果有效，将做一个物理内存页到虚拟页的映射。<br>WinCE实现了一个和其他Win32操作系统类似的分页式虚拟内存系统。在WinCE中，一页的大小可以从1024字节到4096字节，基于微处理器的不同而不同。这和Windows XP不同，Windows XP页面尺寸是Intel微处理器所支持的4096字节。对WinCE所支持的CPU类型来说，有486，Intel的Strong-ARM，和Hitachi SH4 都是是用了4096-byte 的页面。NEC 4100在Windows CE 3.0中使用了4-KB的页面尺寸但是在较早期的开放式系统版本中使用了1-KB的页面大小。<br>虚拟内存页可以处在三种状态：自由（free），保留（reserved），或被提交（committed），）。自由页就像它的名称一样，自由并且可被分配。保留页是虚拟地址已经被保留，并且不能被操作系统或进程中的其他线程重新分配。保留页不能用在别处，但是它同样不能被当前程序使用，因为它没有被映射到物理内存。要想执行映射，它必须被提交，一个提交页能被应用程序保留，并且直接映射到物理地址。<br>所有我刚才讲述的内容对有经验的Win32 程序员们来说是些陈旧的知识。对Windows CE 程序员来说最重要的东西是学习Windows CE 是如何改变这些因素的。当Windows CE 实现了大部分和它的老大哥Win32一样的内存API集的时候，Windows CE下面的基础结构将影响到上面的程序。在分开来看Window CE 应用程序的内存结构之前，让我们先来看看一些提供系统内存全局状态的函数。<br><strong><font size=5>查询系统的内存</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果一个应用程序知道系统当前的内存状态，它将可以较好地管理可用到的资源。WinCE实现了Win32的GetSystemInfo和GlobalMemoryStatus函数，GetSystemInfo函数原型如下：<br>VOID GetSystemInfo (LPSYSTEM_INFO lpSystemInfo);<br>它传递了一个指针给SYSTEM_INFO结构，定义如下<br>typedef struct {<br>&nbsp;&nbsp;&nbsp; WORD wProcessorArchitecture;<br>&nbsp;&nbsp;&nbsp; WORD wReserved;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwPageSize;<br>&nbsp;&nbsp;&nbsp; LPVOID lpMinimumApplicationAddress;<br>&nbsp;&nbsp;&nbsp; LPVOID lpMaximumApplicationAddress;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwActiveProcessorMask;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwNumberOfProcessors;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwProcessorType;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwAllocationGranularity;<br>&nbsp;&nbsp;&nbsp; WORD&nbsp; wProcessorLevel; <br>&nbsp;&nbsp;&nbsp; WORD&nbsp; wProcessorRevision;<br>} SYSTEM_INFO;<br>&nbsp;&nbsp;&nbsp; wProcessorArchitecture参数表示系统微处理器的架构。它的值是定义在Winnt.h中，例如PROCESSOR_ARCHITECTURE_INTEL。Windows CE扩展了这些常数，包括PROCESSOR_ARCHITECTURE_ARM，PROCESSOR_ARCHITECTURE_SHx，等等。增加的常数包括像Win32操作系统支持的网络CPU（net CPU）。跳过一些参数，我们看dwProcessorType参数，它来自于特定的微处理器类型。常数有Hitachi SHx架构中的PROCESSOR_HITACHI_SH3和PROCESSOR_HITACHI_SH4。最后两个参数，wProcessorLevel和wProcessorRevision，指明了CPU类型的特征。wProcessorLevel参数类似于dwProcessorType参数，它一个指定的微处理器系列中被定义了，dwProcessorRevision告诉你模式（model）和芯片的步进级别（stepping level）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwPageSize参数说明了微处理器页面的大小，以字节为单位。知道这个值，将会在你直接处理虚拟内存API的时候带来方便，在此我只作简短说明。lpMinimumApplication&shy;Address和lpMaximumApplicationAddress参数说明了应用程序可用到的最小和最大的虚拟内存地址。dwActiveProcessorMask和dwNumberOfProcessors参数显示被Window XP系统支持的多个处理器数量。因为Windows CE 只支持一个处理器，所以你可以忽略这个参数。dwAllocationGranularity参数说明了一个完整的虚拟内存区域分配的界限。像Windows XP，Windows CE 规定虚拟区为64-KB的界限（译者注：作者此处64-KB的意思是即使你只分配一个字节的内存，系统也将会保留64-KB的虚拟地址空间给它，这个值一般是由硬件代码实现的，但是不同硬件可能不同值）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二个方便的检测系统状态的函数如下：<br>void GlobalMemoryStatus(LPMEMORYSTATUS lpmst);<br>它返回一个MEMORYSTATUS结构，定义为<br>typedef struct { <br>&nbsp;&nbsp;&nbsp; DWORD dwLength; <br>&nbsp;&nbsp;&nbsp; DWORD dwMemoryLoad; <br>&nbsp;&nbsp;&nbsp; DWORD dwTotalPhys; <br>&nbsp;&nbsp;&nbsp; DWORD dwAvailPhys; <br>&nbsp;&nbsp;&nbsp; DWORD dwTotalPageFile; <br>&nbsp;&nbsp;&nbsp; DWORD dwAvailPageFile; <br>&nbsp;&nbsp;&nbsp; DWORD dwTotalVirtual; <br>&nbsp;&nbsp;&nbsp; DWORD dwAvailVirtual; <br>} MEMORYSTATUS;<br>&nbsp;&nbsp;&nbsp; dwLength参数在调用这个函数之前必须初始化。dwMemoryLoad参数是一个不确定的值；这是一个可用的一般性的参数指示了当前系统的内存使用情况（译者注：该参数是一个近似的百分比值，指明了物理内存的使用情况）。dwTotalPhys和dwAvailPhys参数指明了RAM有多少页被分配给了程序存储区RAM，和还有多少可用（译者注：实际上是以字节为单位）。这些值不包括分配对象存储区的RAM。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwTotalPageFile和dwAvailPageFile参数在Windows XP下和Windows Me下指明了当前页面文件（paging file）的状态。因为Windows CE不支持页面文件，所以这些参数总是0。dwTotalVirtual和dwAvailVirtual参数指明了总共的和可用的可被应用程序存取的虚拟内存页的数量（译者注：参数都是以字节为单位，而不是指页数，dwAvailVirtual是指未保留和未提交的内存）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过GlobalMemoryStatus返回的信息可以验证Windows CE内存结构，通过在有32MBRAM的HP iPaq Pocket PC上调用函数，返回值如下：<br>dwMemoryLoad&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (24)<br>dwTotalPhys&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x011ac000&nbsp;&nbsp;&nbsp; (18,530,304)<br>dwAvailPhys&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00B66000&nbsp;&nbsp;&nbsp; (11,952,128)<br>dwTotalPageFile&nbsp;&nbsp;&nbsp; 0<br>dwAvailPageFile&nbsp;&nbsp;&nbsp; 0<br>dwTotalVirtual&nbsp;&nbsp;&nbsp;&nbsp; 0x02000000&nbsp;&nbsp;&nbsp; (33,554,432)<br>dwAvailVirtual&nbsp;&nbsp;&nbsp;&nbsp; 0x01e10000&nbsp;&nbsp;&nbsp; (31,522,816)<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; dwTotalPhys参数表明了系统的32MB RAM，分配了18.5MB给程序存储区RAM，其中12MB仍然可用。注意这对应用程序来说，并不是通过这次调用，就知道了另外14MB的RAM是分配给对象存储区的。要检测分配给对象存储区的RAM的大小，要使用GetStoreInformation。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwTotalPageFile和dwAvailPageFile参数是0，表明页面文件不被Windows CE所支持。dwTotalVirtual参数十分有趣，因为它显示了Windows CE 强制给程序的32-MB的虚拟内存限制。其间，dwAvailVirtual参数显示了只使用了32MB虚拟内存的一小部分（译者注：即33,554,432-31,522,816=2,031,616）。<br><span><strong><font size=5>应用程序的地址空间</font></strong></span><br>尽管和Windows XP的应用程序设计类似，但Windows CE应用程序地址空间有一个巨大的不同影响到应用程序。在Windows CE之下，一个应用程序被限制在虚拟内存空间中它自己的32MB slot（槽）和 32MB 的slot 1中，slot 1用来加载基于XIP的DLL（译者注：Windows CE将虚拟地址空间分为33个slot，每个slot 32MB，序号从0-32 ，附插图c-1,c-2）。当系统只有4MB RAM的时候，分配给应用程序32MB的虚拟地址空间看起来是比较合理的，Win32的程序员在使用这个2-GB的虚拟地址空间的时候，必须记住对Windows CE应用程序的虚拟地址空间限制。<br>图7-1展示了一个应用程序的64-MB虚拟地址空间，其中包括32MB用于XIP的DLL空间。<br><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nbcool/p1.bmp"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图7-1&nbsp;&nbsp;&nbsp;&nbsp; Windows CE的内存映射图<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要注意的是应用程序是以一个64-KB的内存区域开始从0x10000映射，记得最低的64KB地址空间是由Windows为所有应用程序保留的。文件映象包括代码，静态数据段和资源段。在实际过程中，当应用程序启动时代码页（code pages）不会载入进来，只有在需要该页面被载入的时候，代码才被载入进来。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只读静态数据段（read-only static data segment）和可读写静态数据区（read/write static data areas）只占很少的页面。这些段都是排列在一起的。如同代码一样，只有当这些数据段被应用程序读或者写的时候才会提交给RAM。应用程序的资源将被载入到一些分离的页面中，这些资源是只读的，并且只有当它们被应用程序获取的时候才会分页进入RAM。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 应用程序的栈（stack）被映射到资源段之上。栈的段位置很容易被找到，因为它提交的页就在被保留的区域的尾部。栈的表现是从高地址到低地址增长（译者注：即从高到低填满地址）。如果该应用程序有超过一个线程，那么应用程序的地址空间就会保留超过一个的栈的段。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 紧接着栈的就是本地堆（local heap）。引导程序保留了大量的页，大约有几百K交给heap来使用，但是只提交满足malloc，new，LocalAlloc函数调用分配的内存（译者注：这里是说，被分配多少内存才可以提交多少内存，没被分配的不能用作提交）。从本地堆的保留页尾部到non-XIP DLL开始的部分剩余保留页面将被映射为自由的保留空间，如果RAM允许，应用程序可以提交这些保留页。Non-XIP DLLs 就是不能被在ROM中现场执行的DLL将被从上至下载入到32MB的地址空间。Non-XIP DLLs 包括那些被压缩存储在ROM中的DLL。被压缩的ROM 中的文件在被载入到RAM中执行前必须先解压缩。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 被保留给XIP DLLs的32MB 应用程序地址空间的较高位置。Windows CE 映射这些XIP DLLs的代码进入这个空间（译者注：即较高空间），而可读写段被映射进较低位置。从Windows CE 4.2开始，在ROM中的纯资源的DLL将被载入到应用程序64MB空间之外的虚拟内存空间。<br>&nbsp;<br><strong>脚注<br></strong>在PocketPC这样的移动式系统中，当用户按下关闭按钮时系统将不会被真正的关闭，系统进入一种低功耗的挂起状态。&nbsp;<br>&nbsp;<br><strong><font size=5>内存分配的不同类型<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个Windows CE 应用程序有许多不同的内存分配方式。在内存食物链的底端是Virtualxxx 函数，它们直接保留，提交和释放（free）虚拟内存页。接下来的是堆（heap） API。堆是系统为应用程序保留的内存区域。堆有两种风味：当应用程序启动时自动默认分配的本地堆（local heap），以及能够由程序手动创建的分离堆（separate heap）。在堆API之后是静态数据，数据块是被编译器定义好的或者由程序手动创建的。最后，我们来看栈，这是程序为函数存储变量的区域。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个Windows CE不支持的Win32 内存API是全局堆（global heap）。全局堆API包括Global&shy;Alloc，GlobalFree和GlobalRealloc，将不会出现在Windows CE中（译者注：很奇怪，我在Windows CE 中仍然可以使用这几个API，并且工作正常，好像Microsoft并没有把它们完全去掉）。全局堆只是从Windows 3.x的Win16 时期继承而来。在Win32中，全部和本地的堆很类似，全局内存一个独特用法是，为剪贴板的数据分配内存，在Windows CE中已经被本地堆替代并加上了句柄。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Windows CE中最小化内存使用的关键是选择与内存块使用模型相匹配的恰当的内存分配策略。我将回顾一下这些内存类型然后讲述Windows CE应用程序中的最小化内存使用策略。<br><span><strong><font size=5>虚拟内存<br></font></strong></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虚拟内存是内存类型中最基础的。系统调用虚拟内存API来为其他类型内存分配内存。包括堆和栈。虚拟内存API，包括VirtualAlloc，VirtualFree和VirtualReSize函数，这些可以直接操作应用程序虚拟内存空间的虚拟内存页面。页面可以保留，提交给物理内存，或使用这些函数释放。<br><span><font size=5><strong>分配虚拟内存</strong></font></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分配和保留虚拟内存是同过这个函数完成的：<br>LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD flAllocationType,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD flProtect);<br>VirtualAlloc的第一个参数是要分配内存区域的地址。当你使用VirtualAlloc来提交一块以前保留的内存块的时候，lpAddress参数可以用来识别以前保留的内存块。如果这个参数是NULL，系统将会决定分配内存区域的位置，并且围绕64-KB的范围（译者注：就是前面说提及的最小内存分配尺寸）。第二个参数是dwSize，要分配或者保留的区域的大小。这个参数以字节为单位，而不是页，系统会根据这个大小一直分配到下页的边界。<br>flAllocationType参数指定了分配的类型，你可以指定或者合并以下标志：MEM_COMMIT，MEM_AUTO_COMMIT，MEM_RESERVE和MEM_TOP_DOWN。MEM_COMMIT标志分配程序使用的内存，MEM_RESERVE保留虚拟地址空间以便以后提交。保留的页不能存取直到调用VirtualAlloc的时候再次指定了MEM_COMMIT标志。第三个标志，MEM_TOP_DOWN，告诉系统从最高可允许的虚拟地址开始映射应用程序。<br>The MEM_AUTO_COMMIT标志是唯一一个Windows CE最方便的标志，当这个参数被指定了之后，内存块立即被保留，当其中的页被第一次存取的时候，系统将自动提交该页。这允许你分配大块的虚拟内存而不需要顾及系统和实际RAM分配直到当前页被第一次使用。自动提交内存的缺点是，物理RAM需要退回当页面被第一次访问时可能不可用的页面。在这种情形下，系统将产生一个异常（exception）（译者注：可能会出现因为无法访问而出错）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VirtualAlloc可以通过并行多次调用提交一个区域的部分或全部来保留一个大的内存区域。多重调用提交同一块区域不会引起失败。这使得一个应用程序保留内存后可以随意提交将被写的页。当这种方式不在有效的时候，它会释放应用程序通过检测被保留页的状态看它是否在提交调用之前已经被提交。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flProtect参数指定了被分配区域的访问保护方式。这些不同的标志被总结在下面的列表中：<br>PAGE_READONLY<br>该区域为只读。如果应用程序试图访问区域中的页的时候，将会被拒绝访问。<br>PAGE_READWRITE<br>区域可被应用程序读写。<br>PAGE_EXECUTE<br>区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。<br>PAGE_EXECUTE_READ<br>区域包含可执行代码，应用程序可以读该区域。<br>PAGE_EXECUTE_READWRITE<br>区域包含可执行代码，应用程序可以读写该区域。<br>PAGE_GUARD<br>区域第一次被访问时进入一个STATUS_GUARD_PAGE异常，这个标志要和其他保护标志合并使用，表明区域被第一次访问的权限。<br>PAGE_NOACCESS<br>任何访问该区域的操作将被拒绝。<br>PAGE_NOCACHE<br>RAM中的页映射到该区域时将不会被微处理器缓存（cached）。<br>PAGE_GUARD和PAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页（guard page），即当一个页被提交时会因第一次被访问而产生一个one-shot异常，接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式（DMA）来共享内存块。<br><span><font size=5><strong>区域和页</strong></font></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在我继续谈论虚拟内存API之前，我需要说明一个比较细微的差异。虚拟内存在区域内被保留是以64KB为基础的。在区域内的页面能够一页一页地被提交（译者注：前面说到在Windows CE中每页是4096字节或1024字节）。你可以直接提交一页或者几页而不是保留区域的全部页。但是对页或几页来说，直接提交的仍是以64-KB为单位（译者注：可以直到被提交的页数量足够填满64KB才真正提交），因为这个原因，最好保留一块64-KB的虚拟内存，然后提交那些需要的页到区域里。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因为对每个进程32MB虚拟内存地址空间的限制，这就有了一个最大值 32MB/64KB-1=511，这是虚拟内存在内存溢出前能被保留的最大值。接下来，有个例子，代码段如下：<br>#define PAGESIZE 1024&nbsp;&nbsp; // Assume we're on a 1-KB page machine<br>for (i = 0; i &lt; 512; i++) <br>pMem[i] = VirtualAlloc (NULL, PAGESIZE, MEM_RESERVE │ MEM_COMMIT,PAGE_READWRITE);<br>代码分配512个单页的虚拟内存。甚至你系统还有一半的可用RAM，VirtualAlloc也会在完成分配前失败。因为它的运行已经超出了应用程序的虚拟地址空间。发生这种情况是因为每1-KB的块要占用64-KB的空间，接下来应用程序的代码，栈，和本地堆也要映射到同样的32-MB虚拟地址空间，可用的虚拟分配区域通常不超过475个。<br>&nbsp;&nbsp;&nbsp;&nbsp; 一个比较好的分配512块特殊内存的方法是这样做：<br>#define PAGESIZE 1024&nbsp;&nbsp; // Assume we're on a 1-KB page machine.<br>// Reserve a region first.<br>pMemBase = VirtualAlloc (NULL, PAGESIZE * 512, MEM_RESERVE,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PAGE_NOACCESS);<br>&nbsp;<br>for (i = 0; i &lt; 512; i++) <br>&nbsp;&nbsp;&nbsp; pMem[i] = VirtualAlloc (pMemBase + (i*PAGESIZE), PAGESIZE, <br>&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; MEM_COMMIT, PAGE_READWRITE);<br>&nbsp;&nbsp;&nbsp;&nbsp; 代码首先保留了一块区域，页面将在以后被提交。因为区域已经被先保留了，提交页就不受64-KB限制（译者注：只有保留页最小值受64KB限制），等等，如果你系统中有512KB的可用内存，分配将会成功。<br>&nbsp;&nbsp;&nbsp;&nbsp; 尽管我刚才给你看的是一个人为的例子（还有比直接分配虚拟内存更好的方法来分配1-KB的内存块），这中内存分配方法验证了一个重要的不同（对于其他Windows系统）。在桌面版本的Windows中，工作中的应用程序有一个完全的2-GB的虚拟地址空间。在Windows CE中，一个程序员必须明白每个应用程序只被保留了较小的32-MB虚拟地址空间。<br><strong><font size=5>释放虚拟内存</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过调用VirtualFree来取消提交，或释放虚拟内存。从物理RAM页中取消提交或者取消映射，但是保持页被保留的状态。函数原型如下：<br>&nbsp;&nbsp;&nbsp; BOOL VirtualFree (LPVOID lpAddress, DWORD dwSize,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwFreeType);<br>lpAddress参数是一个指针，指向要被释放或取消提交的虚拟内存的区域。dwSize参数指明要取消提交区域的大小，以字节为单位。如果区域要被释放，这个值必须是0，dwFreeType参数包含了操作类型标志，MEM_DECOMMIT标志指定了区域将被取消提交但是仍被保留，MEM_RELEASE标志说明区域要取消提交并且释放。<br>在区域中的所有的页通过VirtualFree被释放必须处在同样的情况下。更确切地说，区域中的全部页要被释放，那这些页要么都是被提交的页，要么都是被保留的页。如果有些页被提交，有些页被保留，那么VirtualFree函数调用就会失败。<br><span><font size=5><strong>改变和查询权限</strong></font></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过调用VirtualProtect来修改最初通过VirtualAlloc指定的虚拟内存区域的访问权限。这个函数只能改变被提交的页的访问权限。函数的原型如下：<br>BOOL VirtualProtect (LPVOID lpAddress, DWORD dwSize, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD flNewProtect, PDWORD lpflOldProtect);<br>开始的两个参数lpAddress和dwSize，指定了函数作用的块的大小。flNewProtect参数包含区域的新的保护标志。这些标志和我前面提到的VirtualAlloc函数使用的一样。lpflOldProtect参数指向一个DWORD，将返回旧的保护标志（译者注：如果此处为NULL或指向一个无效的变量，函数将会失败）。<br>当前区域的保护权限可用通过下面的调用查询：<br>DWORD VirtualQuery (LPCVOID lpAddress, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PMEMORY_BASIC_INFORMATION lpBuffer,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwLength);<br>&nbsp;&nbsp;&nbsp; lpAddress参数包含区域开始查询的地址。lpBuffer指针指向我很快就要提到的一个PMEMORY_BASIC_INFORMATION结构。第三个参数dwLength，必须包含PMEMORY_BASIC_INFORMATION结构的大小。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PMEMORY_BASIC_INFORMATION结构被定义如下：<br>typedef struct _MEMORY_BASIC_INFORMATION { <br>&nbsp;&nbsp;&nbsp; PVOID BaseAddress; <br>&nbsp;&nbsp;&nbsp; PVOID AllocationBase; <br>&nbsp;&nbsp;&nbsp; DWORD AllocationProtect; <br>&nbsp;&nbsp;&nbsp; DWORD RegionSize; <br>&nbsp;&nbsp;&nbsp; DWORD State; <br>&nbsp;&nbsp;&nbsp; DWORD Protect; <br>&nbsp;&nbsp;&nbsp; DWORD Type; <br>} MEMORY_BASIC_INFORMATION;<br>MEMORY_BASIC_INFORMATION结构的第一个字段是BaseAddress，是传递给VirtualQuery函数的一个地址。AllocationBase字段包含使用VirtualAlloc函数分配的区域的基地址，AllocationProtect字段包含区域原来被分配时的保护属性。RegionSize字段包含从传递给VirtualQuery的指针开始到一系列具有相同属性的页为结尾的区域大小（译者注：这里是从基地址开始）。State字段包含区域中页的状态－自由，保留，提交。Protect字段可以包含MEM_PRIVATE标志，指明该区域包含应用程序私有的数据；MEM_MAPPED指明该区域被映射为一个内存映射文件；MEM_IMAGE指明该区域被映射为一个EXE或DLL模块。<br>理解VirtualQuery最好的方式是看例子，比方说一个应用程序保留了16,384字节（在以页面大小为1-KB的机器中占16页）。系统从地址0xA0000开始保留这16-KB的块。后来应用程序从最初的区域中提交了从第2048字节（2页）开始的9216字节（9页）。图7-2显示了这个假设的情况。<br>图7-2被保留的区域有9页被提交<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果一个对VirtualQuery的调用中，lpAddress指向第四页的区域（地址0xA1000），返回值如下：<br>BaseAddress&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xA1000<br>AllocationBase&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xA0000<br>AllocationProtect&nbsp;&nbsp;&nbsp; PAGE_NOACCESS<br>RegionSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x1C00&nbsp;&nbsp;&nbsp; (7,168 bytes or 7 pages)<br>State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MEM_COMMIT<br>Protect&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PAGE_READWRITE<br>Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MEM_PRIVATE<br>BaseAddress字段包含传递给VirtualQuery的地址，值为0xA1000，在最初的区域中是第4096字节。AllocationBase字段包含最初区域的地址。当AllocationProtect设为PAGE_NOACCESS时，指明区域是最初被保留的，而不是直接提交。RegionSize字段包含传递给VirtualQuery的指针0xA1000开始，到被提交的页结束地址0xA2C00的字节数。State和Protect字段包含的标志表明当前的页状态。Type字节表明区域被应用程序分配给自己使用。<br><span><strong><font size=5>堆</font></strong></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很明显，以页为单位分配内存对应用程序来说效率是很低的。为了优化内存的使用，应用程序需要以字节为单位分配和释放内存，或者至少以每8字节为单位。系统通过堆来实现这种分配方式。使用堆可以免去处理由Windows CE支持的不同微处理器的不同页面大小。一个应用程序可以简单地在堆中分配一块内存，由系统来处理分配需要的页数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 就像我前面提到的，堆是系统为应用程序保留的虚拟内存区域。系统提供大量的函数来在堆中分配和释放内存块，并且间隔比页要小（译者注：例如每页大小为4KB，而堆分配可以字节为单位）。当内存由应用程序的堆分配时，系统自动分配调整堆大小来满足需要，当堆中的内存块被释放时，系统会查看是否整页被释放，如果是的话，那么该页将被回收。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不同于Windows XP，Windows CE只支持在堆中分配固定（fixed）的块。这简化了内存块在堆中的处理，但是这使得堆在分配和释放一段时间后会产生碎片。当堆里已经清空的时候，仍然会占用大量的虚拟内存页，因为系统不能在堆中内存页没有完全释放的时候回收这些页（译者注：因为堆以字节为单位，一页中可能有的块需要被释放，其他的块不需要，所以整页都不会被释放）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当应用程序启动的时候，每个程序都会有一个由系统创建的默认或本地堆。本地堆中的内存块，可以通过LocalAlloc，LocalFree和LocalRealloc来分配，释放和改变大小。一个应用程序也可以建立分离堆。这些堆和本地堆有着相同的属性，但是是通过一组Heapxxxx函数来管理的。<br><strong><font size=5>本地堆</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在默认情况下，Windows CE最初会保留192,512字节给本地堆，但是只提交被分配的页。如果应用程序在本地堆中分配了超过188KB，系统将会分配更多的空间给本地堆。增加堆大小将需要一个分离的，不连续的保留地址空间作为堆的附加空间。应用程序不应该假设本地堆被包含在一块虚拟地址空间里。因为Windows CE 的堆只支持固定的块，Windows CE执行的只是Win32本地堆函数的子集，提供必要的分配，改变大小，释放固定的本地堆内存块。<br><strong><font size=5>在本地堆中分配内存<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过一下调用在本地堆中分配一块内存：<br>HLOCAL LocalAlloc (UINT uFlags, UINT uBytes);<br>调用返回一个HLOCAL，这是本地内存块的句柄，但是由于内存块是固定分配的，所以返回值可以被简单地看作是一个指向块的指针。<br>uFlags参数描述了内存块的特征。标志由于Windows CE被限制固定分配操作，只支持以下内存：<br>LMEM_FIXED<br>在本地堆中分配一个固定内存块，因为本地堆分配已经固定，所以是多余的。<br>LMEM_ZEROINIT<br>初始化内存内容为0。<br>LPTR<br>合并LMEM_FIXED和LMEM_ZEROINIT标志。<br>uBytes参数指定了要分配的内存块的大小，以字节为单位。块大小要补齐，但是只针对后面8字节范围。<br><span><font size=5><strong>释放本地堆的内存<br></strong></font></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用释放内存块：<br>HLOCAL LocalFree (HLOCAL hMem);<br>函数需要本地堆内存句柄，成功会返回NULL。如果调用失败，会返回内存块的句柄。<br><strong><font size=5>改变和查询本地堆内存的大小</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过调用改变本地堆的分配：<br>HLOCAL LocalReAlloc (HLOCAL hMem, UINT uBytes, UINT uFlag);<br>hMem参数是一个由LocalAlloc返回的指针（句柄）。uBytes参数是内存块的新大小。uFlag参数包含给新内存块的标志。在Windows CE中，有两个新标志与之相关，LMEM_ZEROINIT和LMEM_MOVEABLE。LMEM_ZEROINIT表示调用函数后内存块中新增加的区域被初始化为0。LMEM_MOVEABLE标志告诉Windows，当内存块增加后，没有合适的空间容纳内存块时，函数可以立即移动内存块。如果没有这个标志，当你没有合适的空间来满足需要的时候，LocalRealloc将会出现out-of-memory的错误而失败，如果你指定了LMEM_MOVEABLE标志，调用将会返回句柄（实际是指向内存块的指针）。<br>内存块的大小可以通过以下调用查询：<br>UINT LocalSize (HLOCAL hMem);<br>返回内存块最少需要的内存大小。像我前面提到的，Windows CE本地堆自动以8个字节来补齐（译者注：就是分配1字节要占8字节）。<br><strong><font size=5>分离堆</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了避免本地堆的碎片，并且如果你要分配连续的内存块，较好的办法是建立分离堆，但将花费一定的时间。一个例子就是，文本编辑器为要编辑的文件建立多个分离堆。当文件被打开或者关闭的时候，堆随之建立和销毁。<br>在Windows CE下的堆和Windows XP下有着同样的API。唯一值得注意的不同是缺少HEAP_GENERATE- _EXCEPTIONS标志。在Windows XP下，该标志表示系统在分配请求不合适的时候产生一个异常。<br><span><font size=5><strong>建立一个分离堆<br></strong></font></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用建立一个分离堆。<br>HANDLE HeapCreate (DWORD flOptions, DWORD dwInitialSize,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwMaximumSize);<br>在Windows CE中，第一个参数flOptions必须为空或包含HEAP_NO_SERIALIZE标志。默认情况下，Windows堆管理程序防止一个进程中的两个线程在同意时间访问堆。这个串行参数防止系统用来跟踪堆中内存块分配的堆指针被破坏。在其他版本的Windows中，当你不需要这种保护时可以使用HEAP_NO_SERIALIZE标志。在Windows CE中，该标志是为了兼容性而提供的，所有的堆访问都是串行的（译者注：串行即非并行，只能依次访问）。<br>其他两个参数，dwInitialSize和dwMaximumSize，指定了最初的大小和预期的堆最大值。dwMaximumSize的值确定虚拟内存空间保留给堆多少页。如果你想让Windows来决定有多少页可以保留，你可以把这个参数设为0。默认一个堆的大小是188KB，dwInitialSize参数决定了有多少这些保留的页将被提交。如果该参数为0，表示堆将一页一页提交。<br><span><font size=5><strong>在分离堆中分配内存<br></strong></font></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用分配内存<br>LPVOID HeapAlloc (HANDLE hHeap, DWORD dwFlags, DWORD dwBytes);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意，返回值是一个指针，而不是和LocalAlloc函数一样的句柄。分离堆总是分配固定的内存块，甚至在Windows XP和Windows Me中也是一样。第一个参数是通过HeapCreate调用返回的句柄。dwFlags参数可以是两个自说明的（self-explanatory）标志之一HEAP_NO_SERIALIZE和 HEAP_ZERO_MEMORY。最后一个参数dwBytes指定了要分配的内存块字节数。大小要和DWORD补齐。<br><span><font size=5><strong>释放分离堆中的内存</strong></font></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用释放内存块：<br>BOOL HeapFree (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);<br>dwFlags参数唯一的标志是HEAP_NO_SERIALIZE，当hHeap包含堆句柄时，lpMem参数指向要释放的内存块。<br>改变和查询分离堆中内存的大小：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用改变堆大小。<br>&nbsp;&nbsp;&nbsp; LPVOID HeapReAlloc (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwBytes);<br>&nbsp;&nbsp;&nbsp; dwFlags参数包含三种标志的组合：HEAP_NO_SERIALIZE，HEAP_REALLOC_IN_PLACE_ONLY和HEAP_ZERO_ MEMORY。其中较新的标志是HEAP_REALLOC_IN_PLACE_ONLY，这个参数告诉堆的管理者，找不到要分配的块的空间，重分配操作失败。这个标志方便的地方在于当你有了一些指向内存数据块的指针，并且你不想改变内存块。lpMem参数是一个指向要改变大小的内存块的指针，dwBytes参数是被请求的新内存块的大小。注意，HeapReAlloc中HEAP_REALLOC_IN_PLACE_ONLY标志提供和LocalReAlloc中LMEM_MOVEABLE相反的作用。HEAP_REALLOC_IN_PLACE_ONLY防止在分离堆中对内存块默认的移动操作。而LMEM_MOVEABLE允许本地堆中对内存块的默认移动操作。如果HeapReAlloc成功，就返回一个指向内存块的指针，否则就返回NULL。除非你指定内存块不可重新定位，那么当内存块因为堆中空间不足时将不得不重定位，因此造成返回指针的值将与原来不同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要决定实际的内存块大小，你可以作以下调用：<br>DWORD HeapSize (HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);<br>参数就像你想象的：有堆的句柄，单选标志HEAP_NO_SERIALIZE，和指向内存块的指针。<br><strong><font size=5>销毁一个分离堆<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你可以通过以下调用完全释放一个堆：<br>BOOL HeapDestroy (HANDLE hHeap);<br>在堆中单个的内存块并不需要在销毁堆前释放。<br>最后一个是写DLL时比较有价值的函数：<br>HANDLE GetProcessHeap (VOID);<br>返回的是调用DLL时进程的本地堆的句柄。这个函数允许一个DLL在调用者进程的本地堆中分配内存。GetProcessHeap返回的句柄可以供其他堆调用使用，HeapDestroy除外。<br><span><strong><font size=5>栈<br></font></strong></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 栈是Windows CE内存类型中最容易使用的（自行管理）。在Windows CE中的栈像其它操作系统一样，是被引用函数的临时变量存储区。操作系统也用栈来存储函数的返回地址和在异常处理中微处理器寄存器的状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在系统中，Windows CE给每个线程一个分离的栈。默认情况下，系统中每个栈大小最大被限制为58KB。在一个进程中，每个分离的线程可以增加栈的大小直到58-KB的限制。<br>这个限制使得要我们要知道Windows CE如何对栈管理。当线程被建立的时候，Windows CE保留一个64-KB的区域给每个线程的栈。栈增加时，提交虚拟内存页是从上至下的。当栈减小时，系统将处于的低内存环境（low-memory），会回收在栈下面未使用但是仍然被提交的页。58KB的限制来源于64-KB的区域减去用来防止栈的上溢和下溢的页面数量。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当一个应用程序建立一个新的线程时，栈的最大尺寸可以通过建立线程时CreateThread调用来指定。应用程序的主线程的栈大小可以通过应用程序被连接时的连接器开关（linker switch）来指定。同样会有一些页用作防护，但是栈的大小可以指定至1MB。注意，这个指定大小同样会被用作所有分离线程栈的默认栈大小。那就是说，如果你指定主栈为128KB，程序中所有其他的线程栈大小也限制为128KB，除非在用CreateThread建立线程时指定一个不同的大小。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当你计划如何在应用程序中使用栈的时候，另一个要值得考虑事情的是。当应用程序调用一个需要栈空间的函数时，Windows CE会试图立即提交满足要求的当前栈之下的页面，如果没有物理RAM可用，需要栈空间的线程将会暂时停止。如果请求在短时间内得不到允许，可能产生一个异常。但是如果系统不发生异常的化，Windows CE将会最大限度释放请求的页。我将简短地说明一下低内存环境，但现在你只需要记住在的内存环境中不要尝试使用大量的栈空间。<br><span><strong><font size=5>静态数据<br></font></strong></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C和C++应用程序有一个预先定义好的内存块，这是由应用程序被装载时自动分配的。这些块被用来存储静态分配的字符串，缓冲区和全局变量，同时也包括通过静态连接到应用程序的静态库函数中的缓冲区。这些对C程序员来说都不陌生，但是在Windows CE下，这是最后一块可以在RAM之外压缩的空间（译者注：作者的意图是尽可能压缩内存占有率）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows CE分配给应用程序两块RAM中的内存块存放静态数据，一个是可读写数据（read/write data）和只读数据（read only data）。因为这些区域是基于页分配的，所以你可以在一页的静态数据开始到下一页开始之间找到一些剩余空间。细微调整Windows CE应用程序就是要写满这些剩余的空间。如果你在静态数据区有空间，最好把一个或两个缓冲区放到静态数据区，避免动态分配缓冲区。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另一个值得考虑的事情是你是否在写一个基于ROM的应用程序。你要把尽可能多的数据移到只读静态数据区。Windows CE不会分配只读的RAM给基于ROM的应用程序。并且，ROM页会直接映射到虚拟地址空间。这实际上就给你了一个无限制的只读空间，而且不会影响到应用程序对RAM的需求。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 确定静态数据区大小的方法是查看连接器产生的映象（map）文件。映象文件主要用于调试（debug）目的来确定函数和数据的位置。但是如果你知道查看什么地方的话，它也可以用来显示静态数据的大小。列表7-1显示了一个由Visual C++产生的示例映象文件的一部分。<br>列表7-1。映象文件的顶部显示了应用程序数据段的大小<br>memtest <br>Timestamp is 34ce4088 (Tue Jan 27 12:16:08 1998) Preferred load address is 00010000<br>&nbsp;Start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;Class<br>0001:00000000 00006100H .text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CODE<br>0002:00000000 00000310H .rdata&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;DATA<br>0002:00000310 00000014H .xdata&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;DATA<br>0002:00000324 00000028H .idata$2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0002:0000034c 00000014H .idata$3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0002:00000360 000000f4H&nbsp; .idata$4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0002:00000454 000003eeH&nbsp;.idata$6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0002:00000842 00000000H .edata&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;DATA<br>0003:00000000 000000f4H&nbsp; .idata$5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0003:000000f4&nbsp; 00000004H .CRT$XCA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0003:000000f8&nbsp; 00000004H .CRT$XCZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:000000fc&nbsp; 00000004H .CRT$XIA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:00000100 00000004H .CRT$XIZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:00000104 00000004H .CRT$XPA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATA<br>0003:00000108 00000004H .CRT$XPZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:0000010c 00000004H .CRT$XTA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:00000110 00000004H .CRT$XTZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:00000114 000011e8H .data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0003:000012fc&nbsp; 0000108cH&nbsp; .bss&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0004:00000000 000003e8H&nbsp; .pdata&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0005:00000000 000000f0H&nbsp;&nbsp; .rsrc$01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA<br>0005:000000f0&nbsp; 00000334H&nbsp; .rsrc$02&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA <br><br>Address&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Publics by Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rva+Base&nbsp;&nbsp;&nbsp;&nbsp; Lib:Object <br>0001:00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _WinMain&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00011000 f&nbsp;&nbsp; memtest.obj<br>0001:0000007c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _InitApp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0001107c f&nbsp;&nbsp; memtest.obj<br>0001:000000d4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _InitInstance&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 000110d4 f&nbsp;&nbsp; memtest.obj<br>0001:00000164&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _TermInstance&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00011164 f&nbsp;&nbsp; memtest.obj<br>0001:00000248&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _MainWndProc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00011248 f&nbsp;&nbsp; memtest.obj<br>0001:000002b0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _GetFixedEquiv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;000112b0 f&nbsp;&nbsp; memtest.obj<br>0001:00000350&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _DoCreateMain&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00011350 f&nbsp;&nbsp; memtest.obj.&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在列表7-1中的映象文件指出了EXE文件有五个区。区0001是文本段，包含程序中可执行的代码。区0002包含只读（read-only）静态数据。区0003包含可读写（read/write）静态数据。区0004包含调用其他DLL的固定表。最后，区0005是资源区，包含应用程序的资源，例如菜单和对话框模板。<br><br>让我们来看看.data，.bss和.rdata行。.data区包含已初始化的可读写数据。如果你这样初始化了一个全局变量：<br>static HINST g_hLoadlib = NULL;<br>g_loadlib变量将结束在.data段末尾。.bss段包含未初始化的可读写数据。一个缓冲被定义如下：<br>static BYTE g_ucItems[256];<br>以.bss段为结尾。最后一个段.rdata，包含只读数据。你使用const关键字定义的静态数据结束在.rdata段。有一个结构的例子，使我用来作消息查询表的：<br>// Message dispatch table for MainWindowProc<br>const struct decodeUINT MainMessages[] = {<br>&nbsp;&nbsp;&nbsp; WM_CREATE, DoCreateMain,<br>&nbsp;&nbsp;&nbsp; WM_SIZE, DoSizeMain,<br>&nbsp;&nbsp;&nbsp; WM_COMMAND, DoCommandMain,<br>&nbsp;&nbsp;&nbsp; WM_DESTROY, DoDestroyMain,<br>};<br>&nbsp;&nbsp;&nbsp; .data和.bss块被折叠进0003区，如果你将第三区的所有块大小加起来，总共为0x2274，或8820字节。为和下页对齐，读写数据区将占9页，那么就有396字节未使用（译者注：1024*9-8820=396）。因此在这个例子中，把一个或者两个缓冲区放入静态数据区比较合适。只读数据段0002区，包括.rdata，占0x0842或2114字节，占3页，剩余958字节，几乎是一整页。在这种情况下，移动75字节的常量数据从只读段到可读写段将在应用程序加载时节约一页的RAM。<br><strong><font size=5>字符串资源</font></strong><br>有一个经常忘记的只读区域时应用程序的资源段，像我前面在第四章提到的Windows CE的新特性有一个LoadString函数，值得再次重复。如果你调用LoadString时指向缓冲区的指针写0，函数将返回一个指向资源段中字符串的指针。例子如下：<br>LPCTSTR pString;<br>&nbsp;<br>pString&nbsp; = (LPCTSTR)LoadString (hInst, ID_STRING, NULL, 0)<br>返回的字符串是只读的，但是它允许你应用字符串而不需要分配一个缓冲给字符串。这里警告一下，字符串不能以0结尾，除非你在资源编译器命令行中加了-n开关。不管如何，单词必须是先于字符串资源长度（译者注：作者此处意思可能是说长度包含字符串资源的长度）。<br><strong><font size=5>选择适当的内存类型<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在我们已经看过了不同类型的内存，是时候来考虑最好的使用办法了。对大的内存块来说，直接分配虚拟内存是最好的办法，一个应用程序可以保留很多的地址空间（直到应用程序32MB的限制）但是只能在一个时间提交必须的页。直接分配虚拟内存是最灵活的内存分配方式，它把页间隔（granularity）的负担以及对保留页和提交页都交由我们负担。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本地堆是很方便的，它不需要创建并且会自动随着需求扩大。但碎片是这里的问题。但是要考虑到Pocket PC的应用程序可能会运行几星期或几个月的时间。在Pocket PC上没有关闭电源的按钮，只有挂起命令。因此，你考虑内存碎片的时候不要假设用户会打开应用程序，改变一个项目，然后关闭它。用户可能打开程序然后让它一直运行以至于程序就像一个快捷方式（quick click away）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分离堆的优点是当你不用时可以销毁，把碎片消灭在萌芽状态。有一点不好的就是分离堆需要手动创建和销毁。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 静态数据区是放置一两个缓冲区的好地方，因为页面是已经被分配的。管理静态数据的关键是使静态数据段大小尽可能地接近，但是要超过你目标处理器的页面的大小。当常量数据在只读段中，往往较好的办法是把它移到可读写段中。但当应用程序被烧到ROM中时，你不要这么做。常量数据越多会比较好，因为它不占RAM。只读段方便应用程序从对象存储区启动，因为只读页能通过操作系统丢弃和重载。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 栈用起来比较简单而且到处存在。唯一要考虑的是栈的最大尺寸和在的内存环境下扩大栈的问题。确定你的应用程序在关闭的时候不需要大量栈空间。当程序被关闭时，如果系统挂起你程序中的一个线程，用户可能会丢失数据。这会使顾客不满意。<br><strong><font size=5>低内存环境</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当系统运行在一个低RAM环境中，应用程序将调整并最小化它们的内存使用。Windows CE运行在一个几乎永久的低内存环境中。Pocket PC被特意设计为运行低内存环境。在Pocket PC中的应用程序没有关闭按钮，当系统需要更多内存时，外壳（shell）自动关闭这些程序。正因为如此，Windows CE有许多方法来管理运行在低内存系统中的程序。<br><strong><font size=5>WM_HIBERNATE 消息</font></strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows CE第一个最明显的变化时是增加了WM_HIBERNATE消息。Windows CE的shell发送消息给最顶层的有WS_OVERLAPPED式样（那就是说，既没有WS_POPUP也没有WS_CHILD式样）和WS_VISIBLE式样的窗口。这些限制将允许大多数程序至少有一个窗口可以接受WM_HIBERNATE消息。有一个例外就是，当应用程序不能真正结束程序而只是简单隐藏所有窗口。这种方式允许应用程序可以快速启动，因为它下次只是显示窗口。但是这就意味着，当用户想关闭它们的时候仍然占据着RAM。这对程序设计来说是正确的，但是不应用在Windows CE中，这种方式会造成程序被隐藏时总处在冬眠（hibernate）模式，因为它们永远接收不到WM_HIBERNATE消息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Shell发送WM_HIBERNATE消息给最顶层的窗口在Z轴相反的位置（reverse Z-order）直到内存被释放，使可用内存超过系统预先的限制。当应用程序接收到一个WM_HIBERNATE消息，它会尽可能减少内存占有程度。这包括释放被缓冲（cached）的数据；释放GDI对象，例如字体，位图和画刷；并销毁任何窗口控件。从本质上来说，应用程序将会减少内存到维持它内部状态的最小值。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果发送WM_HIBERNATE消息给后台的应用程序不能释放足够的内存以便使系统离开内存被限制的状态。WM_HIBERNATE消息将会发送给前台程序。如果你正在冬眠的程序开始销毁窗口的控件，你必须确保它不是前台的程序，控件消失不会给用户带来兴奋的感觉而是困惑。<br><span><font size=5><strong>内存限度</strong></font></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows CE监视系统自由的RAM，并对越来越少的RAM作出响应。当很少内存可用时，Windows CE首先发送WM_HIBERNATE消息，接下来会限制可能的内存分配。下面的两个表显示了Explorer shell和Pocket PC引发的低内存事件的自由内存级别。Windows CE定义了是个内存状态：normal，limited，low和critical。系统的内存状态依赖于整个系统有多少内存可用。这些限制都比4-KB页要高，因为系统具有内存最小分配限制，就像7-1和7-2的表。<br>表7-1 Explorer Shell的内存限度<br>事件&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; 自由内存1024-Page Size&nbsp; 自由内存4096-Page Size&nbsp;&nbsp;&nbsp; 注解 <br>Limited-memory state&nbsp; 128 KB&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; 160 KB&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; 发送WM_HIBERNATE 消息给in reverse Z-order的应用程序。<br>&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 释放栈空间并回收利用。 <br>Low-memory state&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;64&nbsp;KB&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;96 KB&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; 限制虚拟内存分配为16 KB。 显示Low-memory对话框。 <br>Critical-memory state&nbsp; 16 KB&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;48 KB&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; 限制虚拟内存分配为8KB。 </p>
<p><br>表7-2 Pocket PC的内存限度<br>事件&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; 自由内存1024-Page Size&nbsp;&nbsp;&nbsp;自由内存4096-Page Size&nbsp;&nbsp;&nbsp;&nbsp;注解 <br>Hibernate threshold&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;200 KB&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; 224 KB&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;发送 WM_HIBERNATE 消息给in reverse Z-order的应用程序。 <br>Limited-memory state&nbsp; 128 KB&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; 160 KB&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;开始关闭在 reverse Z-order上的应用程序。释放栈空间并回收利用。 <br>Low-memory state&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 64 KB&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; 96 KB&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;限制虚拟内存分配为16 KB。 <br>Critical-memory state&nbsp; &nbsp;16 KB&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; 48 KB&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; 限制虚拟内存分配为8 KB。 </p>
<p><br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这些内存状态的影响是共享剩余的财富。首先，WM_HIBERNATE消息被发送给应用程序，并请求减少它们的内存占有率，当应用程序被发送了一个WM_HIBERNATE消息后，系统将检测内存级别，确认是否可用内存在限度之上，如果可用内存不足，WM_HIBERNATE消息将被发送给下一个程序。这会持续到所有程序被发送了WM_HIBERNATE消息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exlporer shell和Pocket PC的低内存策略在这点上有区别。如果Explorer shell运行时，系统会显示OOM（out of memory）对话框，并请用户确认是否关闭一个应用程序或把对象存储区的RAM重新划分给程序内存。如果用户选择了其中之一，仍然没有足够的内存，out of memory对话框将会再次出现，这个过程会重复，直到H/PC有足够的在限度之上的内存。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对Pocket PC来说，操作稍微有些不同。Pocket PC shell自动开始关闭最近最少使用的应用程序，而不询问用户。如果关闭除了前台程序和shell之外的所有程序，仍然没有足够内存，系统将会使用其他的技术来从栈开始清理自由的页，并限制虚拟内存分配。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果在任何一个系统上，应用程序被请求关闭却没有关闭，系统在8秒钟后将会清理该应用程序。这就是一个应用程序不要分配大量的栈空间的原因。如果应用程序被关闭而导致低内存环境，很可能是栈空间不能分配，应用程序将被挂起。如果发生在系统请求应用程序关闭以后，可能是清除内存以后没有适当的恢复状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在low和critical-memory状态，应用程序被限制了内存分配的大小。在这些情况下，甚至还有可以满足要求的内存剩余情况下，请求分配大过允许限度的虚拟内存将会被拒绝。记住，并不止是虚拟内存分配被限制，堆分配和栈分配也被禁止，要满足分配请求，那么分配时需要虚拟内存在可允许的限制之上。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我这里要指出，发送WM_HIBERNATE消息和自动关闭应用程序是由系统的shell执行的。在一个OEM自己可以编写shell的嵌入式系统中，实现WM_HIBERNATE消息和其他内存管理技术是OEM厂商的责任。幸运的是，Microsoft Windows CE PlatForm Builder提供了Exlporer shell实现WM_HIBERNATE消息的源码。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里不言而喻，应用程序要检查任何内存分配调用的返回代码，但是因为这里还没说，所以我还是要说。检查内存分配调用的返回代码。在Windows CE中比在桌面版本的Windows中可能有更多的机会导致内存分配失败。应用程序必须很好地实现拒绝内存分配。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows CE不支持完全的Win32内存管理API，但是很清楚这里有对WindowsCE设备受限制内存的足够支持。一个极好的学习Win32错综复杂的内存管理API来源是Jeff Richter&#8217;s Programming Applications for Microsoft Windows (Microsoft Press, 1999)。当Jeff和我总结上述相同问题的时候，他在内存管理上花了6章篇幅。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们已经看过了程序存储区RAM，这是应用程序可用的RAM。现在是时间，在下两个章节，来看看另一部分RAM，对象存储区。对象存储区支持超过一个文件系统，它也支持一般的注册表API和Windows CE特有的数据库API。</p>
<img src ="http://www.cppblog.com/Jrong/aggbug/77540.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Jrong/" target="_blank">iJrong</a> 2009-03-23 09:16 <a href="http://www.cppblog.com/Jrong/articles/77540.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>