屏幕上.所有屏幕错误和张贴WM_DISPLAYCHANGE,到一个单一的WinForm应用程序
本文关键字:屏幕 一个 应用程序 WinForm 单一 错误 WM 张贴 DISPLAYCHANGE | 更新日期: 2023-09-27 18:12:39
首先,很抱歉发了这么长的帖子。
关于如何限制WM_DISPLAYCHANGE消息的发布范围的任何建议?
场景:
Screen.AllScreens
返回客户端检测到的所有监视器的坐标和分辨率数组。如果应用程序在工作站被锁定时启动(在一夜之间重新启动应用程序期间),Screen.AllScreens
只返回一个元素,该元素详细描述了单个屏幕,其中所有多个监视器的尺寸为一个。
随后,在这种情况下,当用户解锁工作站并开始使用该应用程序时,正在使用的inffragistics控件(UltraWinDock)不允许将浮动窗口拖到主屏幕之外,因为Screen.AllScreens
属性不返回系统的真正监视器配置。inffragistics控件实际上查看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);
与以下参数:
PostMessage(HWND_BROADCAST, WM_DISPLAYCHANGE,UIntPtr.Zero,IntPtr.Zero)
这工作得很好,并实现了预期的结果! Screen.AllScreens
复位,inffragistics控制功能正常。
在我看来,这就像一个模糊的bug,当一个应用程序在一个锁定的工作站上启动时,Screen.AllScreens
没有重新评估自己,然后解锁。
我承认这是一个罕见的问题,但仍然是一个问题。
对于WM_DISPLAYCHANGE消息,lParam和wParam被描述为:
按钮
- 显示的新图像深度,单位为每像素位。
lParam
低阶字指定屏幕的水平分辨率
高阶字指定屏幕的垂直分辨率
对于这些参数,我发送的是null IntPtr.Zero
,因为我不知道发送消息时的实际值是什么。
我这里关心的是,我在整个系统中广播WM_DISPLAYCHANGE
消息,并使用null参数,并且可能有正在运行的进程消耗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。
- 我还没有看到任何副作用广播
WM_DISPLAYCHANGE
方法的PostMessage。 - 我已经下载了Infragistics的源代码,并确定了问题发生在他们的代码中,并考虑重新编译控件以集成修复,但决定反对这一点。我已经通知了inffragistics这个问题,但不能等待修复,也不认为这是inffragistics的问题,因为是
Screen.AllScreens
造成的。 - 一夜之间重启应用程序是必要的,不能更改为等到用户早上登录。
- 我创建了一个测试应用程序,它锁定用户的工作站,重新启动自己(应用程序,而不是工作站),并在应用程序被锁定时评估
Screen.AllScreens
属性,然后在我发送WM_DISPLAYCHANGE
方法后评估另一个快照。我想添加一个屏幕截图,但我不允许作为一个新的StackOverflow用户!!
在这里给出答案可能太晚了,但这里的另一种选择是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消息。
我只是(!)需要知道如何使用p/Invoke来PostMessage到特定的应用程序
你需要得到应用的hwnd,而不是:
SendMessage(this.Handle...
使用spy++找到应用程序的窗口类,然后使用FindWindow()获得它的hwnd。
但我可能在这里遗漏了一些东西——你似乎有足够的能力意识到这一点,所以也许我是误解,这是你重新编译的Infragistics应用程序中的代码?