2011-10-26 42 views
3

首先,對於長篇文章感到抱歉。Screen.AllScreens錯誤併發布WM_DISPLAYCHANGE,到一個單獨的WinForm應用程序

關於如何限制WM_DISPLAYCHANGE消息的發佈範圍的任何建議?

場景:

Screen.AllScreens返回一個客戶端上檢測到的所有顯示器座標和分辨率的陣列。如果應用程序在工作站被鎖定時啓動(在應用程序重啓過程中),Screen.AllScreens僅返回一個元素,詳細說明單個屏幕,其中所有多個監視器的尺寸爲一個。

隨後,在這種情況下,當用戶解鎖工作站並開始使用應用程序時,正在使用的Infragistics控件(UltraWinDock)不允許拖動主屏幕之外的浮動窗口,因爲Screen.AllScreens屬性未返回系統的真實監視器配置。 Infragistics控件實際上看起來是Screen.PrimaryScreen.Bounds,但Screen.PrimaryScreen屬性又調用緩存的Screen.AllScreens數組,該數組返回一個巨大的主屏幕!

當應用程序正常啓動(工作站解鎖)時,控件功能正常。

的唯一手段,我可​​以看到Screen.AllScreens被複位,並且可以被刷新是經由SystemEvents.DisplayChanging事件被升高, 在該點處內部字段被設置爲空。 (Screen.AllScreens掛鉤此事件。)Screen.AllScreens將在下次調用它時重新填充。

從我所能確定的情況來看,SystemEvents.DisplayChanging事件可以通過WM_DISPLAYCHANGE WMI消息產生。

由我已設法解決方法的裝置是通過調用:

[DllImport("user32.dll")] 
public static extern int GetSystemMetrics(int nIndex); 

SM_CMONITORS的參數表示在系統上顯示的數目。無論工作站是否鎖定,這似乎總是返回實際顯示的監視器數量。

我然後評估Screen.AllScreens陣列的長度是否小於GetSystemMetrics(SM_CMONITORS)結果,如果它是,我掛鉤到 SystemEvents.SessionSwitch靜態事件,並檢查SessionSwitchEventArgs.Reason屬性,對於SessionUnlock的值。 當工作站被解鎖時,接收到此事件和條件滿足,所以我使用P/Invoke方法

[DllImport("user32.dll")] 
public static extern bool PostMessage(IntPtr hWnd, uint wMsg, UIntPtr wParam, IntPtr lParam); 

具有以下ARGS發佈消息:

PostMessage(HWND_BROADCAST, WM_DISPLAYCHANGE,UIntPtr.Zero,IntPtr.Zero) 

這工作非常漂亮並達到預期的效果!Screen.AllScreens已重置且Infragistics控件正常工作。

它在我看來像一個難以理解的錯誤,Screen.AllScreens在鎖定的工作站上啓動應用程序時不會重新評估,然後解鎖。

一個罕見的問題,我承認,但仍然是一個問題。

對於WM_DISPLAYCHANGE消息,wParam和lParam被描述爲:

的wParam

  • 顯示器的新的圖像的深度,在每個像素的位數。

lParam的

  • 低位字指定屏幕的水平分辨率。

  • 高位字指定屏幕的垂直分辨率。

我送在這些爭論空IntPtr.Zero,因爲我不知道什麼實際值在發送消息的時間。

我的問題在於,我在整個系統上用空參數廣播WM_DISPLAYCHANGE消息,並且可能有正在運行的進程正在運行,它會消耗WM_DISPLAYMESSAGE並利用這些參數。我希望如果發送空論據,任何消費者都會忽略這些論點,但這是一個非常危險的假設。

有沒有辦法將消息發送或發佈到有問題的應用程序,並消除影響其他進程的風險?

我曾嘗試以下無濟於事:

PostMessage(IntPtr.Zero, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero) 
PostMessage(this.Handle, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero) 
PostThreadMessage(AppDomain.GetCurrentThreadId(), WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero) 
SendMessage(this.Handle, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero) 

注:

  • 我沒有用的P/Invoke或WMI大量經驗。
  • 目標框架是.Net 3.5。
  • 我還沒有看到廣播PostMessage的WM_DISPLAYCHANGE方法的任何副作用。
  • 我已經下載了Infragistics的源代碼,並確定問題出現在他們的代碼中,並且認爲重新編譯該控件以集成修補程序,但決定不這樣做。我已通知Infragistics該問題,但不能等待修復,並且不要特別將其視爲Infragistics問題,因爲這是造成該問題的Screen.AllScreens
  • 應用程序一夜之間重新啓動是必要的,無法更改爲等待用戶在早上登錄。
  • 我創建了一個測試應用程序,用於鎖定用戶的工作站,重新啓動自身(應用程序,而不是工作站),並在應用程序鎖定時評估Screen.AllScreens屬性,然後在發送WM_DISPLAYCHANGE方法後評估另一個快照。我想添加一個屏幕截圖,但不允許我是一個新的StackOverflow用戶!
+0

Infragistics再次表示,該公司是一家重要的錯誤工廠。解決你真正的問題,轉儲他們。如果你不想在他們的支持渠道尋求幫助。 –

+0

是的,我同意,但不幸的是使用控件的決定是從我手中,並且支持渠道將花費太長時間,雖然我已經跟他們記錄它。我簡單地(!)需要知道如何使用P/Invoke來PostMessage到特定的應用程序。 – pandrew

回答

0

我只需要知道如何使用P/Invoke來PostMessage的特定應用

您需要獲取應用程序的HWND,而不是(!):

SendMessage(this.Handle... 

使用spy ++查找應用程序的窗口類,然後使用FindWindow()獲取它的hwnd。

但有可能是我丟失的東西在這裏 - 你似乎很多足夠能力實現這一點,所以也許我誤解,那就是你有你重新編譯應用程序Infragistics的內碼?

0

可能來不及在這裏給出一個答案,但在這裏,另一種選擇是隻的P/Invoke是AllScreens使用相同的電話。然後,您將獲得實際值,而不是AllScreens保留的緩存值。看一看:http://www.pinvoke.net/default.aspx/user32/EnumDisplayMonitors.html

如果你在WPF,可以做這樣的事情是什麼張貼在這裏:http://social.msdn.microsoft.com/Forums/vstudio/en-US/41e0bf65-2e96-4d0f-98aa-2c0cf31aa493/wpf-application-controls-does-not-work-when-an-external-monitor-is-connected?forum=wpf

基本上,找到SystemResourceNotifyWindow和發佈WM_DISPLAYCHANGE消息給它。

相關問題