WINCE常見問題彙總

在mediaplayer全屏播放的時候,我可以用鍵盤上的某一個鍵調節聲音大小,現在我想在屏幕上顯示調節的結果就跟我們看電視一樣能出來一些標記。當聲音變大在屏幕上就增多,當聲音變小的時候就減少,得到播放窗口的DC,然後在上面顯示一個不同顏色的矩形或者其它形狀。 
 
 在ce自制平臺中如何添加自己的驅動?cec文件和reg,bib文件有何區別?看混了,不知道如何才能讓驅動註冊到平臺中。另外,reg和bib是我在編寫驅動必須要自己寫的麼 
CE 的驅動分爲本機驅動和流驅動,本機驅動你可以修改源碼(在%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS),然後 build驅動源碼,之後用PB編譯內核。流驅動是以DLL的形式被系統程序加載的。加載流驅動要在註冊表中添加信息,這和cec文件沒關。如果你想把此驅動DLL和註冊信息作爲一個整體給別人使用,那麼就要做一個cec文件,PB能夠讀取cec文件內容並加載到內核工程中。cec文件相當於安裝程序。 
 
 我安裝的是evc4.0,我開發的系統要顯示中文比如姓名什麼的。在編輯的時候是中文顯示,但是到了輸出時,顯示的都是亂碼。不知道應該怎麼去解決 
EVC的模擬器不支持中文。所以顯示出來的都是亂碼。1、你可以只顯示英文,調試結束後在改界面爲中文。2、安裝PB,PB的模擬器支持中文。  
 
 CStatic及其控件的文字無法垂直居中顯示,雖然在evc的資源編輯器中可設置,但是一旦運行,卻無法垂直居中顯示 
這種情況正常,而且除此之外還不能右對齊。 
 
 獲取設備ID的種類有哪些 
除了手機模塊外,還有CF卡有序列號、硬盤有ID號、網卡有MAC號等。這幾種方法數獲取手機模塊ID最簡單。 

  
     在應用程序中,如何向修改本機的ip 地址等網絡參數,並使之立即生效? 
網絡設置保存在註冊表中,位置[HKEY_LOCAL_MACHINE/Comm/網卡名稱/Parms/TcpIp],例如常見的CS8900網卡設置: 
[HKEY_LOCAL_MACHINE/Comm/CS89001/Parms/TcpIp] 
   "EnableDHCP"=dword:0 
   "DefaultGateway"="192.168.0.1" 
   "DNS"="111.111.111.111" 
   "UseZeroBroadcast"=dword:0 
   "IpAddress"="192.168.0.2" 
   "Subnetmask"="255.255.255.0" 
設置之後要生效有兩種辦法:一種熱啓動,調用KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0,  NULL),熱啓動時間很短暫;另外一種調用DevieceIoControl API,傳遞IOCTL= IOCTL_NDIS_REBIND_ADAPTER。 
 
 如何向控制面板中那樣,修改系統聲音的音量 
調用API waveOutSetVolume(HWAVEOUT, dwVolume ),一般參數1爲0。在[HKEY_CURRENT_USER/ControlPanel/Volume]下是系統聲音的註冊表設置。 
 
 在應用程序中如何實現jpg、gif圖片的顯示 
有幾種辦法: 
1、在MSDN中搜索標題爲“Windows CE .NET Technical Frequently Asked Questions”的文檔,其中有一個問題“How can I display JPEG, GIF, and other graphics files?”,下面就是答案。 
2、如果安裝了Windows CE 5.0,一個例子源碼位於WINCE500/PUBLIC/GDIEX/SDK/SAMPLES/SIMPLE。 
3、用IWebBrowser組件實現。 

  
     在應用程序中如何實現系統待機 
void GwesPowerOffSystem(void); 

  
     在WINCE下如何實現鍵盤鉤子 
我寫了一個簡單的例子,把其中主要部分截取出來放到了我的FTP裏。用戶名以及密碼均爲winceuser,地址是ftp://211.95.73.26/[email protected]/SourceCode/用鉤子禁止系統鍵.rar。 

  
     在WINCE中如何得到網卡MAC地址 
事實證明,獲得物理網卡的MAC地址並沒有被統一成一個API或者IOCTL,如果網卡驅動程序沒有提供接口的話只能直接訪問寄存器獲得。讀者可以參考目錄WINCE500/PUBLIC/COMMON/OAK/DRIVERS/NETCARD裏的一些驅動源碼。 
 
 Windows XP Embedded 和Windows CE有何區別 
簡單地說Windows XP Embedded採用Windows XP內核,只能運行在x86處理器上,優點是能夠運行PC上現有的應用軟件,缺點是授權費太高,標價¥900元;Windows CE採用Windows CE內核,能夠運行在多種處理器上,如x86、ARM、SHX、MIPS等,優點是授權費低,最低Core版標價¥30元。缺點是需要單獨開發應用軟件、定製內核,甚至開發BSP、Driver。 

  
     wince下只是把調制解調器的驅動掛接在了com1,如何將器驅動掛接在com2上? 
1、在HLM/drivers/buildin/com2/unimodem下複製和com1一樣的數據 2、在HLM/ExtModems/ HayesCompat下改寫Port爲COM2:,再改寫FriendlyName爲"Hayes Compatible 在 COM2:"。 
 
 如何定製自己的外殼 
1、先開發一個外殼軟件,假設名稱爲MyShell.exe 
2、刪除註冊表[HKEY_LOCAL_MACHINE/init]下如下一行: 
   "Launch50"="explorer.exe" 
3、在註冊表[HKEY_LOCAL_MACHINE/init]下添加如下一行: 
   "Launch50"="MyShell.exe" 
上述的“LaunchXX”中的XX爲序列數,內核依據這個序列數按由小到大的順序來分別執行所有子鍵列出的應用程序,具體數值應該爲多少請參考幫助文檔的說明。 
如果原來的內核中添加了標準外殼(standard shell)組件,或者添加了其它組件而這些組件需要依賴標準外殼,那麼在PB中是無法刪除標準外殼組件的,解決辦法一是保留explorer.exe在內核中,二是刪除依賴標準外殼的組件。 

  
     我原來的工程是x86版本的,編譯選項只有x86,我如何能夠編譯ARM版本的 
兩種辦法: 
1、用EVC新建一個工程的時候,建議複選“CPUs”列表,這樣發生了這種事情也能夠輕易通過選擇“WCE Configuration”工具欄中的CPU列表來編譯不同CPU版本的軟件 
2、如果打開工程後CPU列表中只有x86,而此時已經安裝了ARM版本的SDK,那麼單擊EVC菜單“build”-“configurations”,然後單擊“add”按鈕來添加CPU。 

  
     通常情況下WINCE採用串口1作爲調試時輸出信息用途,要正式出產品前如何去掉串口1的調試功能 
正常情況下串口1只有在編譯debug版本的內核時纔在BootLoader中初始化串口1用於輸出信息,而編譯release版本會跳過此代碼。而有些 BSP設計成沒有宏定義,也就是說無論什麼版本都會在BootLoader中初始化串口1,這樣造成WINCE啓動後串口1無法被應用程序使用。對於這種情況只能在BootLoader源碼中刪除初始化代碼,如OEMInitDebugSerial。 
 
 基於wince的應用程序能建成console project麼? 
不能 
 
 ARM系統外擴一片512K RAM,驅動程序經過映射可以使用這一段RAM。1、應用系統如何使用這一段RAM?要加一層驅動嗎?2、如果要將這一段RAM當作RAM盤存儲系統,該如何作呢?應該要加文件系統吧,如何加呢? 
