| 導購 | 订阅 | 在线投稿
分享
 
 
當前位置: 王朝網路 >> delphi >> Delphi中正常窗口的實現
 

Delphi中正常窗口的實現

2007-02-02 20:09:02  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
 
  Delphi中正常窗口的實現

  摘要 在Delphi的VCL庫中,爲了使用以及實現的方便,應用對象Application創建了一個用來處理消息響應的隱藏窗口。而正是這個窗口,使得用VCL開發出來的程序存在著與其他窗口不能正常排列平鋪等顯得有些畸形的問題。本文通過對VCL的深入分析,給出了一個只需要對應用程序項目文件作3行代碼的修改就能解決問題的方案,且不需要原有的編程方式作任何改變。

  關鍵字 VCL,正常窗口,正常化

  1 引言

  用Delphi所提供的VCL類庫編寫的Windows應用程序,有一個明顯不同于標准Windows窗口的特點--主窗口的系統菜單與任務欄上的系統菜單不相同。一般情況下,主窗口的系統菜單有六個菜單項而任務欄系統菜單只有三個菜單項。實際使用中我們發現用VCL開發的程序有以下幾個方面的尴尬:

  1)不夠美觀。這是肯定的,與標准不符自然會顯得有些畸形。

  2)主窗口最小化時沒有動畫效果。

  3)窗口不能正常與其它窗口排列平鋪。

  4)任務欄系統菜單具有最高的優先級。在存在模態窗口的情況下整個程序仍然可以被最小化,與模態窗口的設計相違背。

  主窗口最小化動畫效果的問題在Delphi 5.0以後的版本中已通過Forms.pas中的ShowWinNoAnimate函數解決,但其余幾個問題則一直存在。盡管多數情況下這不會對應用程序帶來什麽影響,但在一些追求專業效果的場合確實不可接受的。由于C++ Builder與Delphi使用的是同一套類庫,所以上述問題同樣存在于使用C++ Builder編寫的Windows應用程序中。

  在以前的文章裏(阿甘的家中可以找到),我已討論過這個問題,當時的敘述看起來基本上是一種取巧的方法,而我也是在偶然之中才找到那個方法的。本文的任務就是通過對VCL類庫作一些分析,說明那樣做的原理,其次再給出一個只用3行代碼的方法,完完全全地解決Delphi中這個"非正常窗口"的問題。

  2 原理

  2.1 應用程序的創建過程

  下面是一個典型的應用程序的Delphi工程文件,我們注意到一開始就有一個對Application對象的Initialize方法的引用,我們的分析也就從這裏開始:

  program Project1;

  uses

   Forms,

   Unit1 in 'Unit1.pas' {Form1};

  {$R *.res}

  begin

   Application.Initialize;

   Application.CreateForm(TForm1, Form1);

   Application.Run;

  end.

  隱藏的窗口是由Application對象創建的,那麽Application對象又從何而來呢?在Delphi的代碼編輯窗口中按住Ctrl點擊Application就會發現,Application對象是在Forms.pas單元中定義的幾個全局對象之一。這還不夠,我們想要知道的是Application對象是在什麽地方創建的,因爲必須成功創建了TApplication類的實例我們才能引用它。

  想一下,有什麽代碼會在Application.Initialize之前執行呢?對了,是initialization代碼段中的代碼。認真調試過VCL源碼就可以知道,VCL中很多單元都有initialization代碼段,啓動Delphi程序時,先是按照uses的順序執行每個單元中initialization代碼段的代碼,完成所有的初始化動作之後才執行Application的Initialize方法以初始化Application,所以很顯然,Application對象是在某個單元的initialization代碼段中創建的。

  以"TApplication.Create"爲關鍵字在VCL源碼目錄中搜索一番,我們果然在Controls.pas單元中找到了創建Application對象的代碼。在Controls.pas單元的initialization代碼段,有一句對InitControls過程的調用,而InitControls的實現則如下所示:

  Unit Controls;

  …

  initialization

   ...

   InitControls;

  procedure InitControls;

  begin

  ...

   Mouse := TMouse.Create;

   Screen := TScreen.Create(nil);

   Application := TApplication.Create(nil);

  ...

  end;

  好,到這裏我們的分析就完成了第一步,因爲要解決非正常窗口的問題,我們必須要在Application對象初始化之前做一件事,因此了解應用程序的初始化過程就非常重要了。

  2.2 IsLibrary變量

  IsLibrary變量是在System.pas單元中定義的全局標志變量之一。如果IsLibrary的值爲true則表明程序模塊是一個動態鏈接庫,反之就是一個可執行程序。VCL類庫中的某些過程就根據這個標志變量的不同值完成不同的動作。也就是這個變量,在解決Delphi的非正常窗口問題中起到了關鍵性的作用。

  前面說過,爲了方便,Application對象初始化時創建了一個看不見的窗口(也就是用Spy++之類的工具看到的那個以"TApplication"爲類名的窗口),但也正是因爲這個看不見的窗口,才使得用Delphi開發出來的程序呈現諸多畸形。好了,如果我們能夠去掉這個看不見的窗口(同時去掉任務欄系統菜單),代之以我們的應用程序主窗口,豈不是所有的問題都解決了?

  說說簡單,但實現起來需要對VCL源代碼動大手術嗎?如果那樣豈不是有點本末倒置了?答案當然是不會,否則也不會有這篇文章了。在此我想說的是,在接下來的分析中,我們將會看到,所謂"編程之道,存乎一心",TApplication設計中無心插柳的做法,實則爲我們解決這一問題留下了接口。不做源代碼的分析,你可能要繞打圈子,而實際上我們會看到,天才的設計留給我們用的東西,不多也不少,剛剛好。

  打開TApplication類的構造函數Create,我們會發現這樣一行代碼。

  constructor TApplication.Create(AOwner: TComponent);

  begin

   ...

   if not IsLibrary then CreateHandle;

   ...

  end;

  這裏說的是,如果程序模塊不是動態鏈接庫,那麽就執行CreateHandle,而CreateHandle所做的工作在幫助中是這樣說的:"如果不存在應用程序窗口,那就創建一個",這裏的"應用程序窗口"就是上面所說的看不見的窗口,也即是罪魁禍首之所在,在TApplication類中用FHandle變量來保存其窗口句柄。這裏就是根據IsLibrary的值完成了不同的動作,因爲在動態鏈接庫中一般並不需要消息循環的,但用VCL開發動態鏈接庫還是要用到Application對象,所以有了這裏的設計。好,我們只需要欺騙一下Application對象,在它創建之前把IsLibrary賦值爲true,即可濾掉CreateHandle的執行,去掉這個討厭的窗口了。

  爲IsLibrary賦值的代碼顯然也應該放在某個單元的initialization代碼段中,而且由于initialization代碼段中的代碼是按照包含的單元的順序執行的,爲了保證在Application對象創建之前把IsLibrary賦值爲true,在工程文件中我們必需將包含賦值代碼的單元放在Forms單元之前,如下(假設該單元名爲UnitDllExe.pas):

  program Template;

  uses

   UnitDllExe in 'UnitDllExe.pas',

   Forms,

   FormMain in 'FormMain.pas' {MainForm},

   ...

  UnitDllExe.pas代碼清單如下:

  unit UnitDllExe;

  interface

  implementation

  initialization

   IsLibrary := true;

   //告訴Applciation對象,這是一個動態鏈接庫,不需要創建隱藏窗口。

  end.

  好了,編譯運行一下,我們看到,由于沒有創建隱藏窗口,原先任務欄上的系統菜單消失了,換成了主窗口的系統菜單,主窗口也能夠與其它Windows窗口正常排列平鋪。但帶來的問題是窗口無法最小化。怎麽回事呢?還是老方法,跟蹤一下。

  2.3 主窗口最小化

  最小化屬于系統命令,最終必定是調用API函數DefWindowProc來將窗口最小化,所以我們毫無困難地就找到了TCustomForm中響應WM_SYSCOMMAND消息的函數WMSysCommand,其中清楚地寫到將最小化的消息重定向到Application.WndProc去處理:

  procedure TCustomForm.WMSysCommand(var Message: TWMSysCommand);

  begin

   with Message do

   begin

   if (CmdType and $FFF0 = SC_MINIMIZE) and (Application.MainForm = Self) then

   Application.WndProc(TMessage(Message))

   ...

   end;

  end;

  而在Application.WndProc中,響應最小化消息時又調用了Application的Minimize方法,所以症結一定是在Minimize過程。

  procedure TApplication.WndProc(var Message: TMessage);

   ...

  begin

   ...

   with Message do

   case Msg of

   WM_SYSCOMMAND:

   case WParam and $FFF0 of

   SC_MINIMIZE: Minimize;

   SC_RESTORE: Restore;

   else

   Default;

   ...

  end;

  最後,找到TApplication.Minimize,就一切都明白了。這裏對于DefWindowProc函數的調用沒有産生任何效果,爲什麽呢?由于前面我們欺騙Application對象,濾掉了CreateHandle的調用,沒有創建Application對象響應消息所需要的窗口,因此導致其句柄FHandle爲0,調用當然不成功了。如果能將FHandle指向我們的應用程序主窗口就能解決問題。

  procedure TApplication.Minimize;

  begin

   ...

   DefWindowProc(FHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);

   //這裏FHandle值爲0

   ...

  end;

  3 實現

  Borland的天才們無心插柳的設計再一次讓我們找到了解決問題的辦法。由前面的分析我們知道,在用VCL開發的動態鏈接庫中並沒有創建隱藏的窗口來接收Windows消息(CreateHandle不執行),但在動態鏈接庫中如果要顯示窗口的話又需要一個父窗口。如何解決這個問題呢?VCL的設計者將保存看不見的窗口句柄的FHandle變量設計爲可寫,于是我們實際上可以簡單地給FHandle賦一個值來爲需要顯示的子窗口提供一個父窗口。例如,在某個動態鏈接庫插件中要顯示窗體,我們通常會在主模塊可執行文件中將Application對象的句柄通過動態鏈接庫的某個函數傳入並賦值給動態鏈接庫的Application.Handle,類似于:

  procedure SetApplicationHandle(MainAppWnd: HWND)

  begin

   Application.Handle := MainAppWnd;

  end;

  好了,既然Aplication.Handle實際上只是一個在內部用來響應消息的窗口句柄,而原本應該創建的看不見的窗口被我們去掉了,那我們只需要給出一個窗口的句柄,用來代替那個原本多余的隱藏窗口的句柄不就行了?這樣的窗口去哪裏找?應用程序的主窗口正是上上之選,于是有了下面的代碼。

  program Template;

  uses

   UnitDllExe in 'UnitDllExe.pas',

   Forms,

   FormMain in 'FormMain.pas' {MainForm};

  {$R *.res}

  begin

   Application.Initialize;

   Application.CreateForm(TFormMain, FormMain);

   Application.Handle := FormMain.Handle;

   Application.Run;

  end.

  于是,一切問題都解決了。你不需要對VCL源碼作任何修改,不需要對原有的程序作任何修改,只要在工程文件中增加兩行代碼,加上UnitDllExe.pas中的一行,共三行代碼,即可使得你的應用程序窗口完全和任何一個標准Windows窗口一樣正常。

  1)任務欄和窗口標題欄擁有一致的系統菜單。

  2)主窗口最小化時有動畫效果。

  3)窗口能夠正常與其它窗口排列平鋪。

  4)存在模態窗口時不能對其父窗口進行操作。

  以上實現代碼使用于Delphi的所有版本。
 
 
 
