一、基礎補充
1 常用控制項的使用提示
◇label中的多行
label1.caption := 'abcd' + #10#13 + '1234567';
◇label的右對齊輸出
label1.Alignment := taRightJustify; label1.autosize := false;
◇memo中設置捲軸
memo1.scrollbars := ssBOTh;
◇radiogroup的用法
不能把radio控制項放在radioGroup堙CradioGroup本身就是“全能”的。
with radioGroup.items do begin
add("yellow"); add("blue");add("green");
add("white");
end; radioGroup.Columns := 2;
radioGroup.itemIndex表示當前哪個按鈕被選中。
◇TStringGrid的用法
mouseToCell(x,y,aCol,aRow);用來把一個x,y座標轉換成單格的行和列號。
◇主功能表和彈出功能表
兩種功能表都可以在設計階段進行定制。caption中的短橫線表示分割線。滑鼠右鍵|new submenu 用來創建自功能表。使用彈出式功能表時,popup(x,y)方法需要的參數是關於螢幕的座標,而我們一般取得的都是關於客戶區的座標,需要一個轉換,使用 clientToScreen可也。
◇使用TAction
我們經常遇到同一個動作有多個操作途徑的情況。比如使用功能表,快捷按鈕,快捷鍵都是對應了同一個動作。對這個動作可能有許多種狀態,比如:禁止,隱藏等。我們希望只在一處寫代碼,就會使所有使用的地方都跟隨這個特性。這實際上是提醒我們使用一種文檔-視圖的結構。
delphi為我們提供了TAction來實現把多個物件連接在一個動作上。
使用多頁
控制項很多的時候,無法在一個form上放置,我們可以使用多頁控制項,把眾多的控制項放在不同的頁上。
2 多表單的關係
主表單
第一個創建的表單是主表單。主表單的關閉導致整個程式的關閉。每個表單在創建的時候,可以指定一個owner,它的任務是負責在自己釋放的時候,把這個表單也一起釋放。
功能表project|options|forms頁|main form選項可以指定哪個表單是主表單。
動態創建與預創建
在缺省的情況下,我們所選擇的多個表單都是預先創建好的。我們還可以在使用的時候,才去創建表單。這需要在project|options|forms中把auto createform移到右邊。自己創建表單的語法是: aForm := TForm4.create(self); self,即釋放管理者,也可以直接填application。
◇即便指定了owner,我們仍然可以主動地釋放表單: aForm.free;
關閉和隱藏
一般情況下,我們關閉一個表單的時候,實際的動作是隱藏,表單並沒有真的被從記憶體中清除。如果我們希望把表單釋放掉,就要顯式地調用 aFrom.free;基於這個原因,如果我們把表單的屬性visible設置為false,或調用表單的方法hide 可以獲得同樣的效果。
典型的登陸窗口
登陸視窗一般是先要求輸入密碼,正確才進入主應用。很多人把登陸視窗做成主視窗,這樣很不舒服,因為登陸視窗無論從生存期,作用上都很難擔當主視窗的“重任”。比較通順的做法是把主視窗隱藏起來,彈出登陸窗,正確,再把登陸窗釋放,主窗顯示出來。
看似簡單,但我們直接隱藏主表單是辦不到的。這需要在project中填寫代碼:
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
form1.ShowPasswordWindow; // 添加這一行代碼,彈出密碼視窗。
Application.Run;
... form1.showPasswordWindow中的代碼:
hide; if form2.showModal = password_ok then show elseapplication.terminate;
二、多進程和多線程
進程從用戶的觀點看是獨立的運行實體,擁有獨立的記憶體空間,處理機資源等。不同的進程之間不能直接訪問對方的記憶體。從作業系統的角度看,進程是輪流執行的獨立任務。在切換任務時,要保存與該任務有關的資源資訊。
從用戶的角度看,線程與進程很類似。區別只是線程之間共用同一虛擬記憶體空間。在作業系統看來,線程的切換不需要保存記憶體的資訊,線程就是輕型的進程。
每一個進程都擁有至少一個線程,這個線程就是主線程。在delphi中,正是主線程在處理日常的事物。在windows作業系統中,在缺省的情況下,主線程的任務就是:循環往復地執行取消息和執行消息的動作。這實際上就是Application.Run的核心內容。在同一時刻,一個程式不可能既去取消息又去執行消息。因而在執行複雜的耗時的操作的時候,視窗會暫時失去反映,給用戶一種好象“死機”的感覺。
三、使用delphi的線程物件
通過繼承TThread可以很方便地生成自己的線程類,從而創建自己的線程物件。
delphi的線程類是對windows API中線程服務的封裝。缺省的線程類創建物件的時候,需要一個參數,就是創建後是否掛起。掛起的意思是創建後並不馬上運行,而要等待調用resume才 開始運行。對於正在運行的物件也可以指定它掛起,調用suspend即可。如果調用suspend兩次,則需要兩次調用resume才能解除掛起狀態。
可以使用掛起的這個特性來讓一個線程等待其他多個線程執行完畢才開始工作。
delphi的控制項並非線程安全控 件。也就是說,多個線程在操作一個控制項時候,無法保證控制項資料的邏輯完整性。一般說來,在主線程中操作控制項。如果其他線程也需要操作,則需要一種同步的機制,就是調用synchronize()方法,把自己操作包含在其中。synchronize的實質是把對控制項的訪問自動進行串列化。
【例】構造線程,完成自動向主表單控制項中連續寫入資料的操作。為了看到效果,使用了延時操作。可以觀察到,在寫操作的過程中,主表單仍然可以被拖動,改變大小等等。
◇創建線程物件可以使用delphi提供的嚮導來完成更方便。new|other ... ThreadObject
◇synchronize中的過程不能有參數,因而需要通過線程的私有變數來傳遞資訊。
四、線程的排斥和同步
排斥和同步是多個線程間的常見關係。排斥可以通過TcriticalSection來控制,它相當於臨界區物件。在同一時間,只能有一個線程獲得該物件。當申請這個物件時,如果物件已經被佔用,則申請的線程會進入掛起狀態;當釋放這個物件時,如果有其他線程是掛起的,則第一個掛起的線程會被喚醒。
【例】TcriticalSection.create; a.Acquire; … a.release;
TcriticalSection實現了線程間的完全排斥。但有些情況下,我們並不希望所有的線程都是排斥的關係,比如最常見的讀寫相同的資料區,則讀的線程間就不排斥。delphi為我們提供了處理這類問題的專門的類:TmultiReadExclusiveWriteSyncronizer。與 TCriticalSection稍有區別的是,這個類的方法是:beginRead,endRead;beginWrite,endWrite。
最簡單的同步是等待某一個進程的結束。A.waitfor;就可以了。
TEvent類可以構造一個信號燈物件。
T := TEvent.create(安全描述,手動重定,創建後有信號,物件名字);
安全描述可以填nil,手動復位如果為true,則線程獲得信號燈的時候,信號等並不熄滅,要等待一個顯式的reset調用才復位。創建後有信號為true,則創建後,該信號燈是打開的。物件的名字只在進程間通信有用,如果是線程,則可以為空串。
五、windows DLL原理
進程間的記憶體是獨立的。若多個進程用了相同的代碼,怎樣節省空間呢?DLL是為了共用代碼而設計的。當第一次被調用時,DLL被裝入實體記憶體,並映射到邏 輯記憶體中。當第二次被調用時,DLL不會被再次裝入,只是再次映射到新的邏輯記憶體。當最後一個使用DLL的進程退出運行,則DLL被自動從實體記憶體卸載。
delphi提供了一個wizard來幫助我們生成DLL類型應用的基本框架。
【例】用Wizard生成一個DLL工程。其中提供一個加法函數,供調用。
再生成一個調用的例子。聲明外部的DLL函數:
function MyAdd(x,y: integer):integer;external 'c:\gyhang\a.dll';
◇注意exports 和export的區別。
exports用來因出一個或多個函數。在DLL中,我們可能定義了許多函數,但並不是每一個函數都要引出的。有些函數僅僅是被其他的引出函數來調用的。如果DLL中的函數很多,一般不放在一個單元中,如果某個需要引出的函數不在主單元中定義(以library開頭的單元),必須在該函數的聲明的後邊加export進行修飾。
◇注意external的使用。在使用DLL的時候,必須用external指出DLL所在的位置。在delphi中,很多Windows DLL都被預先進行了定義,因此,我們使用的時候可以直接調用,不必再去聲明位置資訊。
◇注意stdcall的含義。如果DLL只是在自己的程式之間調用,不會有什麼問題,但當一個DLL函數在跨語言調用的時候,會存在一個“調用約定”問題,即:以怎樣的順序把參數壓棧?由誰來恢復棧指針?使不使用寄存器等等。stdcall修飾的函數使用的是與WinAPI相同的處理方式,因而被廣泛地採用。
◇注意使用c語言串指標的使用。在調用WinAPI函數的時候,經常遇到參數類型為c格式的串指針。delphi的串格式與c語言不同,所以必須進行轉換。
strPCopy用來把pascal串拷貝到一個c格式存儲的字串陣列中
strPas從一個c格式串返回等價的pascal串。
六、使用DLL
使用DLL之前必須明確第描述函數的定義,以及存在哪個DLL中。
DLL與我們的應用程式公用棧空間,因此我們要充分估計棧的使用,使不至於溢出。
在DLL的使用中,最關鍵是傳遞的參數必須是在記憶體中具有相同表示。比如整數,布林型。DLL的調用錯誤經常是由於雙方的資料表示不一致造成的。
[ 本帖最後由 灰機灰呀灰 於 2009-3-9 23:25 編輯 ] |