解答這個問題前,先要說一下WINCE的地址映射機制。對於包含MMU(存儲器管理單元)的處理器來說,如ARM和x86,WINCE要求OEM在定製內核的時候填寫一個虛擬地址與物理地址映射關係的表,稱爲OEMAddressTable,在這個表中定義了所有物理設備的起始物理地址,對應的起始虛擬地址,地址空間大小,RAM就包括在其中(如果是x86平臺還要求RAM起始虛擬地址從0x80000000開始)。如果有多片RAM,應該在 OEMAddressTable中將它們定義在一起,使之地址連續。對於非OEM的開發者來說,他們拿到的是定製好的內核,不能做任何修改,如果在產品中外擴一片RAM,只能通過API函數通知操作系統增加一條虛擬地址與物理地址映射關係表項。相關API函數有兩個,分別是 CreateStaticMapping和VirtualCopy。它們的相同之處是都用於建立物理地址和虛擬地址的映射關係。它們的不同之處是 CreateStaticMapping映射的虛擬地址範圍在0xC400 0000 到 0xE000 0000之間,這個範圍只能由內核訪問,一般用於ISR訪問,因爲ISR只能訪問靜態映射的虛擬地址空間,不能用VirtualCopy。VirtualCopy通常和VirtualAlloc配合使用,映射的虛擬地址空間在0x8000 0000以下,一般用於驅動程序和應用程序訪問。 
1、應用程序要訪問這片RAM,和驅動程序訪問方法一樣,調用VirtualAlloc和VirtualCopy。 
2、可以做一個流驅動程序專門用於讀寫這片RAM,這樣所有應用程序就可以通過調用流驅動接口函數來訪問,非要加文件系統也是可行的,通過修改註冊表就可以做到,但是麻煩一些。 

  
     我怎麼能在PB左邊的定製平臺加進我的驅動呢? 
兩種辦法: 
1、在platform.bib或者project.bib的MODULES部分添加一條語句,例如: 
 MyDriver.dll       C:/Driver/MyDriver.dll                     NK SH 
這樣編譯內核的時候就會把你的驅動DLL文件添加到內核中,如果有註冊表需要設置,在platform.reg或者project.reg中添加註冊表內容。 
2、通過製作.cec文件來添加驅動,製作.cec文件的優點是隻需製作一次,以後就可以通過將.cec文件導入到PB的Catalog中,象PB自帶的feature一樣通過菜單“Add to OS Design”添加到左邊的內核工程中。 

  
     WINCE有沒有相對路徑概念?如果沒有如何得到當前模塊的路徑? 
1、WINCE沒有相對路徑概念,只有絕對路徑,所以凡是涉及到路徑均爲絕對路徑。 
2、調用API GetModuleFileName,傳遞一個模塊的實例句柄就能夠得到模塊的絕對路徑。 

  
     怎樣讓 POCKET WORD打開*.dat格式(裏面都是數據)的文件?  
兩種辦法: 
1、調用API ShellExecuteEx,在結構體SHELLEXECUTEINFO中添加.dat文件的路徑。 
2、調用API CreateProcess,在第二個參數中設置.dat文件的路徑。 

  
     x86 Rom Boot Loader真的可以實現嗎?它確實能代替BIOS啓動計算機?  
Rom Boot  被設計存放在Flash/EEPROM中,也就是原來BIOS的位置,這樣當上電後CPU到固定地址執行代碼,也就是執行了Rom Boot的代碼,它對整個硬件系統進行初始化和檢測,並且支持通過網卡從遠程機器上下載nk.bin或者從本地IDE/ATA 硬盤的活動分區中尋找nk.bin文件加載。 Rom Boot的優點就是引導並且加載速度快,而且它自身完成了所有的操作,這樣就不用BIOS、MSDOS,更不用Loadcepc了。 
 
 對於x86 Rom Boot Loader,如何Build得到Romboot.rom? 
1、在PB中打開一個內核工程(x86的) 2、單擊PB菜單“Build”-“Open Build Release Directory” 3、用cd命令進入 %_WINCEROOT%/Platform/Geode/Romboot 4、build 
 
 如何設置、更改顯示分辨率 
能否設置、更改顯示分辨率由顯示驅動程序決定,而沒有統一的標準。例如CEPC,在啓動的時候可以通過設置loadcepc.exe的參數 /L來決定 WINCE啓動後的顯示分辨率,這是由於顯示驅動“VGA Linear Framebuffer”支持,而Geode可以通過在定製內核時修改註冊表項來決定WINCE啓動後的顯示分辨率。 
 
 幾個硬件使用同一個IRQ,那麼發生中斷的時候系統怎麼判斷到底是哪一個硬件發生的中斷呢?ISR裏面又應該怎麼控制呢? 
Windows CE 支持多個設備中斷共享一個IRQ,當一個共享IRQ發生時,CE內核的異常處理程序檢測設備特定的寄存器,因爲大多數設備都有一個單獨的寄存器用於表示設備的活動狀態,所以通過遍歷共享這個IRQ的所有設備的寄存器就可以判斷哪個設備發生中斷。nk.exe加載一個giisr.dll,這個.dll是微軟提供的,它其實是第一個可安裝ISR。默認CE內核就是調用這個dll來檢測寄存器狀態的,當然OEM可以編寫自己的.dll。 
CE內部有一個ISR鏈,也就是可安裝ISR。因爲CE允許OEM添加自己的ISR處理程序,所以ISR被設計成一個鏈表。排在前面的ISR比後面的ISR優先處理中斷,如果當前ISR能夠處理當前中斷,那就返回中斷ID由IST處理或者返回SYSINTR_NOP,如果當前ISR不能夠處理當前中斷,那就返回 SYSINTR_CHAIN讓下一個ISR處理。 
 
 請問在wince中如何在內核中增加一個與/windows同級的目錄? 
在platform.dat或者project.dat中添加語句。例如要創建根目錄下子目錄Program Files,語句如下: 
root:-Directory("Program Files")   
 
 文件格式如下所示,我想把每行的4個值讀到4個變量中,用EVC如何編程? 
第一行: 460.000,  3384672.357342,  521268.972763 
第二行: 475.117,  3384663.772419,  521281.415271 
僞代碼如下: 
FILE  *stream; 
stream = _wfopen(L"//a.txt", L"r+"); 
if( stream == NULL ) 
    return; 
fseek(stream, 0L, SEEK_SET ); 
while( !feof( stream ) ) 

fwscanf(stream, L"%s", WCHAR1); 
    fwscanf(stream, L"%f", float1); 
    fwscanf(stream, L"%f", float2); 
    fwscanf(stream, L"%f", float3); 

fclose( stream ); 
 
 GWES組件的功能有哪些? 
GWES不僅負責GDI、窗口、消息,還負責管理本機設備驅動程序,負責加載顯示、鍵盤鼠標、觸摸屏驅動程序,而且GWES本身包含電源、LED驅動程序。 
 
 如何在PB中預先設定好存儲內存和程序內存的大小,我想多劃分一些空間給程序內存?  
兩種辦法: 
1、在定製內核時在config.bib文件中設置FSRAMPERCENT = number,具體number可參考標題爲“FSRAMPERCENT ”的幫助文檔。這種辦法是修改內核的設置,所有一直有效。 
2、在應用程序中調用API SetSystemMemoryDivision,如果函數返回SYSMEM_CHANGED表示成功,如果返回SYSMEM_MUSTREBOOT表示需要熱啓動纔能有效。這種辦法需要每次啓動後調用API纔有效。 
 
 如何取消鼠標光標? 
通過取消SYS變量來實現此目的,在PB命令行下鍵入“set SYSGEN_CURSOR=”,然後回車確認。 
 
 EVC下調用TextOut如何編譯會出錯? 
類似這樣的問題很多,這是因爲EVC的幫助文檔內容有錯誤。可能EVC的幫助文檔內容是從桌面Windows幫助文檔複製過來的,所以很多API函數還有例子代碼都有錯誤,例如幫助文檔中包含一個API函數的說明,但是實際編譯的時候提示沒有這個API,有的例子代碼採用ANSI字符串,而WINCE的 API都是寬字符版本,造成直接複製過來編譯失敗。 
因爲MFC for WINCE的CDC類中沒有TextOut成員函數,所以編譯會出錯,可以用其它類成員函數ExtTextOut或者DrawText替換。 
 
 我如何將我的dll軟件讓現有的ce系統認可?儘管我也知道應該使用signfile.exe程序進行簽名,但是我並不知道那個ce系統認可的簽名應該是啥 
如果你說的WINCE系統內核已經加入了簽名認證機制,那麼沒有私鑰對你的DLL文件簽名肯定是無法運行在此內核中的,一般簽名密鑰的密鑰長度都是1024位,很難破解。 
 
 如果查看WINCE註冊表中的內容? 
兩種辦法: 
1、建立同步後,用EVC自帶的工具“Remote Registry Editor”打開查看。 
2、從網上下載註冊表查看工具,放到WINCE設備中。 
 
 調用directshow出現鏈接錯誤,如何解決?  
