C# Screen.AllScreens错误并将WM_DISPLAYCHANGE发布到单个WinForm应用程序

C# Screen.AllScreens错误并将WM_DISPLAYCHANGE发布到单个WinForm应用程序,c#,wmi,pinvoke,C#,Wmi,Pinvoke,首先,很抱歉发了这么长的帖子 关于如何限制WM\u DISPLAYCHANGE消息的发布范围,有什么建议吗? 情景: Screen.AllScreens返回客户端上检测到的所有监视器的坐标和分辨率数组。如果在工作站锁定时启动应用程序(在夜间应用程序重新启动期间),Screen.AllScreens仅返回一个元素,详细说明单个屏幕,所有多个监视器的尺寸为一个 随后,在此场景中,当用户解锁工作站并开始使用应用程序时,由于屏幕,正在使用的Infragistics控件(UltraWinDock)不允许将

首先,很抱歉发了这么长的帖子

关于如何限制WM\u DISPLAYCHANGE消息的发布范围,有什么建议吗?

情景:

Screen.AllScreens
返回客户端上检测到的所有监视器的坐标和分辨率数组。如果在工作站锁定时启动应用程序(在夜间应用程序重新启动期间),
Screen.AllScreens
仅返回一个元素,详细说明单个屏幕,所有多个监视器的尺寸为一个

随后,在此场景中,当用户解锁工作站并开始使用应用程序时,由于
屏幕,正在使用的Infragistics控件(UltraWinDock)不允许将浮动窗口拖动到主屏幕之外。AllScreens
属性不返回系统的真实监视器配置。Infragistics控件实际上查看
Screen.PrimaryScreen.Bounds
,但是
Screen.PrimaryScreen
属性反过来调用缓存的
Screen.AllScreens
数组,它返回一个巨大的主屏幕

当应用程序正常启动(工作站解锁)时,控制功能正常

我能看到
Screen.AllScreens
被重置和刷新的唯一方法是通过引发的
SystemEvents.DisplayChanging
事件, 此时内部字段设置为空。(
Screen.AllScreens
钩住此事件。)
Screen.AllScreens
将在下次调用时重新填充

根据我的判断,
SystemEvents.DisplayChanging
事件可以通过
WM\u DISPLAYCHANGE
WMI消息引发

我管理解决方案的方法是调用:

[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
其参数表示系统上的显示器数量。无论工作站是否锁定,这似乎总是返回存在的监视器的实际数量

然后,我评估
Screen.AllScreens
数组的长度是否小于
GetSystemMetrics(SM\u 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)
这非常有效,并且达到了预期的效果
屏幕。所有屏幕
均已重置,基础设施控制功能正常

在我看来,这就像是一个带有
屏幕的模糊bug。当应用程序在锁定的工作站上启动,然后解锁时,所有屏幕都不会重新评估自身

我承认这是一个罕见的问题,但仍然是一个问题

对于消息,lParam和wParam描述为:

wParam

  • 显示器的新图像深度,单位为位/像素
lParam

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

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

我为这些参数发送null
IntPtr.Zero
,因为我不知道发送消息时的实际值是多少

我在这里担心的是,我正在使用空参数在整个系统中广播
WM\u DISPLAYCHANGE
消息,并且可能有正在运行的进程使用
WM\u 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没有太多的经验
  • 目标框架是.NET3.5
  • 我还没有看到播放
    WM_DISPLAYCHANGE
    方法的PostMessage的任何副作用
  • 我已经下载了Infragistics的源代码,并指出了代码中出现的问题,并考虑重新编译控件以集成修复程序,但决定不这样做。我已将此问题告知Infragistics,但我不能等待修复,尤其不能将其视为Infragistics问题,因为它是导致此问题的
    Screen.AllScreens
  • 应用程序必须在一夜之间重新启动,并且不能更改为等待用户在早上登录
  • 我创建了一个测试应用程序,它锁定用户的工作站,重新启动自身(应用程序,而不是工作站),并在应用程序锁定时评估
    屏幕。AllScreens
    属性,然后在发送
    WM_DISPLAYCHANGE
    方法后创建另一个快照。我想添加一个屏幕截图,但作为一个新的StackOverflow用户,我不允许这样做
我只是(!)需要知道如何使用p/Invoke向特定应用程序发送邮件

您需要获取应用程序的hwnd,而不是:

SendMessage(this.Handle...
使用spy++查找应用程序的窗口类,然后使用FindWindow()获取其hwnd


但这里可能有我遗漏的东西——你似乎足够有能力意识到这一点,所以也许我误解了,这就是你在重新编译的Infrastics应用程序中的代码?

在这里给出答案可能太晚了,但这里的另一个选择是只调用所有屏幕使用的相同调用。然后您将获得实际值,而不是