上一篇《Delphi 消息機制引入的一個副作用》
下一篇《Delphi中實現可以更改大小的對話框》
 
 
 
 
 
 
日版寵物情人插曲《Winding Road》歌詞

日版寵物情人2017的插曲,很帶節奏感,日語的,女生唱的。 最後聽見是在第8集的時候女主手割傷了,然後男主用嘴幫她吸了一下,插曲就出來了。 歌手:Def...

兄弟共妻,我成了他們夜裏的美食

老鍾家的兩個兒子很特別,就是跟其他的人不太一樣,魔一般的執著。兄弟倆都到了要結婚的年齡了,不管自家老爹怎麽磨破嘴皮子,兄弟倆說不娶就不娶,老父母爲兄弟兩操碎了心...

如何磨出破洞牛仔褲?牛仔褲怎麽剪破洞?

把牛仔褲磨出有線的破洞 1、具體工具就是磨腳石,下面墊一個硬物,然後用磨腳石一直磨一直磨,到把那塊磨薄了,用手撕開就好了。出來的洞啊很自然的。需要貓須的話調幾...

我就是掃描下圖得到了敬業福和愛國福

先來看下敬業福和愛國福 今年春節,支付寶再次推出了“五福紅包”活動,表示要“把欠大家的敬業福都還給大家”。 今天該活動正式啓動,和去年一樣,需要收集“五福”...