player.obj : error LNK2001: unresolved external symbol _IID_IVideoWindow 
player.obj : error LNK2001: unresolved external symbol _IID_IMediaControl 

 這是因爲鏈接器沒有找到合適的.lib文件。兩種辦法: 
1、在EVC菜單Tools—options—directories 裏把library files的路徑重新調整一下。如果你只安裝了EVC自帶的 Standard SDK而沒有其它SDK,可以指定WINCE目錄中的.lib文件路徑,例如D:/WINCE500/PUBLIC/DIRECTX/ OAK/LIB/X86/RETAIL。注意CPU的類型。 
 2、安裝SDK,前提是導出SDK的PB內核工程必須包括DirectShow或者其它組件。 
  
     在PB的config.bib文件中,“IMGFLASH”表示什麼意思呢? 
表示能夠刷NK到ROM中,具體請查看標題爲“IMG Environment Variables”的幫助文檔。 
 
 x86平臺如何映射各種地址空間?如何編寫中斷服務例程? 
如果是x86平臺,可以調用HalTranslateBusAddress轉換物理總線地址到物理系統地址,調用 HalTranslateSystemAddress轉換物理系統地址到邏輯總線地址,也可以不調用這兩個函數,因爲x86平臺除32位物理地址外還有 16位的IO地址空間,對於16位的IO地址空間,可以直接調用WRITE_PORT_UCHAR或者READ_PORT_UCHAR等函數直接讀寫端口。對於32位物理地址可以調用VirtualAlloc和VirtualCopy來映射。這樣做思路清晰,簡單明瞭。 
在x86平臺要實現ISR,有如下幾個步驟(以Geode BSP爲例): 
1、用SETUP_INTERRUPT_MAP宏關聯SYSINTR和IRQ。以“SYSINTR_”爲前綴的常量由內核使用,用於唯一標識發生中斷的硬件,又稱爲中斷ID。在Nkintr.h文件中預定義了一些SYSINTR,OEM可以在Oalintr.h文件中自定義SYSINTR。 
2、用HookInterrupt函數關聯硬件中斷號和ISR。這裏提到的硬件中斷號爲物理中斷號,IRQ爲邏輯中斷號。在InitPICs函數的最後調用了HookInterrupt函數,如下: 
for (i = 64; i < 80; i++) 
HookInterrupt(i, (void *)PeRPISR);  ///用ISR關聯16箇中斷號  
3、調用InterruptInitialize函數關聯SYSINTR和IST創建的事件對象,也是IST等待的事件對象。詳細內容請參考Geode BSP源碼。 
在x86 平臺要實現可安裝ISR,先調用LoadIntChainHandler函數註冊在註冊表中指定的ISR DLL,然後填充GIISR_INFO結構體並調用KernelLibIoControl函數將此結構體傳遞給可安裝ISR。詳細內容請參考WINCE幫助文檔或者我著的《Windows CE下驅動開發基礎》。 
 
 修改了WINCE自帶的驅動程序後如何編譯?如果是自己開發的驅動程序如何編譯? 
1、分爲IDE方式和命令行方式。 
IDE 方式的編譯很簡單,以PB5.0爲例,打開定製內核的工程,在左邊的“workspace”—“FileView”中找到你已經修改了的目錄,然後單擊右鍵彈出菜單,在菜單中選擇“Build and Sysgen Current Project”,這樣PB就會編譯指定的目錄中的項目源碼文件,然後執行sysgen命令根據source文件中的內容生成目標文件並複製到當前內核工程目錄下。 
命令行方式的編譯需要打開“Build OS” —“Open Release Directory”,以cd命令進入你已經修改的驅動程序目錄中,然後鍵入“build –cfs”,然後鍵入 “sysgen –p 項目名稱”,一般項目名稱爲source文件中的“TARGETNAME”。 
2、如果想完全自己開發驅動程序,建議直接採用EVC或者PB來編寫編譯。 
 
  開發PCI設備驅動時,InterruptInitialize函數的第一參數是否是PCI卡配置空間信息中的InterruptLine 參數?是否需要在HKLM/Drivers/BuiltIn/PCI/Template 加一個自己的 subkey,並填寫相應內容?如何填? 
1、InterruptInitialize的第一參數是IRQ,也就是邏輯中斷號,而不是物理中斷號,InterruptLine是指物理中斷號 
2、需要在template下加自己的PCI設備的信息,例如: 
   [HKEY_LOCAL_MACHINE/Drivers/PCI/Template/Serial] 
    "Dll"="Com16550.Dll" 
    "Class"=dword:07 
    "SubClass"=dword:00 
    "ProgIF"=dword:02 
    "VendorID"=multi_sz:"0AF0","B320","B320" 
    "DeviceID"=multi_sz:"0020","0300","0302" 
    "Prefix"="COM" 
而這些信息就來自於你執行pcienum.exe的結果。  
 
 編譯器報錯:error C2065: ''''CFileFind'''' : undeclared identifier,如何解決? 
MFC for WINCE版本沒有CFileFind類,所以要查找文件只能調用API FindFirstFile 和FindNextFile。 
 
 如何設置WINCE系統字體、字號?如何設置自己開發的軟件的字體、字號? 
1、系統字體通過註冊表設置。如下: 
[HKEY_LOCAL_MACHINE/System/GDI/SysFnt]   ///系統字體 
Wt=420 
Ht=18 
Nm=Arial 
[HKEY_LOCAL_MACHINE/System/GWE/Menu/BarFnt]   ///菜單欄字體 
[HKEY_LOCAL_MACHINE/System/GWE/Menu/PopFnt]   ///彈出窗口字體 
[HKEY_LOCAL_MACHINE/System/GWE/Menu]         ///菜單字體 
HKEY_LOCAL_MACHINE/System/GWE/Button          ///按鈕字體 
2、創建字體時把字體高度參數設置大點就可以了。如CFont::CreateFont(nHeight,...),也可以在LOGFONT結構中設置字體高度或者字體種類。如果是控件,調用控件的SetFont成員函數。如果是直接畫,在OnPaint響應函數中調用SelectObject選字體到DC。  
 
 nk.bin和nk.nb0有什麼區別? 
這裏提到的bin是一種二進制鏡像格式,以片斷(section)爲單位組織數據,每個片斷都包括一個頭,頭裏指定了起始地址,長度,校驗值。Platform Builder調用工具將WINCE內核所有文件以bin格式合併成一個文件,默認文件名爲nk.bin。BootLoader又以同樣的格式將nk.bin分解成多個文件放到RAM中。可以在命令行中鍵入 “viewbin nk.bin”來查看bin文件中具體包括了哪些內容。鍵入Cvrtbin命令轉換.bin格式文件爲.sre格式或者.abx格式。  
nb0格式是原始的二進制鏡像,它不包括頭,一般情況下將內核下載到設備的RAM中運行都採用nb0格式。要生成nbx格式的文件,需要在相關.bib文件中確定如下值:ROMSTART、ROMWIDTH、ROMSIZE。 
 
 在不採用硬件計時器的情況下如何創建更精確的計時器?最精確週期能否達到1毫秒? 
對於精確值的要求不同,所採用的辦法不同。以下闡述幾種辦法。 
1、在單線程中循環調用API Sleep函數,Sleep函數精確程度爲如果Sleep(N),那麼實際睡眠時間在N到N+1毫秒之間。而且還要注意調用Sleep的線程優先級的問題。如果任務過多並且此線程優先級低,那誤差就更大些。 
2、調用API QueryPerformanceCounter函數,舉例如下: 
LARGE_INTEGER  liFrequency; 
if (QueryPerformanceFrequency(&liFrequency))  // 查詢系統時鐘的頻率,這裏將返回1000 

   liFrequency.QuadPart /= 1000;    
   LARGE_INTEGER  liTimeOut;    
   if (QueryPerformanceCounter(&liTimeOut))   //得到截至到當前累計發生的系統時鐘中斷次數 
   { 
      liTimeOut.QuadPart += liFrequency.QuadPart;   ///計算下一秒到來時總的中斷次數是多少 
      LARGE_INTEGER  liCurrent; 
      do 
      {          
         QueryPerformanceCounter(&liCurrent);    // 循環查詢累計的的中斷次數 
      } while (liCurrent.QuadPart < liTimeOut.QuadPart); ///到達下一秒 
   } 

