| 導購 | 订阅 | 在线投稿
分享
 
 
 

Delphi 消息機制引入的一個副作用

2007-02-07 12:19:07  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
  Delphi 在處理進程的消息時引入了一個隱藏的窗體Application ,借此進行消息的分發。這樣的機制優美的處理了消息的分發和處置的問題。但是最近我發現這個機制也引入了一個副作用,會在某些情況下影響程序的界面交互行爲。

  我遇到的需求是需要在程序中實現單個實例,並且在第二個實例被啓動的時候,首先將前一個實例置到最前,然後退出。按說這樣的問題應該是比較典型的例子,但是這樣的一個簡單需求就受到了這個副作用的影響。

  我的實現方式是這樣的:第二個實例啓動的時候,對前一實例發一個消息,要求它將自己置前,然後退出。至于前一個實例的句柄怎麽取得,可以用FindWindows ,也可以用命名的FileMapping ,總之,第一個窗體就這樣接到了將自己置前的命令。

  怎麽將這個窗體置到最前呢?衆所周知,SetForegroundWindow 並不能真正將一個窗體置到最前,相反的,爲了禮貌起見,它會讓這個指定的窗體在任務欄裏面閃動,吸引用戶注意,但是它不會把這個窗體蓋在Z-Order 的頂端。很有教養,但是我不喜歡,因爲這不是我想要的。

  于是我使用了這樣一個方法將我要的窗體直接置到最前面:

   SetWindowPos(hForm, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);

   SetWindowPos(hForm, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);

  強行把窗體拖到Z-Order 的頂端,然後去掉它的TOPMOST 屬性,這樣就可以了。有點不夠禮貌,不過要是禮貌有用的話,要警察做什麽?

  好了,前面說的內容似乎和我們的標題沒太多關系,可是後面的麻煩都根源在這裏。

  我發現,當第一個實例被最小化時,第二個實例將它喚醒,將它的主窗體置到最前,然後窗體的“最小化”按鈕失效了!這時窗體可以操作、可以最大化、可以關閉,卻再也不能最小化了!

  這是一個很郁悶的問題,一個不能最小化的窗體實在是很不友好的,我可以接受一個不禮貌的窗體,卻不願意接受一個這樣不友好的家夥。

  我發現問題原因的過程是這樣的:

  在進程中,主窗體的WM_SYSCOMMAND 消息是被傳遞給Application 類處理的,當CmdType 爲SC_MINIMIZE的時候,Application 會調用Minimize 方法:

  procedure TApplication.Minimize;

  begin

   if not IsIconic(FHandle) then

   begin

   NormalizeTopMosts;

   SetActiveWindow(FHandle);

   if (MainForm <> nil) and (ShowMainForm or MainForm.Visible)

   and IsWindowEnabled(MainForm.Handle) then

   begin

   SetWindowPos(FHandle, MainForm.Handle, MainForm.Left, MainForm.Top,

   MainForm.Width, 0, SWP_SHOWWINDOW);

   DefWindowProc(FHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);

   end else

   ShowWinNoAnimate(FHandle, SW_MINIMIZE);

   if Assigned(FOnMinimize) then FOnMinimize(Self);

   end;

  end;

  注意這個IsIconic(FHandle),它就是問題原因的冰山一角。IsIconic 是用來檢測窗體是否處于最小化狀態的API。我發現,第二實例將前一實例的主窗體置前之後,這個窗體最小化調用這個方法時,每次IsIconic(FHandle) 都是True。也就是說,Application 一直認爲自己是最小化的。

  于是問題就比較清楚了:我們在將主窗體強行置到最前的時候,Application 並沒有恢複原狀態。于是在Minimize 方法中主窗體就得不到最小化的命令了。

  難怪在VC 開發的程序中不會有這樣的問題!因爲不存在Application 的這個因素。

  于是我們只要將主窗體強行置前之前,首先將Application 恢複:

  if IsIconic(Application.Handle) then

  begin

   DefWindowProc(Application.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);

  end;

  這樣就好了。
 
