Windows CE Development Notes/zh TW
This article applies to WinCE only.
See also: Multiplatform Programming Guide
│
English (en) │
中文(臺灣) (zh_TW) │
為您的程式進行偵錯
請參考[這裡]的說明 (中文)。
- 有時候GDB程式可能會死掉,請直接把偵錯程式停止,然後重來一次吧。
- 如果您打開了呼叫堆疊(call stack)子視窗的話,他會占用很多資源,所以當您進行偵錯的時候速度會很慢,有時候也可能讓偵錯程式死掉,所以請注意,沒必要的時候不要打開這個子視窗。
- 請在您需要中斷程式的部分設定breakpoint(中斷點),否則您可能沒辦法在需要程式暫停的時候讓它乖乖停止。
- 除非是要作偵錯,不然請別對您的程式進行反組譯或逆向工程,因為這些strip的工具程式或多或少都有些問題,拆解過以後,您的程式可能會沒辦法執行,所以不建議您使用它。
- 有時候單步執行會用很久的時間,要請您多準備些耐心,我曾經要透過偵錯程式做個簡單的單步執行,結果就花了一分多鐘…
資料存取不一致的問題
在使用ARM處理器的時候,有時會遇到EBusError的另外狀況,當時的訊息可能是資料存取不一致 (misaligned data access),以下的幾個章節會說明它發生的原因跟解決的方法。
甚麼是”資料存取不一致”?
假設CPU到記憶體的匯流排(Bus)是8bit,而您想讀取一個Byte。既然您的Bus是8bit的,就可以對每一個Byte的定址定的清清楚楚,那麼一次讀取一個byte就一定沒有問題。現在請想像一下,一個使用32bit匯流排的CPU要讀取一個Byte。32bit的匯流排,每次讀取的資料都會是一致的長度,也就是32bits (=4bytes),定址的時候也是一樣,都是以四的倍數進行定址的,所以要得到CPU真正想讀取的資料時,CPU就會把右邊的byte進行搬移。
好的,現在我們再想想,在讀取一個32bits的整數時,在一個8bits的CPU上面,必須要讀四次,每次讀一個byte,才能讀到一個32bits的整數,在32bits的CPU,如果定址也正好是4bytes的倍數,則相同的動作只要讀一次就行了。如果不是的話,就得把32bits整數分拆成不同的部分讀取,然後再組合成一個整數,在x86系列的CPU上面就是這麼作的,所以是比較沒效率的。 在SPARC跟ARM系列CPU的一些分支上面(*)就不支援這種分拆讀取再組合的動作,它們會直接回報出一個匯流排錯誤,錯誤訊息就是”資料存取不一致”。
(*)有些以ARM為基礎的CPU已經作了功能補強,把這問題給處理掉了,但這要看是哪個製造商製造的ARM處理器,還有它們的規格書上面是否有註明已經作了這問題的修補。
如何解決這個問題呢?
在對指標進行轉型(typecasting)的時候,一定要很小心。16bit (2bytes)長的資料跟32bit (4bytes)長的資料一定要在記憶體裡面轉成4bytes長的資料,不然這個bus錯誤就會發生了。 或者在使用到這樣的轉型動作之前加上一個新的關鍵字 (unaligned),這個關鍵字可以同時放在一個語法描述的左邊跟右邊。
例如:
var p1:^Longint; l:longint; begin p1^:=20; //這個assignment可能會造成bus error, 如果p這個指標正好指向了一個沒對齊的記憶體位址,例如: 該位址不是四的倍數。 unaligned(p1^):=20; //這樣就沒問題 l:=pl^; //這也可能會造成問題 l:=unaligned(pl^); //加個關鍵字就保險了 end.
在使用Pascal的自訂形別:Pack record時,編譯器會自動把對該record的所有成員的所有動作都定義成unaligned存取。目前,有時在循環使用到pack record的時候(有人翻成巢式定義),編譯器不會自動把需要的程式碼建立出來,所以這時候我們就得自己加上unaligned關鍵字了。 但是請留意啊! 循環使用pack record來定義新資料結構,而且對它所有成員的存取都加上unaligned時,會對程式處理資料的速度造成很大的影響。 所以非必要時,別這麼用吧。
實現分頁控制項(Tab Controls)的細節(TPageControl)
絕大多數的控制項在移植到WinCE的時候,都是直接把Win32的原始碼拿來稍微改一改而已,但是分頁控制項在WinCE上面要實現,比Win32平台上難得太多了。
簡單的說,有以下這幾個難處:
- 不支援縱向文字,所以分頁控制項的分頁標籤如果放在左邊或右邊,文字就沒辦法顯示了。
- 由於還沒釐清的原因(是WinCE的錯誤嗎?),分頁控制項的分頁標籤放在畫面上方的時候,分頁標籤上的文字也不會顯示出來。
此外,微軟也建議在Windows CE裡面使用分頁控制項時,使用分頁標籤位於畫面下方的樣式,這樣使用者在用手切換分頁的時候就不會遮到觸控螢幕了。綜合上述援因,我決定把Windows CE版本的分頁控制項寫死成只有一個樣式,這是跟其它控制項最大的不同。 預設的樣式看來有點老式,但我發現新的樣式太簡化了。雖然樣式有點老式,但您還是可以一眼就看出它是個分頁控制項,如果您想設定分頁控制項的樣式(設定為平滑樣式: flat)的話,請從程式碼裡面直接控制(TWinCEWSCustomNotebook.CreateHandle),要改變它的外觀也並不難。
您也可以從這裡參閱相關文件: http://msdn2.microsoft.com/en-us/library/aa921319.aspx
背景跟表單位置的問題
每一頁的背景之間是不會自動被更新的,且因為控制項的背景邊框很細,而且並不完整(不是每一邊都有邊框的),有時使用者就可以這樣看到表單的背景了。
我使用了個不錯的方法,把WinCE的表單背景作了些處理,細節請見: http://www.pocketpcdn.com/forum/viewtopic.php?t=499
在沒有進行表單位置調整前,分頁控制項看起來像這樣:
調整完以後,看起來會像這樣:
請注意,在沒有調整之前,您可以看到控制項後面的表單背景,但是在調整過以後,很明顯的就看不到了,因為我使用了TPanel控制項來調整控制項的位置。
我不確定用固定的背景色是不是個好主意,或者應該用哪個固定的顏色,所以我把這個問題留到現在還沒解決。
仍在偵錯中的項目
已知的問題跟錯誤
SetProp - GetProp - RemoveProp APIs
Windows CE沒有SetProp跟GetProp這兩個很常用的API,所以我已經自己製作/模擬了類似動作。 目前我製作的動作跟Win32 API並不完全一樣,在Win32裡面,我們可以對每個視窗,一次設定多個不同名稱的屬性,但我在實現這功能的時候,把名稱給省略了,所以我們只能對每一個控制項中,指定的同一類屬性進行設定。(要改成跟Win32的作法一樣也不難,但我發現目前這麼作沒甚麼幫助) 而removeprop這個功能也還沒被使用過。 所以在您離開應用程式的時候,可能發生記憶體裂縫。(我不確定WinCE是否會自己釋放記憶體空間,或者在Windows Handle被釋放時同時釋放記憶體)這部分會很快就被實現的。
LCLControlSizeNeedsUpdate LCLBoundsToWin32Bounds LCLFormSizeToWin32Size GetLCLClientBoundsOffset GetLCLClientBoundsOffset
這部分我還沒驗證過,我也不覺得這幾個元件會像我們在Win32平台上那麼需要它們。所以還需要重新設計,舉例來說TWinCEWSWinControl.SetBounds,我就把裡面的movewindow給拿掉了,因為在WinCE上面也不知道能把它移到哪兒去。
Brushes
在WinCE裡面也沒有Brush,我已經把CreateBrushIndirect直接對應到CreateSolidBrush跟CreateDIBPatternBrushPt了,如果在LCL裡面用到其它的flag的話,就會出錯喔。
其它
一些有用的資訊:
External signal(?) Error
有時候您可能會在偵錯程式裡面看到這個訊息,表示出現了”資料存取不一致”的問題,正常狀況下應該不會發生的。 您可以在出錯的原始碼指令檔裡面加入”unaligned”這個關鍵字,這個錯誤會發生在所有可能出問題的地方。
Menus
這需要大規模的工程,需要對LCL如何處理Menu的背景知識有稍微深入的了解,或許還需要稍微修正一下該架構。
未來的工作
- 要確認WinCE版本的WinAPI相關描述,目前使用到的部分跟Win32還大致相同,僅有少數情形是不同的。
- 目前用Lazarus跟WinCE介面編譯出來的應用程式都是經過最佳化的,並且只能在PocketPC裝置裡工作。
連結
以下是建立Windows CE介面時,有幫助的幾個連結: 如何將您的Win32程式移植到Windows CE
一些舊文章,說明在建立WinCE控制項的時候需要使用到的參數
- 初版正體中文翻譯,以及Device Emulator 2.0等相關資訊,由元智大學資訊傳播學系兼任講師張子仁 (Dennies Chang)製作。 - 2008/1/7 User:Dennies