調用QueryPerformanceCounter同調用Sleep在本質上都是一樣的,都是在單線程中無限循環等到週期一到執行任務,相比較 QueryPerformanceCounter要比Sleep更精確些,越精確就越要求線程的優先級,保障線程能夠正常得到處理器。 
3、以上辦法難以保證週期精確到1毫秒並且WINCE系統穩定地運行,所以要從中斷入手。以x86平臺爲例,先在Timer.c中將默認的SetTimer0 (TIMER_COUNT)中的TIMER_COUNT /=2,SetTimer0函數負責設置系統時鐘的頻率,默認1毫秒發生一次中斷,如果除以2就是0.5毫秒發生一次中斷。然後在fwpc.c文件中修改ISR函數PeRPISR,因爲原來默認是1毫秒發生一次中斷,在處理INTR_TIMER0時系統負責累計計數、管理線程的調度,返回相應的SYSINTR值,而我們沒有辦法再添加代碼返回自己定義的SYSINTR值,所以現在要修改原來的處理代碼,例如設置一個BOOL型變量,TRUE就執行原來默認的代碼,而FALSE就返回我們自己定義的SYSINTR值,這樣即不影響原來的ISR處理,又加入了我們的中斷響應代碼。ISR返回我們定義的SYSINTR後WINCE內核激活相對應的EVENT事件,我們就可以在我們編寫的IST裏處理任務了。 
 
 flash中存放了BootLoader和內核鏡像,如何把剩餘flash部分劃分爲一個存儲區域供應用程序讀寫? 
以WINCE 提供的驅動(FAT文件系統和MSFLASH驅動)來舉例說明。如果採用默認common.reg中的註冊表設置,那麼MSFLASH驅動默認把整個 flash作爲存儲區域來讀寫,這不符合問題的要求,所以必須告訴MSFLASH驅動程序可供讀寫的區域的起始地址和長度。以下是一個註冊表例子: 
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/FASLD] 
    "Dll"="fasld.dll"        ///實際Flash存儲器的驅動程序 
    "Order"=dword:2       ///該驅動程序相對於其它驅動程序的加載順序 
    "Prefix"="DSK"        ///前綴 
    "Ioctl"=dword:4        ///IOCTL碼,設備管理器加載驅動的時候調用IOControl函數,傳遞這個IOCTL碼。 
"Profile"="MSFlash"    ///Profile名稱,也就是[HLM/System/StorageManager/Profiles/MSFlash] 
///當設備管理器加載此驅動程序的同時發送通知給系統,IClass(GUID)的值表明這是一個存儲設備的驅動程序。 
"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"    
    "MemBase"=dword:00000000    ///Flash中可供讀寫區域的起始物理地址,也就是Flash的首地址+偏移量 
    "MemLen"=dword:00000000     ///Flash中可供讀寫區域的長度 
[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/MSFlash] 
    "DefaultFileSystem"="FATFS"   ///MSFlash驅動默認採用的文件系統 
    "PartitionDriver"="mspart.dll"    ///採用的分區驅動程序 
    "MountAsRoot"=dword:1        ///此目錄作爲文件系統的根目錄 
    "Folder"="NOR Flash"          ///目錄名稱 
    "Name"="FLASH Disk Block Device"   ///Flash驅動名稱 
 "PartitionDriverName"="MSPART" ///分區驅動名稱 
 "AutoMount"=dword:1         ///自動裝載檢測到的分區 
    "AutoPart"=dword:1            ///自動分區 
    "AutoFormat"=dword:1          ///自動格式化分區 
[HKEY_LOCAL_MACHINE/System/StorageManager/AutoLoad/MSFlash] 
    "DriverPath"="Drivers//BuiltIn//FASLD"    ///Flash驅動在註冊表中的位置 
    "LoadFlags"=dword:1                 ///這個值可以被設置爲0、1、2。1表示同步加載,其它表示異步加載 
    "Order"=dword:0 
[HKEY_LOCAL_MACHINE/System/StorageManager/FATFS] 
    "FriendlyName"="FAT FileSystem"    ///文件系統名稱 
    "Dll"="fatfsd.dll"                   ///文件系統驅動程序 
    "Flags"=dword:00000064             ///標誌,詳見幫助文檔 
    "Paging"=dword:1                   ///是否分頁 
    "EnableCache"=dword:1              ///是否允許緩存數據 
    "CacheSize"=dword:0                ///指定緩存大小,0表示默認 

  
     驅動程序如何發通知給應用程序? 
這裏介紹一下常見的兩種辦法。 
1、驅動程序調用API SendNotifyMessage,發送特定的消息給應用程序,這就要求應用程序要有消息循環機制並且要事先做好消息的處理。參數 1爲窗口句柄,可以設置HWND_BROADCAST表示廣播消息。要注意的是不要在參數中傳遞指針(虛擬地址),因爲執行驅動程序的線程和應用程序並不在同一個進程空間中。解決辦法可以利用內存映射文件技術,比如在驅動程序中創建一個內存映射文件對象,申請一塊物理內存,然後把對象名稱和內存長度傳遞給應用程序,應用程序打開同名的內存映射文件對象,讀取裏面的數據。對象名稱可以事先協定好,也可以通過註冊表來傳遞,內存長度是32位值,通過消息參數就可以傳遞,也可以通過註冊表來傳遞。另外一種解決辦法是在定製內核時候預留一塊物理內存,這樣驅動程序和應用程序都可以通過VirtualAlloc和 VirtualCopy來映射到同一塊物理內存,其原理同內存映射文件技術一樣,但是這塊物理內存不具備通用性。最後一個辦法是應用程序事先將一個緩衝區地址傳遞給驅動程序,驅動程序調用MapPtrToProcess映射應用程序傳遞過來的地址,當驅動程序調用SendNotifyMessage後應用程序可以直接到該地址中讀取數據。 
設備管理器就是調用此函數廣播WM_DEVICECHANGE消息的。另外WINCE的一個例子程序RNAApp在撥號連接建立的時候也是調用這個函數廣播WM_NETCONNECT消息的。 
2、驅動程序調用API CeEventHasOccurred指明一個事件A發生,在此之前應用程序調用API CeRunAppAtEvent將驅動程序指明的A事件和一個應用程序名稱相關聯,或者和一個事件B相關聯。這樣當A事件發生時,如果指明和一個應用程序名稱關聯,那這個應用程序就會被啓動。如果指明瞭和一個事件B相關聯,那麼等待事件B的線程將被激活。如果想了解當前系統內部所有驅動程序支持哪些類似事件A的事件,調用 API CeNotifyPublic_FilterEvent,在該API的幫助文檔裏也列舉了常見的事件,例如 NOTIFICATION_EVENT_NET_CONNECT和 NOTIFICATION_EVENT_NET_DISCONNECT。 
 
 EVC創建的工程名稱如果用中文就出錯,該怎麼辦?  
用EVC創建的工程名稱如果爲中文將導致資源文件打不開和編譯出錯,可以改資源文件名稱爲英文,再編輯.rc文件中的資源文件名稱。但建議儘量不要用中文爲工程名稱。 
作爲習慣,應該在EVC創建一個工程後,立刻在“project”—“settings”中設置資源的語言屬性,然後在“resource view”中設置每個資源的語言屬性,這些工作做完後再修改資源就沒有問題了。有人詢問對話框的標題爲亂碼,其原因就是在沒有修改語言屬性的情況下設置標題爲中文。 
 
 WinCE下如何讀寫幾百兆的大文件呢? 使用內存映射文件嗎?  
一般嵌入式設備配備128MB物理內存就算頂級的了,所以要讀寫幾百MB的文件用內存映射文件技術是最好的選擇了。映射文件之後讀數據是非常容易的,要注意的是寫數據,內存映射方面的API沒有提供改變文件長度的功能,所以要在關閉映射文件對象後用文件API改變文件長度。 
 
 請問如何改系統調度的默認時間片值? 
更改schedule.c文件中的dwDefaultThreadQuantum 變量,然後重新編譯該文件並SYSGEN。調用API CeGetThreadQuantum就知道更改是否生效。 
 
 如何讓系統加載自己寫的驅動程序?  
兩種辦法: 
1、在[HKEY_LOCAL_MACHINE/Drivers/BuiltIn]下添加註冊鍵。 
2、在應用程序中調用ActivateDeviceEx。 
 
 在一些文件中用分號來表示註釋,例如下面的內容  