Delphi 在處理進程的消息時引入了一個隱藏的窗體Application ,借此進行消息的分發。這樣的機制優美的處理了消息的分發和處置的問題。但是最近我發現這個機制也引入了一個副作用,會在某些情況下影響程序的界面交互行爲。 我遇到的需求是需要在程序中實現單個實例,並且在第二個實例被啓動的時候,首先將前一個實例置到最前,然後退出。按說這樣的問題應該是比較典型的例子,但是這樣的一個簡單需求就受到了這個副作用的影響。 我的實現方式是這樣的:第二個實例啓動的時候,對前一實例發一個消息,要求它將自己置前,然後退出。至于前一個實例的句柄怎麽取得,可以用FindWindows ,也可以用命名的FileMapping ,總之,第一個窗體就這樣接到了將自己置前的命令。 怎麽將這個窗體置到最前呢?衆所周知,SetForegroundWindow 並不能真正將一個窗體置到最前,相反的,爲了禮貌起見,它會讓這個指定的窗體在任務欄裏面閃動,吸引用戶注意,但是它不會把這個窗體蓋在Z-Order 的頂端。很有教養,但是我不喜歡,因爲這不是我想要的。 于是我使用了這樣一個方法將我要的窗體直接置到最前面: SetWindowPos(hForm, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE); SetWindowPos(hForm, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE); 強行把窗體拖到Z-Order 的頂端,然後去掉它的TOPMOST 屬性,這樣就可以了。有點不夠禮貌,不過要是禮貌有用的話,要警察做什麽? 好了,前面說的內容似乎和我們的標題沒太多關系,可是後面的麻煩都根源在這裏。 我發現,當第一個實例被最小化時,第二個實例將它喚醒,將它的主窗體置到最前,然後窗體的“最小化”按鈕失效了!這時窗體可以操作、可以最大化、可以關閉,卻再也不能最小化了! 這是一個很郁悶的問題,一個不能最小化的窗體實在是很不友好的,我可以接受一個不禮貌的窗體,卻不願意接受一個這樣不友好的家夥。 我發現問題原因的過程是這樣的: 在進程中,主窗體的WM_SYSCOMMAND 消息是被傳遞給Application 類處理的,當CmdType 爲SC_MINIMIZE的時候,Application 會調用Minimize 方法: procedure TApplication.Minimize; begin if not IsIconic(FHandle) then begin NormalizeTopMosts; SetActiveWindow(FHandle); if (MainForm <> nil) and (ShowMainForm or MainForm.Visible) and IsWindowEnabled(MainForm.Handle) then begin SetWindowPos(FHandle, MainForm.Handle, MainForm.Left, MainForm.Top, MainForm.Width, 0, SWP_SHOWWINDOW); DefWindowProc(FHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0); end else ShowWinNoAnimate(FHandle, SW_MINIMIZE); if Assigned(FOnMinimize) then FOnMinimize(Self); end; end; 注意這個IsIconic(FHandle),它就是問題原因的冰山一角。IsIconic 是用來檢測窗體是否處于最小化狀態的API。我發現,第二實例將前一實例的主窗體置前之後,這個窗體最小化調用這個方法時,每次IsIconic(FHandle) 都是True。也就是說,Application 一直認爲自己是最小化的。 于是問題就比較清楚了:我們在將主窗體強行置到最前的時候,Application 並沒有恢複原狀態。于是在Minimize 方法中主窗體就得不到最小化的命令了。 難怪在VC 開發的程序中不會有這樣的問題!因爲不存在Application 的這個因素。 于是我們只要將主窗體強行置前之前,首先將Application 恢複: if IsIconic(Application.Handle) then begin DefWindowProc(Application.Handle, WM_SYSCOMMAND, SC_RESTORE, 0); end; 這樣就好了。
󰈣󰈤
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
王朝網路微信公眾號
微信掃碼關註本站公眾號 wangchaonetcn
 
  免責聲明:本文僅代表作者個人觀點,與王朝網絡無關。王朝網絡登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
© 2005- 王朝網路 版權所有