冰箱異味産生的原因和臭味去除的方法

有時候我們打開冰箱就會聞到一股異味,冰箱裏的這種異味是因爲一些物質發出的氣味的混合體,聞起來讓人惡心。 産生這些異味的主要原因有以下幾點。 1、很多人有這種習...

《極品家丁》1-31集大結局分集劇情介紹

簡介 《極品家丁》講述了現代白領林晚榮無意回到古代金陵,並追隨蕭二小姐化名“林三”進入蕭府,不料卻陰差陽錯上演了一出低級家丁拼搏上位的“林三升職記”。...

李溪芮《極品家丁》片尾曲《你就是我最愛的寶寶》歌詞

你就是我最愛的寶寶 - 李溪芮 (電視劇《極品家丁》片尾曲) 作詞:常馨內 作曲:常馨內 你的眉 又鬼馬的挑 你的嘴 又壞壞的笑 上一秒吵鬧 下...

烏梅的功效與作用以及烏梅的食用禁忌有哪些?

烏梅,又稱春梅,中醫認爲,烏梅味酸,性溫,無毒,具有安心、除熱、下氣、祛痰、止渴調中、殺蟲的功效,治肢體痛、肺痨病。烏梅泡水喝能治傷寒煩熱、止吐瀉,與幹姜一起制...