; @CESYSGEN IF SERVERS_MODULES_HTTPD 
; @CESYSGEN ENDIF 
在“CESYSGEN...”前加了“@”,有沒有什麼特別的含義? 
在WINCE的一些文件中,用“;”作爲註釋並在註釋文字中用@CESYSGEN作爲標記,後面接條件語句。Cefilter.exe工具負責按照條件來篩選文件內容,所以不要輕易地刪除包含@CESYSGEN的註釋語句。 
 
 通過串口建立ActiveSync聯接,串口線用三線的可以嗎?  
不可以,因爲用串口同步時要用到其餘口的狀態。 

  
     WINCE是否支持MAPI? 
不支持。WINCE自帶的pmail.exe軟件也不是很好用。建議自開發郵件收發軟件。如果需要購買WINCE下郵件收發軟件可以聯繫我。 
 
 如何旋轉屏幕顯示的內容? 
例子代碼如下(前提是顯示驅動程序支持旋轉): 
DEVMODE  devmode = {0}; 
 devmode.dmSize = sizeof(DEVMODE); 
 devmode.dmDisplayOrientation = DMDO_90;       ///垂直模式 
 devmode.dmFields = DM_DISPLAYORIENTATION; 
 ChangeDisplaySettingsEx(NULL, &devmode, NULL, 0, NULL);  ///改變顯示的設置 
 CRect  rcWorkArea(0, 0, 320, 240);    ///整個屏幕尺寸 
 ///設置客戶區大小並廣播消息,這樣所有軟件也就隨之更改顯示 
 SystemParametersInfo(SPI_SETWORKAREA, 0, (void*)&rcWorkArea, SPIF_SENDCHANGE);  
 
 請問如何修改字形緩存的容量? 
[HKEY_LOCAL_MACHINE/System/GDI/GLYPHCACHE] 
"limit"=dword:0400 
 
 如何得到從WINCE啓動開始到現在的時間? 
調用API GetTickCount,得到的值爲32位整數,單位爲毫秒。 
 
 如何調用WINCE的軟鍵盤? 
調用API SipShowIM(SIPF_ON),前提是內核加入了軟鍵盤組件。 
 
 基於HIVE的註冊表,如何在系統關閉前保存註冊表的數據到文件system.hv? 
調用API RegFlushKey函數。 
 
 使用VirtualAlloc和VirtualCopy的時候需要注意哪些事項? 
1、 VirtualAlloc的作用是申請虛擬地址空間,這肯定不是最終的目的,最終目的可能是申請物理內存、映射寄存器、提交文件等。沒有一個目的會在意虛擬地址空間的位置,所以儘量傳遞參數1爲0,也就是讓WINCE自動分配虛擬地址空間。VirtualAlloc分配地址空間實際上是以64KB爲單位,所以要指定申請的虛擬空間的首地址的話,參數1應該爲64KB的整數倍,申請的長度也應該爲64KB的整數倍,即使你不需要那麼大。 
2、 VirtualCopy的主要作用是映射物理地址空間,如果參數2爲物理地址,那麼最後一個參數要添加PAGE_PHYSICAL,參數2必須是256的整數倍。如果參數2爲虛擬地址(0x80000000以上),那麼最後一個參數就不要添加PAGE_PHYSICAL,WINCE內核會根據這個虛擬地址找到對應的物理地址。 
 
 驅動程序和應用程序之間傳遞數據時何時調用MapPtrToProcess? 
因爲設備管理器負責加載驅動程序DLL,這意味着當應用程序調用驅動程序接口函數的時候,WINCE內核會將調用驅動程序接口函數的線程轉移到設備管理器的進程空間然後執行具體的驅動程序代碼,應用程序和設備管理器處於兩個進程空間,這就造成設備管理器無法訪問應用程序傳遞的指針(虛擬地址),所以當我們在應用程序中傳遞指針給流驅動程序接口函數時,WINCE內核從中作了一個地址映射,例如ReadFile、WriteFile、DeviceIoControl函數的參數凡是指針都經過了映射才傳遞給驅動程序,所以很多驅動程序開發者並不瞭解其中的奧祕就可以編程了。但是如果參數是一個指向一個結構體的指針,而結構體裏包括一個或多個指針,那麼WINCE內核並不負責映射,所以就需要開發者在驅動程序接口函數中調用API函數MapPtrToProcess來映射地址。例如:pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());  
 
 如何判斷可插拔的設備是否存在? 
1、通過查找註冊表的值。凡是由API ActivateDeviceEx加載的驅動程序都在[HKEY_LOCAL_MACHINE/Drivers/Active]鍵下有註冊鍵,通過查找“name”或者其它鍵值就能夠找到。設備管理器就調用這個API。如果是PCI設備,在註冊表[HLM/Drivers/BuiltIn/PCI/Instance]下查找關鍵字,例如[HLM/ Drivers/BuiltIn/PCI/Instance/WaveDev1],說明音頻驅動已經加載。 
2、調用驅動程序接口函數,根據返回值或者執行結果來判斷。 
 
 如何做到通過串口過來的一個信號啓動自己開發的應用程序? 
創建一個線程負責等待串口過來的信號,調用API SetCommMask設置要等待的信號種類,具體可以等待的信號種類參見參數2的說明。然後再調用 API WaitCommEvent函數等待這個信號,接收之後再調用API CreateProcess啓動應用程序。 
 
 在WINCE中如何只能啓動應用程序的一個實例? 
常用的兩種辦法: 
1、如果應用程序實例創建了窗口,可通過API FindWindow函數通過窗口類名和窗口標題名稱來查找,前提是系統內不會出現窗口名稱重複的情況。 
2、應用程序初始化的時候創建一個事件或互斥等內核對象,因爲內核對象是由內核創建,名稱在系統內唯一。 
 
 能不能自己編輯一個數字簽名文件導入到手機上,這樣就可以用這個簽名籤自己的程序了? 
WINCE的內核簽名機制的用途是限制非法的可執行模塊EXE、DLL等在設備上運行。要求內核的加載模塊用公鑰驗證請求加載的EXE、DLL的簽名是否合法,而這個公鑰是在定製內核的時候加進去的,所以除內核的定製者以外的人無法修改這個驗證機制。 
 
 我按照版主的文章《加密WINCE系統》裏操作,提示錯誤如下: 
Error 80090016 during CryptSignHash 1! 
Error signing hash 
這是因爲傳遞了無效的鑰容器名稱,使CryptoAPI調用失敗。應該在使用signfile工具之前創建一個鑰容器,在桌面Windows中調用 API CryptAcquireContext創建一個指定名稱的鑰容器,接着再創建一個簽名密鑰對,這時再使用signfile工具就可以了。我在文章裏寫成-kfulinlin是因爲我創建鑰容器的時候沒有指定名稱,系統就採用當前登錄的用戶名爲容器名。 
 
 編譯錯誤:CVTRES : fatal error CVT1102: out of memory; 42 bytes required ? 
多數情況下出現這種錯誤是因EVC的bug而起,應該在安裝EVC之後就立刻安裝EVC的SP補丁。另外爲了避開BUG,使用EVC編程應該養成一些習慣,比如定期備份工程所有文件,每次編譯時採用Clean + Rebuild All,正調試時不要關閉模擬器等等。 
 
 在WINCE下是否能夠得到某一進程使用的物理內存總量? 
目前沒發現有這樣一個API能夠得到指定進程使用的物理內存總量。只有GlobalMemoryStatus能夠得到整個系統使用的物理內存總量。 
 
 應用程序如何控制lcd的亮度?如何獲得電池的電量? 
從常見的平臺如Geode、三星ARM系列來看,的確在驅動方面沒有統一的控制LCD或者其它種類屏幕亮度的接口函數,所以只能根據具體平臺提供的接口來做。從幫助文檔來看微軟的帶有DirectDraw功能的顯示驅動程序的確有標準的增加亮度的接口函數,關於背景光參見標題爲 “Enabling a Backlight”的幫助文檔。 
獲得電池電量有標準的接口函數GetSystemPowerStatusEx,前提是驅動程序和硬件都要支持。 
 
 WINCE的socket函數好像不支持發送/接收超時? 
是的,最早版本的WINCE支持選項SO_RCVTIMEO、SO_SNDTIMEO,後來卻不支持了。 
 
 WINCE下如何設置窗口最大化和最小化? 
