亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

Delphi中正常窗口的實現

2019-11-18 18:05:08
字體:
來源:轉載
供稿:網友
 

Delphi中正常窗口的實現

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

關鍵字 VCL,正常窗口,正?;?/FONT>

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

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的所有版本。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩免费一区| 丝袜美腿精品国产二区| 成人午夜一级二级三级| 欧美在线激情视频| 国产日韩在线视频| 91精品国产电影| 国产免费观看久久黄| 久久伊人91精品综合网站| 色悠久久久久综合先锋影音下载| 欧美成人午夜激情视频| 激情成人在线视频| 97国产suv精品一区二区62| 国a精品视频大全| 欧美日韩中文字幕| 91精品国产高清自在线看超| 久久九九精品99国产精品| 成人在线观看视频网站| 啪一啪鲁一鲁2019在线视频| 国产+成+人+亚洲欧洲| 97超碰蝌蚪网人人做人人爽| 日韩在线小视频| 91夜夜揉人人捏人人添红杏| 欧美xxxx做受欧美.88| 亚洲女人天堂成人av在线| 亚洲一区亚洲二区亚洲三区| 亚洲精品自拍视频| 亚洲日本欧美中文幕| 国产视频精品一区二区三区| 日韩毛片中文字幕| 日产精品久久久一区二区福利| 国产一区二区三区高清在线观看| 日韩av在线一区二区| 欧美在线精品免播放器视频| 国产成人鲁鲁免费视频a| 精品一区二区三区电影| 中文字幕精品国产| 国产精品自拍小视频| 美日韩丰满少妇在线观看| 欧美亚洲另类制服自拍| 欧美疯狂做受xxxx高潮| 69久久夜色精品国产69| 亚洲欧洲国产一区| 菠萝蜜影院一区二区免费| 91久久精品美女高潮| 久久免费视频这里只有精品| 国产精品入口夜色视频大尺度| 91伊人影院在线播放| 91在线视频导航| 欧美精品videofree1080p| 国产精品欧美亚洲777777| 国产精品户外野外| 精品久久久久久久久久久| 欧美午夜性色大片在线观看| 欧美日韩激情视频8区| 久久6免费高清热精品| 69国产精品成人在线播放| 欧美人成在线视频| 国产午夜精品一区二区三区| 久久久久久久影院| 国产成人一区二区三区电影| 欧美成人手机在线| 日韩毛片在线观看| 欧美日韩国产限制| 国产午夜精品视频| 久久高清视频免费| 国模视频一区二区三区| 26uuu另类亚洲欧美日本一| 色婷婷亚洲mv天堂mv在影片| 国产成人福利视频| 成人97在线观看视频| 成人妇女淫片aaaa视频| 91亚洲国产成人久久精品网站| 欧美在线日韩在线| 精品五月天久久| 国内精品久久久| 九九热视频这里只有精品| 亚洲国产日韩精品在线| 日韩中文字幕在线播放| 久久资源免费视频| 国产日韩视频在线观看| 热re99久久精品国产66热| 亚洲国产成人精品久久| 欧洲中文字幕国产精品| 久久69精品久久久久久国产越南| 午夜欧美大片免费观看| 中文字幕v亚洲ⅴv天堂| 俺也去精品视频在线观看| 97在线视频免费观看| 尤物tv国产一区| 亚洲精品日韩在线| 97视频在线观看免费| 日韩精品视频免费在线观看| 91高清在线免费观看| 中文字幕av一区中文字幕天堂| 国产精品专区第二| 这里只有精品视频| 欧美视频中文字幕在线| 国产精品福利在线| 亚洲色图综合网| 日本精品一区二区三区在线播放视频| 欧美最猛性xxxxx亚洲精品| 91啪国产在线| 91超碰中文字幕久久精品| 日韩福利伦理影院免费| 精品成人国产在线观看男人呻吟| 精品久久久国产精品999| 亚洲精品白浆高清久久久久久| 国产精品色午夜在线观看| 亚洲一级黄色片| 黄色精品一区二区| www.亚洲成人| 国产午夜精品视频免费不卡69堂| 欧美激情小视频| 成人在线视频网站| 亚洲人高潮女人毛茸茸| 欧美日本中文字幕| 国产亚洲精品久久久| 久久综合伊人77777| 欧美成人精品三级在线观看| 成人精品一区二区三区电影黑人| 国产一区二区三区在线播放免费观看| 欧美孕妇性xx| www.亚洲一区| 欧美成人精品激情在线观看| 国产91精品久| 亚洲综合在线中文字幕| 欧美成年人视频网站欧美| 亚洲男人第一网站| 中文字幕亚洲一区二区三区五十路| 亚洲精品456在线播放狼人| 日韩av片永久免费网站| 久久全国免费视频| 久久香蕉国产线看观看av| 国产mv免费观看入口亚洲| 欧美乱妇高清无乱码| 国产精品久久久久久久久久新婚| 国产精品久久久久久久久久99| 亚洲在线观看视频| 亚洲精品国偷自产在线99热| 欧美有码在线观看视频| 青青草一区二区| 欧美多人爱爱视频网站| 精品国内产的精品视频在线观看| 亚洲成人亚洲激情| 欧美日韩中文字幕在线视频| 日韩在线小视频| 亚洲小视频在线观看| 久久国产精彩视频| 成人观看高清在线观看免费| 欧美精品免费看| 精品日韩美女的视频高清| 91久久精品国产91性色| 成人网在线观看| 精品国偷自产在线视频| 午夜免费日韩视频| 国产中文字幕日韩| 成人性生交大片免费看小说| 国产精品第8页| 欧美美女15p| 欧美日韩第一视频| 九九久久久久久久久激情| 国产精品一区二区久久久| 久久精品国产2020观看福利| 国产91精品视频在线观看|