什麽是脂肪粒?如何消除臉部脂肪粒?

什麽是脂肪粒 在我們的臉上總會長一個個像脂肪的小顆粒,弄也弄不掉,而且顔色還是白白的。它既不是粉刺也不是其他的任何痘痘,它就是脂肪粒。 脂肪粒雖然也是由油脂...

網絡安全治理:國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰受害者

來源:中國青年報 新的攻擊方法不斷湧現,黑客幾乎永遠占據網絡攻擊的上風,我們不可能通過技術手段杜絕網絡攻擊。國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰...

河南夫妻在溫嶺網絡直播“造人”內容涉黃被刑事拘留

夫妻網絡直播“造人”爆紅   1月9日,溫嶺城北派出所接到南京警方的協查通告,他們近期打掉了一個涉黃直播APP平台。而根據掌握的線索,其中有一對涉案的夫妻主播...

如何防止牆紙老化?牆紙變舊變黃怎麽辦?

如何防止牆紙老化? (1)選擇透氣性好的牆紙 市場上牆紙的材質分無紡布的、木纖維的、PVC的、玻璃纖維基材的、布面的等,相對而言,PVC材質的牆紙最不透氣...

鮮肌之謎非日本生産VS鮮肌之謎假日貨是謠言

觀點一:破日本銷售量的“鮮肌之謎” 非日本生産 近一段時間,淘寶上架了一款名爲“鮮肌之謎的” 鲑魚卵巢美容液,號稱是最近日本的一款推出的全新護膚品,産品本身所...

中國最美古詩詞精選摘抄

系腰裙(北宋詞人 張先) 惜霜蟾照夜雲天,朦胧影、畫勾闌。人情縱似長情月,算一年年。又能得、幾番圓。 欲寄西江題葉字,流不到、五亭前。東池始有荷新綠,尚小如...

關于女人的經典語句

關于女人的經典語句1、【做一個獨立的女人】 思想獨立:有主見、有自己的人生觀、價值觀。有上進心,永遠不放棄自己的理想,做一份自己喜愛的事業,擁有快樂和成就...

未來我們可以和性愛機器人結婚嗎?