WINCE 的幫助文檔在介紹API ShowWindow函數的參數時指出SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE,  SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE都不被支持,但實際上並不完全是這樣,具體來說: 
SW_MAXIMIZE            比原來窗口大,但不是最大化 
SW_MINIMIZE             編譯成功,但是不起作用 
SW_SHOWMAXIMIZED     最大化 
SW_SHOWMINIMIZED      編譯出錯 
SW_RESTORE              能恢復 
SW_SHOWDEFAULT        編譯出錯 
SW_SHOWMINNOACTIVE   編譯出錯 
SW_HIDE                  能夠隱藏 
 
 如何用程序調用控制面板的觸摸屏校對程序?  
兩種辦法: 
1、調用API TouchCalibrate函數 
2、調用CreateProcess,參數1爲L"//windows//ctlpnl.exe",參數2爲L"cplmain.cpl,9"。 
 
 如何獲得U盤或者其它類型的存儲器總容量和剩餘可用容量? 
調用API GetStoreInfo得到扇區數、每扇區字節數,相乘即是總容量。調用API GetDiskFreeSpaceEx得到剩餘可用容量。 
 
 三星2440頭文件定義#define IIC_BASE 0xB1400000 // 54000000,datasheet是54000000,那麼怎麼轉成0xB1400000? 
物理地址映射方法分爲兩種,一種靜態映射另一種爲動態映射。在OEMAddressTable中定義了物理地址與虛擬地址的映射關係屬於靜態映射,用 VirtualCopy映射屬於動態映射,採用哪種辦法都可以。問題中提到的屬於靜態映射,2440的BSP在map.a文件中定義了IIC控制寄存器的物理起始地址和對應的虛擬地址如下: 
DCD 0x91400000, 0x54000000, 1 ; 
在OEMAddressTable 中定義的虛擬地址範圍在0x8000 0000—0x9FFF FFFF,這部分可緩存,適合內核程序和應用程序使用,同時WINCE內核在 0xA000 0000—0xBFFF FFFF中映射了另一份,指向了同樣的物理地址,這部分不可緩存,適合驅動程序使用。三星ARM處理器帶有L1級高速緩存,可緩存會提高執行效率。對於特殊的設備寄存器適合映射到不可緩存的虛擬地址。 
當驅動程序調用VirtualCopy對0xB1400000地址讀寫時,WINCE自動將這個地址減去0x2000 0000,也就是0x91400000,對應的物理地址就是0x54000000,也就是IIC控制寄存器的物理起始地址。 
 
 基於RAM的註冊表如何保存數據? 
調用API RegCopyFile備份註冊表。調用API RegRestoreFile恢復註冊表,然後調用KernelIoControl熱啓動使恢復生效。 
 
 如何隱藏和顯示winCE下標準外殼的任務欄? 
 HANDLE  hTaskBar = FindWindow(L"HHTaskBar", NULL);  
 ShowWindow(hTaskBar, SW_HIDE);  
 ShowWindow(hTaskBar, SW_SHOWNORMAL);  
 
 如果能讓WINCE的IE瀏覽器播放flash動畫? 
播放flash需要Macromedia Flash Player SDK,參見http://www.adobe.com/products/flashplayer_sdk/。這和real player相似,都需要WINCE平臺的SDK,都需要申請。 
 
 WINCE下內核模式和用戶模式有什麼區別? 
爲了使讀者能夠詳細瞭解WINCE的地址映射原理還有兩種模式,在這裏我分幾個部分說明: 
1、 WINCE內核nk.exe的任務是管理操作系統核心功能。按照OEMAddressTable的映射要求,所有物理地址都映射到0x80000000以上,所以對於內核程序nk.exe和內核模式下的線程來說,只要訪問0x80000000以上的有效虛擬地址經MMU就能夠訪問物理地址,無需再映射是內核模式的一個特點。內核模式的第二個特點是沒有地址訪問限制,內核模式線程可以訪問任何有效虛擬地址,所謂有效虛擬地址是指有實際事物對應。 
2、用戶模式線程只能訪問0x80000000以下的虛擬地址空間,WINCE6.0之前版本的內核爲每個進程劃分32MB的地址空間,在不調用特殊函數的情況下不能相互訪問,這樣的設計使得WINCE系統更安全、更穩定,限制訪問地址是用戶模式的第一個特點。第二個特點就是需要多一層映射,如果線程要訪問物理內存的話需要先映射到0x80000000以上,再經MMU訪問物理內存地址。 
WINCE的線程具有轉移性(參考 API GetCallerProcess的說明,有一個很好的例子),當應用程序的線程調用API或者調用驅動程序接口函數時,該線程會轉移到 gwes.exe、device.exe、filesys.exe等進程中執行,轉移是由WINCE內核操作的,它會修改線程的上下文,記錄線程的當前進程、調用者進程、擁有者進程三個值。 
3、如果在定製內核的時候選擇了“Full Kernel Mode”,那麼在這個內核上運行的所有線程都處於內核模式,即使調用SetKMode(FALSE)後線程仍然具有內核模式的特點,能夠訪問任何有效的虛擬地址。假設現有一個64MB RAM的 WINCE產品,RAM映射從0x80000000到0x84000000,如果線程處於內核模式,它就直接可以訪問這個範圍的虛擬地址: 
在OnButton1()中編寫 
DWORD oldMode = SetKMode(FALSE); 
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000) 
*piTemp = 12345; 
在OnButton2()中編寫 
DWORD oldMode = SetKMode(FALSE); 
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000) 
int iTemp = *piTemp; 
先只執行OnButton1()然後關閉程序,再重啓程序然後執行OnButton2(),iTemp仍然等於12345。結果說明了兩點:內核模式線程可以直接訪問0x80000000以上的有效虛擬地址;我們寫到RAM中的數據沒有丟失,說明虛擬地址有效。 
如果在定製內核的時候沒有選擇“Full Kernel Mode”,那麼在這個內核上運行的所有線程都處於用戶模式。可以調用SetKMode(TRUE)使調用線程暫時處於內核模式,還是原來的假設環境,我再舉個例子: 
在OnButton1()中編寫 
DWORD oldMode = SetKMode(TRUE); 
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000) 
*piTemp = 12345; 
在用戶模式下,如果不調用SetKMode(TRUE),那麼執行*piTemp = 12345一定會彈出對話框,提示地址訪問非法,如果調用SetKMode(TRUE)就不會提示地址訪問非法,而且在OnButton2()中仍然能得到12345這個值。 
通過這兩個例子我相信讀者能夠完全瞭解兩種模式的區別了。 
4、 WINCE提供了兩個函數SetKMode和SetProcPermissions,其中SetKMode能夠把調用線程切換到內核模式,還可以切換回用戶模式。SetProcPermissions + GetCurrentPermissions添加當前進程訪問權限給調用線程, SetProcPermissions (0xFFFFFFFF)能讓調用線程訪問所有進程空間,但是調用線程仍然處於用戶模式。SetKMode和 SetProcPermissions函數使得用戶模式的特點不那麼明晰。 
如上所說一個應用程序的線程可能轉移到其它兩個進程地址空間中讀寫數據,而每一個線程在被創建的時候只有訪問創建它的進程地址空間的權限,所以驅動程序開發者必須在驅動程序讀寫數據前調用SetKMode或者 SetProcPermissions增加調用此函數的線程訪問其它進程空間的權限。如果一個應用程序的線程只轉移到一個進程地址空間,一般爲設備管理器進程device.exe,這種情況下不必增加線程訪問其它進程空間的權限,但如果驅動程序本身創建了一個線程,那還是要調用SetKMode或者 SetProcPermissions增加新的線程訪問其它進程的權限的,因爲驅動程序創建線程時,當前進程爲設備管理器,所以新線程只具有訪問設備管理器進程空間的權限,而不具備訪問應用程序進程空間的權限。 
5、可能一個編寫過簡單的流驅動的初學者會很疑惑,因爲開發一個簡單的流驅動程序根本不需要調用這些函數,也沒有調用過MapPtrToProcess,那是因爲如果標準流驅動接口函數的參數爲指針(ReadFile、 WriteFile、DeviceIoControl參數都有指針),WINCE內核會自動映射指針包含的地址,但僅此而已,其餘任何情況都要求開發者自行處理,比如流接口函數的參數是一個指向結構體的指針PA,而結構體中包括指針PB,PB指針就必須在流接口函數中映射,映射後才能訪問,否則就會造成地址訪問非法。所以結構體中每個指針都要映射。 
爲了讓讀者能瞭解其中的原因,我舉個例子: 
假設設備管理器被加載到Slot4,應用程序A被加載到Slot 8,A只有一個主線程T,T開始執行,按照WINCE的規定,正獲得CPU的進程必須映射到Slot0,那麼在執行代碼的時候 A的所有虛擬地址都被減去一個偏移值,也就是8×0x02000000,A調用DeviceIoControl,傳遞一個指向一個結構體的指針B,而這個結構體中包含一個指針C,指針C包含的地址假設爲0x00030000,當執行DeviceIoControl時WINCE把設備管理器的進程地址空間映射到Slot0,因爲放在註冊表[HKLM/Drivers/BuiltIn]下的驅動程序是由設備管理器加載的,自然驅動程序的代碼段被加載到設備管理器進程空間,但是線程仍然是T,此時T的當前所在進程爲設備管理器(CurrentProcess),A變成了T的調用者進程(CallerProcess),T自動具有了訪問調用者進程空間的權限。這時訪問Slot0中的虛擬地址其實質就是訪問設備管理器的進程地址空間,要把地址加上一個偏移值,也就是4×0x02000000,所以DeviceIoControl訪問指針C包含的地址時本應該加上8×0x02000000,卻加上4×0x02000000,結果地址並不是設備管理器的合法區域,系統就會提示地址訪問非法。而如果做了一個映射,指針C包含的地址就會被加一個正確的偏移值,使地址處於A的地址空間Slot 8中,T此時具有訪問A進程空間的權限,訪問到正確的虛擬地址當然會得到正確的數據了。 
 
 爲什麼WINCE目錄下的例子用build+sysgen能夠編譯成EXE文件,而我添加的例子就不能編譯呢? 
如果這個例子是一個應用程序,那麼肯定包括代碼文件(.h .c .cpp)和資源文件(.rc和其它資源文件),build工具根據source文件內容把代碼文件編譯成lib文件,資源文件編譯成.res文件,sysgen工具根據makefile文件內容將source文件中列出的需要鏈接的各個庫文件合併成一個EXE文件。所以說關鍵在於makefile文件,WINCE目錄下凡是能夠用build+sysgen編譯的都在makefile中有如何鏈接的設置,而我們添加的例子當然沒有在makefile中找到如何鏈接的設置,nmake工具就會提示不知道如何創建。 
 
 pcienum.exe幹什麼用的? 
如果你要開發某一個PCI設備的驅動程序,首先要知道這個PCI設備的信息(如VendorID、DeviceID、BaseClass、 SubClass)和PCI總線的信息。運行這個pcienum.exe就能得到相關信息。pcienum.exe提供了源碼,位置/Public/ Common/Oak/Drivers/Ceddk/Test/Pcienum。 
 
 wince下如何讓操作系統進入待機模式?又如何把它激活? 
通過註冊表就可以設置,前提是你的驅動和硬件都支持。註冊表項參見標題爲“GWES Suspend Time-outs”的幫助文檔。 
[HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Power] 
    "BattPowerOff"=dword:300 
    "ExtPowerOff"=dword:0 
    "WakeupPowerOff"=dword:60 
    "ScreenPowerOff"=dword:0 
 
 現有一個GPRS模塊,如何通過GPRS連接到Internet? 
1、先在內核中加入WAN下面的幾個組件,如RAS/PPP、TAPI。WINCE採用unimodem驅動,所以不必擔心沒有Modem驅動的支持。 
2、WINCE啓動後新建一個撥號連接,比如名稱叫“gprs1”,輸入用戶名、密碼、電話號碼。電話號碼不同,所採用的模式不一樣,例如“*99#”是GPRS模式,“17201”是普通的數據模式,速度差很多,價錢也差很多。 
3、開始連接,連接過程會在對話框中顯示,直到顯示“連接成功”。 
4、打開瀏覽器或者自己開發的通訊軟件測試網絡連接情況。 
5、關閉連接。 
6、保存[HKEY_CURRENT_USER/Comm/RasBook/gprs1]下的所有數據,添加到project.reg中,重新編譯後內核中就有了一個撥號連接“gprs1”。 
7、調用RAS函數可以修改撥號連接“gprs1”的參數,如用戶名、密碼、電話號碼,但是不能修改硬件設置,如波特率、串口、數據位、停止位等。RAS函數還能夠撥號、掛斷。爲了修改波特率可以多保存幾個撥號連接,也可以直接調用TAPI開發撥號軟件,另外WINCE自帶的撥號連接是有源碼的,位置在/ PUBLIC/COMMON/OAK/DRIVERS/NETSAMP/CONNMC。 
 
 採用基於HIVE的註冊表如何刪除用戶保存在註冊表中的數據,恢復到出廠時的註冊表? 
用戶修改的數據保存在user.hv文件中,直接刪除一定失敗,所以不能通過刪除文件實現恢復出廠設置。微軟考慮到了這個問題,在WINCE啓動過程中 filesys.exe加載註冊表時會調用OEMIoControl函數並傳遞一個IOCTL,這個IOCTL在pkfuncs.h中定義如下: 
#define  IOCTL_HAL_GET_HIVE_CLEAN_FLAG  CTL_CODE(FILE_DEVICE_HAL, 49, METHOD_BUFFERED, FILE_ANY_ACCESS) 
filesys.exe 會分別傳遞參數HIVECLEANFLAG_SYSTEM和HIVECLEANFLAG_USERS,如果返回值爲TRUE那麼filesys.exe清除原來的註冊表文件,如果返回值爲FALSE那麼filesys.exe保留原來的註冊表文件。默認WINCE並沒有實現這個IOCTL,所以OEM要刪除註冊表文件就必須先編寫這個IOCTL代碼。代碼的例子可參考標題爲“IOCTL_HAL_GET_HIVE_CLEAN_FLAG”的幫助文檔。另外必須在ioctl.h和ioctl.c兩個文件中編寫該代碼。在ioctl.c文件中找到 const OAL_IOCTL_HANDLER g_oalIoCtlTable[],添加IOCTL和對應的處理函數。要進一步瞭解這個全局數組,參見標題爲“IOCTL Library”的幫助文檔。 
 
 如何在不刪除必要組件的前提下減小內核文件長度? 
要減小內核文件長度首先要在使用PB的定製內核嚮導中選擇自定義,也就是說對於每個組件都由自己來選擇,而不是選擇PB的標準配置。但減小內核文件長度最有效最直接的辦法是縮小字體,尤其對於東亞字體,採用字體壓縮技術並且選擇合理的字庫文件將明顯縮小文件長度。 
1、在定製內核時選擇AGFA AC3 Font Compression組件。SYSGEN變量爲SYSGEN_AGFA_FONT。 
2、參考標題爲“East Asian Font Versions”的幫助文檔,從中選擇你需要的字庫文件加到內核中,從文檔可以看出加AC3壓縮比不加壓縮在文件長度方面差距很大。 
 
 如何得到WAV文件播放的總時間? 
1、直接讀取wav文件頭信息,從文件起始地址偏移28個字節長度爲4個字節保存的是每秒鐘播放的字節數,從文件起始地址偏移40個字節長度爲4個字節保存的是聲音數據的總的字節數,相除就是播放時間。 
2、調用IGraphBuilder::RenderFile打開一個wav文件,然後通過IGraphBuilder得到IMediaSeeking指針,再調用IMediaSeeking::GetDuration得到總的時間(結果要除以10000000),IMediaSeeking:: GetCurrentPosition得到當前播放時間。 
 
 如何在Dialog-Based程序中加入menubar? 
先調用CommandBar_Create再調用CommandBar_InsertMenubar。 
 
 請問MultiByteToWideChar與_T、L、TEXT的區別? 
MultiByteToWideChar函數轉換的對象可以是常量也可以是變量。其它只能轉換常量。_T和TEXT會根據當前系統是否定義_UNICODE宏來決定是否轉換,而L就是轉換成寬字符,當然也包括其他類型常量的轉換。 
 
 在用UBS線纜通過ActiveSync同步有效的情況下,如何插上USB線纜後WINCE自動與PC同步? 