你想體驗機器人性愛嗎?你想和性愛機器人結婚嗎?如果你想,機器人有拒絕你的權利嗎? 近日,第二屆“國際人類-機器人性愛研討會”大會在倫敦金史密斯大學落下帷幕。而...

全球最變態的十個地方

10.土耳其地下洞穴城市 變態指數:★★☆☆☆ 這是土耳其卡帕多西亞的一個著名景點,傳說是當年基督教徒們爲了躲避戰爭而在此修建。裏面曾住著20000人,...

科學家稱,人類死亡後意識將在另外一個宇宙中繼續存活

據英國《每日快報》報道,一位科學家兼理論家Robert Lanza博士宣稱,世界上並不存在人類死亡,死亡的只是身體。他認爲我們的意識借助我們體內的能量生存,而且...

《屏裏狐》片頭曲《我愛狐狸精》歌詞是什麽?

《我愛狐狸精》 - 劉馨棋   (電視劇《屏裏狐》主題曲)   作詞:金十三&李旦   作曲:劉嘉   狐狸精 狐狸仙   千年修...

 
 
 
Delphi中正常窗口的實現 摘要 在Delphi的VCL庫中,爲了使用以及實現的方便,應用對象Application創建了一個用來處理消息響應的隱藏窗口。而正是這個窗口,使得用VCL開發出來的程序存在著與其他窗口不能正常排列平鋪等顯得有些畸形的問題。本文通過對VCL的深入分析,給出了一個只需要對應用程序項目文件作3行代碼的修改就能解決問題的方案,且不需要原有的編程方式作任何改變。 關鍵字 VCL,正常窗口,正常化 1 引言 用Delphi所提供的VCL類庫編寫的Windows應用程序,有一個明顯不同于標准Windows窗口的特點--主窗口的系統菜單與任務欄上的系統菜單不相同。一般情況下,主窗口的系統菜單有六個菜單項而任務欄系統菜單只有三個菜單項。實際使用中我們發現用VCL開發的程序有以下幾個方面的尴尬: 1)不夠美觀。這是肯定的,與標准不符自然會顯得有些畸形。 2)主窗口最小化時沒有動畫效果。 3)窗口不能正常與其它窗口排列平鋪。 4)任務欄系統菜單具有最高的優先級。在存在模態窗口的情況下整個程序仍然可以被最小化,與模態窗口的設計相違背。 主窗口最小化動畫效果的問題在Delphi 5.0以後的版本中已通過Forms.pas中的ShowWinNoAnimate函數解決,但其余幾個問題則一直存在。盡管多數情況下這不會對應用程序帶來什麽影響,但在一些追求專業效果的場合確實不可接受的。由于C++ Builder與Delphi使用的是同一套類庫,所以上述問題同樣存在于使用C++ Builder編寫的Windows應用程序中。 在以前的文章裏([url=http://eagleboost.myrice.com]阿甘的家[/url]中可以找到),我已討論過這個問題,當時的敘述看起來基本上是一種取巧的方法,而我也是在偶然之中才找到那個方法的。本文的任務就是通過對VCL類庫作一些分析,說明那樣做的原理,其次再給出一個只用3行代碼的方法,完完全全地解決Delphi中這個"非正常窗口"的問題。 2 原理 2.1 應用程序的創建過程 下面是一個典型的應用程序的Delphi工程文件,我們注意到一開始就有一個對Application對象的Initialize方法的引用,我們的分析也就從這裏開始: program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. 隱藏的窗口是由Application對象創建的,那麽Application對象又從何而來呢?在Delphi的代碼編輯窗口中按住Ctrl點擊Application就會發現,Application對象是在Forms.pas單元中定義的幾個全局對象之一。這還不夠,我們想要知道的是Application對象是在什麽地方創建的,因爲必須成功創建了TApplication類的實例我們才能引用它。 想一下,有什麽代碼會在Application.Initialize之前執行呢?對了,是initialization代碼段中的代碼。認真調試過VCL源碼就可以知道,VCL中很多單元都有initialization代碼段,啓動Delphi程序時,先是按照uses的順序執行每個單元中initialization代碼段的代碼,完成所有的初始化動作之後才執行Application的Initialize方法以初始化Application,所以很顯然,Application對象是在某個單元的initialization代碼段中創建的。 以"TApplication.Create"爲關鍵字在VCL源碼目錄中搜索一番,我們果然在Controls.pas單元中找到了創建Application對象的代碼。在Controls.pas單元的initialization代碼段,有一句對InitControls過程的調用,而InitControls的實現則如下所示: Unit Controls; … initialization ... InitControls; procedure InitControls; begin ... Mouse := TMouse.Create; Screen := TScreen.Create(nil); Application := TApplication.Create(nil); ... end; 好,到這裏我們的分析就完成了第一步,因爲要解決非正常窗口的問題,我們必須要在Application對象初始化之前做一件事,因此了解應用程序的初始化過程就非常重要了。 2.2 IsLibrary變量 IsLibrary變量是在System.pas單元中定義的全局標志變量之一。如果IsLibrary的值爲true則表明程序模塊是一個動態鏈接庫,反之就是一個可執行程序。VCL類庫中的某些過程就根據這個標志變量的不同值完成不同的動作。也就是這個變量,在解決Delphi的非正常窗口問題中起到了關鍵性的作用。 前面說過,爲了方便,Application對象初始化時創建了一個看不見的窗口(也就是用Spy++之類的工具看到的那個以"TApplication"爲類名的窗口),但也正是因爲這個看不見的窗口,才使得用Delphi開發出來的程序呈現諸多畸形。好了,如果我們能夠去掉這個看不見的窗口(同時去掉任務欄系統菜單),代之以我們的應用程序主窗口,豈不是所有的問題都解決了? 說說簡單,但實現起來需要對VCL源代碼動大手術嗎?如果那樣豈不是有點本末倒置了?答案當然是不會,否則也不會有這篇文章了。在此我想說的是,在接下來的分析中,我們將會看到,所謂"編程之道,存乎一心",TApplication設計中無心插柳的做法,實則爲我們解決這一問題留下了接口。不做源代碼的分析,你可能要繞打圈子,而實際上我們會看到,天才的設計留給我們用的東西,不多也不少,剛剛好。 打開TApplication類的構造函數Create,我們會發現這樣一行代碼。 constructor TApplication.Create(AOwner: TComponent); begin ... if not IsLibrary then CreateHandle; ... end; 這裏說的是,如果程序模塊不是動態鏈接庫,那麽就執行CreateHandle,而CreateHandle所做的工作在幫助中是這樣說的:"如果不存在應用程序窗口,那就創建一個",這裏的"應用程序窗口"就是上面所說的看不見的窗口,也即是罪魁禍首之所在,在TApplication類中用FHandle變量來保存其窗口句柄。這裏就是根據IsLibrary的值完成了不同的動作,因爲在動態鏈接庫中一般並不需要消息循環的,但用VCL開發動態鏈接庫還是要用到Application對象,所以有了這裏的設計。好,我們只需要欺騙一下Application對象,在它創建之前把IsLibrary賦值爲true,即可濾掉CreateHandle的執行,去掉這個討厭的窗口了。 爲IsLibrary賦值的代碼顯然也應該放在某個單元的initialization代碼段中,而且由于initialization代碼段中的代碼是按照包含的單元的順序執行的,爲了保證在Application對象創建之前把IsLibrary賦值爲true,在工程文件中我們必需將包含賦值代碼的單元放在Forms單元之前,如下(假設該單元名爲UnitDllExe.pas): program Template; uses UnitDllExe in 'UnitDllExe.pas', Forms, FormMain in 'FormMain.pas' {MainForm}, ... UnitDllExe.pas代碼清單如下: unit UnitDllExe; interface implementation initialization IsLibrary := true; //告訴Applciation對象,這是一個動態鏈接庫,不需要創建隱藏窗口。 end. 好了,編譯運行一下,我們看到,由于沒有創建隱藏窗口,原先任務欄上的系統菜單消失了,換成了主窗口的系統菜單,主窗口也能夠與其它Windows窗口正常排列平鋪。但帶來的問題是窗口無法最小化。怎麽回事呢?還是老方法,跟蹤一下。 2.3 主窗口最小化 最小化屬于系統命令,最終必定是調用API函數DefWindowProc來將窗口最小化,所以我們毫無困難地就找到了TCustomForm中響應WM_SYSCOMMAND消息的函數WMSysCommand,其中清楚地寫到將最小化的消息重定向到Application.WndProc去處理: procedure TCustomForm.WMSysCommand(var Message: TWMSysCommand); begin with Message do begin if (CmdType and $FFF0 = SC_MINIMIZE) and (Application.MainForm = Self) then Application.WndProc(TMessage(Message)) ... end; end; 而在Application.WndProc中,響應最小化消息時又調用了Application的Minimize方法,所以症結一定是在Minimize過程。 procedure TApplication.WndProc(var Message: TMessage); ... begin ... with Message do case Msg of WM_SYSCOMMAND: case WParam and $FFF0 of SC_MINIMIZE: Minimize; SC_RESTORE: Restore; else Default; ... end; 最後,找到TApplication.Minimize,就一切都明白了。這裏對于DefWindowProc函數的調用沒有産生任何效果,爲什麽呢?由于前面我們欺騙Application對象,濾掉了CreateHandle的調用,沒有創建Application對象響應消息所需要的窗口,因此導致其句柄FHandle爲0,調用當然不成功了。如果能將FHandle指向我們的應用程序主窗口就能解決問題。 procedure TApplication.Minimize; begin ... DefWindowProc(FHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0); //這裏FHandle值爲0 ... end; 3 實現 Borland的天才們無心插柳的設計再一次讓我們找到了解決問題的辦法。由前面的分析我們知道,在用VCL開發的動態鏈接庫中並沒有創建隱藏的窗口來接收Windows消息(CreateHandle不執行),但在動態鏈接庫中如果要顯示窗口的話又需要一個父窗口。如何解決這個問題呢?VCL的設計者將保存看不見的窗口句柄的FHandle變量設計爲可寫,于是我們實際上可以簡單地給FHandle賦一個值來爲需要顯示的子窗口提供一個父窗口。例如,在某個動態鏈接庫插件中要顯示窗體,我們通常會在主模塊可執行文件中將Application對象的句柄通過動態鏈接庫的某個函數傳入並賦值給動態鏈接庫的Application.Handle,類似于: procedure SetApplicationHandle(MainAppWnd: HWND) begin Application.Handle := MainAppWnd; end; 好了,既然Aplication.Handle實際上只是一個在內部用來響應消息的窗口句柄,而原本應該創建的看不見的窗口被我們去掉了,那我們只需要給出一個窗口的句柄,用來代替那個原本多余的隱藏窗口的句柄不就行了?這樣的窗口去哪裏找?應用程序的主窗口正是上上之選,于是有了下面的代碼。 program Template; uses UnitDllExe in 'UnitDllExe.pas', Forms, FormMain in 'FormMain.pas' {MainForm}; {$R *.res} begin Application.Initialize; Application.CreateForm(TFormMain, FormMain); Application.Handle := FormMain.Handle; Application.Run; end. 于是,一切問題都解決了。你不需要對VCL源碼作任何修改,不需要對原有的程序作任何修改,只要在工程文件中增加兩行代碼,加上UnitDllExe.pas中的一行,共三行代碼,即可使得你的應用程序窗口完全和任何一個標准Windows窗口一樣正常。 1)任務欄和窗口標題欄擁有一致的系統菜單。 2)主窗口最小化時有動畫效果。 3)窗口能夠正常與其它窗口排列平鋪。 4)存在模態窗口時不能對其父窗口進行操作。 以上實現代碼使用于Delphi的所有版本。
󰈣󰈤
 
 
 
  免責聲明:本文僅代表作者個人觀點,與王朝網路無關。王朝網路登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
 
陽光靓麗的模特兒(8)
陽光靓麗的模特兒(7)
陽光靓麗的模特兒(6)
陽光靓麗的模特兒(5)
秋-印象
德慶盤龍峽 一
松江印象之三
雲之南(寬幅)
 
>>返回首頁<<
 
 
 
 熱帖排行
 
 
 
 
© 2005- 王朝網路 版權所有