1、新建一個撥號連接,假設名稱爲“usb1”,選擇連接類型爲“直接連接”,並在連接設備裏選擇通過USB線纜連接。 
2、將註冊表[HKEY_CURRENT_USER/Comm/RasBook/usb1]下的數據添加到project.reg或者platform.reg中。 
3、在[HKEY_CURRENT_USER/ControlPanel/Comm]下添加如下: 
"AutoCnct"=dword:1  ///直接連接 
"Cnct"="usb1"       ///連接名稱 
4、重新編譯內核。爲了節省編譯時間也可以在內核工程下搜索*.reg文件,將2、3步驟中的註冊表數據添加其中,然後直接make image。 
 
 如何通過進程句柄來獲得該進程的主窗口句柄? 
好像沒有API能夠通過進程句柄直接獲得主窗口的句柄,因爲並非每個應用程序都帶UI。但是可以反過來,先枚舉當前系統所有主窗口,然後根據每個窗口的句柄調用GetWindowThreadProcessId函數得到進程的ID,再調用OpenProcess得到進程句柄,與現有的進程句柄比較。 
 
 我做的顯示驅動DLL已經編譯成功了,但是在加載顯示驅動的過程中彈出話框,提示如下: 
unhandled exception  in  gwes.exe  (0xc0000005 access violation) 
提示的錯誤——地址訪問非法,表明你的驅動程序代碼並沒有在讀寫數據前添加SetKMode(TRUE)或者SetProcPermissions (0xFFFFFFFF)函數讓線程能夠訪問任何進程的地址空間。你可以調用 IsBadReadPtr和IsBadWritePtr函數檢測地址是否能夠合法訪問。編寫和gwes有關的驅動程序應該首先調用SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF) 函數,這是一個好習慣。 
 
 請問在嵌入式系統中如何設置GPRS拔號用的APN? 
對一個撥號連接比如“我的連接”單擊鼠標右鍵,在彈出的菜單中選擇“屬性”,然後單擊“配置”—“撥號選項”,在“附加設置”中添加AT命令如“+cgdcont=1,"ip","cmnet"”。“cmnet”位置即爲APN。 
 
 WINCE的IP Phone功能如何? 
WINCE的voip需要c-s-c結構,既需要服務器的中轉,而skype採用第三代p2p技術就不需要中轉,但是在gprs下也做不到語音流暢。skype有pocket pc版本,但是無線方面需要wlan或者cdma。 
 
 三星ARM平臺如何定義自己的中斷ID? 
以S3C2410 爲例,在oalintr.h文件中定義中斷ID,也稱SYSINTR,例如 #define SYSINTR_MYINT   (SYSINTR_FIRMWARE+20),最大值不能超過SYSINTR_FIRMWARE+23。然後在armint.c文件中找到 OEMInterruptHandler函數,用if (IntPendVal == INTSRC_XXX) 判斷當前發生的中斷源號,然後返回 SYSINTR_MYINT。內核分別調用OEMInterruptDisable(禁止當前中斷)、OEMInterruptDone(中斷處理結束)、OEMInterruptEnable(當前中斷有效)三個函數,參數都爲中斷ID,在這三個函數中用 case SYSINTR_MYINT判斷當前要處理的中斷。 
 
 如何開發軟件從PC端複製文件到基於WINCE的設備? 
調用RAPI (Remote Application Programming Interface)函數,此函數集由桌面計算機調用,由基於WINCE的設備執行。一旦連接上就可以在桌面計算機端調用RAPI。通過註冊表還可以限制RAPI能夠訪問目錄的範圍。具體參考RAPI和RDP(遠程桌面協議)。 
 
 請問如何對NandFlash分區、格式化? 
你看看WINCE420/PUBLIC/COMMON/OAK/DRIVERS/ETHDBG/BOOTPART/bootpart.cpp,在Eboot中先要調用BP_LowLevelFormat( 
DWORD dwStartBlock,  DWORD dwNumBlocks, DWORD dwFlags)再flash的一個區域建立空的MBR,然後連續兩次調用 BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType,  BOOL fActive, DWORD dwCreationFlags)函數來建立BINFS和FAT分區。建好後,將nk.bin燒入binfs 分區中。 
 
  要做個彈出對話框具有 always on top 屬性,如何實現? 
調用SetWindowPos(.. , HWND_TOPMOST, ...., SWP_NOACTIVATE)。 
 
 s3c2410+WINCE下網絡PING一會就斷,如何解決? 
原因在於中斷處理程序把已經產生的中斷標誌清除掉了,這樣就丟失一次中斷。因爲原驅動裏配置中斷爲上升沿觸發,一次中斷丟失就導致不會再產生中斷信號跳變,因爲只有在中斷服務中讀取了cs8900的 Interrupt status queue寄存器後,纔會產生下一次中斷!解決辦法: 
1、在cfw.c文件中全局定義BOOL Inited = FALSE 
2、修改OEMInterruptEnable()中case SYSINTR_ETHER: 下面的語句爲: 
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;  
break;  
注:本解決辦法轉載於http://stoned.blogchina.com/stoned/3083045.html,非我本人研究成果。 
 
 已經搜索到文件,如何用CListBox以圖標形式顯示出來? 
CListCtrl        ListCtrl; 
CImageList      ImageList; 
ImageList.Create(IDB_BITMAP, 48, 2, RGB(0,0,0)); 
ListCtrl.SetImageList(&ImageList, LVSIL_NORMAL); 
ListCtrl.InsertItem(iListIndex, strItem, 1);  
 
 如何改變控制面板中電源屬性對話框的尺寸?  
1、需要修改對話框的尺寸是因爲對話框是以資源方式加載的,不會根據當前系統顯示分辨率而自我調節尺寸。 
2、安裝WINCE後有一些組件(feature)的資源文件*.res就已經有了,如果你不改變,那麼build內核的時候PB只是把這些.res複製到工程目錄下,然後與*.obj合併成EXE、DLL、CPL。所以修改了.rc文件裏面的對話框尺寸後要重新編譯.rc文件爲.res文件,然後再覆蓋原來 WINCE自帶的.res文件。 
3、改變對話框尺寸有兩種辦法:一種方法是更改系統字體字號,系統字體的字號變化會影響對話框的尺寸,但是缺點是所有系統字體有關的UI都會改變。另一種是在.rc文件中調整對話框尺寸,然後編譯成.res文件,再將.res複製到對應的語言目錄裏,比如目錄名爲0804(中文),再執行Rebuild命令重新編譯內核,或者執行sysgen+build。在研究中我發現.res文件雖然能夠直接用EVC打開、修改、保存,但是和其它Obj鏈接成EXE、DLL、CPL後並不能運行,所以還是建議讀者用CE自帶的rc工具編譯最好。讀者可在PB的命令行中鍵入“rc /?”瞭解rc.exe工具的用途和參數。 
 
 使用EVC build之後連接模擬器的時候,提示download file等了一會又出現download failed? 
一般這樣的問題從下面幾個步驟解決: 
1、如果之前能啓動模擬器而現在不能,那麼先clean然後重啓計算機再build。 
2、如果開發的主機爲WINXP+SP2,可能存在與EVC模擬器不兼容的情況,檢查C:/boot.ini,將/noexecute=optin改爲/execute=optin。 
3、檢查你的模擬器是否能運行,假設你正用的SDK名稱爲MYSDK,單擊菜單tools—configure platform manager,選擇 MYSDK—MYSDK emulator,再單擊properties—test,看看模擬器是否能夠啓動,如果能啓動那問題就不大。 
4、單擊菜單build—update remote output files,看看模擬器是否能夠啓動。 
5、如果上述辦法均不行,關閉EVC然後重新建立一個新的工程,編譯,看看模擬器是否能夠啓動,如果能啓動說明原來工程出了問題,最好恢復原工程的備份。 
 
 如何設置能夠自動撥號、禁止自動撥號? 
在[HKEY_LOCAL_MACHINE/Comm/Autodial]下是自動撥號的註冊表設置。 
Enabled=DWORD:1          ///是否能夠自動撥號 
FailRetryWaitMS=DWORD    ///如果失敗再次撥號的等待時間 
RasEntryName1= REG_SZ     ///自動撥號採用的撥號連接名稱 
更多細節請參考標題爲“Auto Dial Registry Settings”的幫助文檔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章