C# 安装uiAccess=True的桌面WPF应用程序时的注意事项

C# 安装uiAccess=True的桌面WPF应用程序时的注意事项,c#,.net,wpf,ui-automation,uipi,C#,.net,Wpf,Ui Automation,Uipi,背景: 我需要在另一个显示器上创建调光效果。我想我通过使用一个WPF窗口解决了这个问题,该窗口占据了整个屏幕的维度,并且最顶层和允许透明度=True。它有一个内部的黑色发光效果,并应用了样式WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,允许用户点击它背后的应用程序 我监视Windows中的EVENT\u OBJECT\u REORDER事件,并调用SetWindowPos强制最顶层的状态高于其他最顶层的窗口。到目前为止,它似乎在我的概念验证测试中运行良好 我发现的问题

背景:

我需要在另一个显示器上创建调光效果。我想我通过使用一个WPF窗口解决了这个问题,该窗口占据了整个屏幕的维度,并且
最顶层
允许透明度
=True。它有一个内部的黑色发光效果,并应用了样式
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW
,允许用户点击它背后的应用程序

我监视Windows中的
EVENT\u OBJECT\u REORDER
事件,并调用
SetWindowPos
强制最顶层的状态高于其他最顶层的窗口。到目前为止,它似乎在我的概念验证测试中运行良好

我发现的问题是这个变暗(窗口)会覆盖任务栏,但如果我单击“开始”菜单则不会。我目前正在用Windows10进行测试。如果单击“开始”菜单,则会导致“开始”菜单和任务栏显示在变暗(窗口)上方。我总是希望一切都保持暗淡

我通过在应用程序清单中设置
uiAccess
=true,生成自签名证书,并将exe复制到“c:\program files*”来解决这个问题。这允许我强制窗口处于最顶层状态,甚至在“开始”菜单上方

我的问题:

  • 是否有一种方法可以将窗口放置在“开始”菜单上,而无需
    uiAccess
    ?或者甚至是另一种不使用窗口(但不依赖于显示器驱动程序或硬件功能)强制屏幕变暗的方法

  • 如果不是,那么在分发WPF应用程序(通过WiX设置项目或类似项目)时,我需要记住哪些注意事项,即使用
    uiAccess
    =True绕过UIPI限制?我可以在安装过程中简单地安装我的自签名证书吗?用户是否会遇到其他障碍?作为一名开发人员,我在构建这个过程中会遇到任何额外的障碍吗(除了我已经提到的)


谢谢大家!

这不会回答任何关于
uiAccess=true
的问题,但是

调暗屏幕 作为调暗屏幕的另一种方法,您可以尝试使用一次性调暗所有屏幕(如果需要)

例如,以以下帮助器类为例:

/// <summary> Allows changing the gamma of the displays. </summary>
public static class GammaChanger
{
  /// <summary>
  ///  Retrieves the current gamma ramp data so that it can be restored later.
  /// </summary>
  /// <param name="gamma"> [out] The current gamma. </param>
  /// <returns> true if it succeeds, false if it fails. </returns>
  public static bool GetCurrentGamma(out GammaRampRgbData gamma)
  {
    gamma = GammaRampRgbData.Create();
    return GetDeviceGammaRamp(GetDC(IntPtr.Zero), ref gamma);
  }

  public static bool SetGamma(ref GammaRampRgbData gamma)
  {
    // Now set the value.
    return SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref gamma);
  }

  public static bool SetBrightness(int gamma)
  {
    GammaRampRgbData data = new GammaRampRgbData
                            {
                              Red = new ushort[256],
                              Green = new ushort[256],
                              Blue = new ushort[256]
                            };

    int wBrightness = gamma; // reduce the brightness
    for (int ik = 0; ik < 256; ik++)
    {
      int iArrayValue = ik * (wBrightness + 128);
      if (iArrayValue > 0xffff)
      {
        iArrayValue = 0xffff;
      }
      data.Red[ik] = (ushort)iArrayValue;
      data.Green[ik] = (ushort)iArrayValue;
      data.Blue[ik] = (ushort)iArrayValue;
    }

    return SetGamma(ref data);
  }

  [DllImport("gdi32.dll")]
  private static extern bool SetDeviceGammaRamp(IntPtr hdc, ref GammaRampRgbData gammaRgbArray);

  [DllImport("gdi32.dll")]
  private static extern bool GetDeviceGammaRamp(IntPtr hdc, ref GammaRampRgbData gammaRgbArray);

  [DllImport("user32.dll")]
  private static extern IntPtr GetDC(IntPtr hWnd);

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  public struct GammaRampRgbData
  {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
    public UInt16[] Red;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
    public UInt16[] Green;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
    public UInt16[] Blue;

    /// <summary> Creates a new, initialized GammaRampRgbData object. </summary>
    /// <returns> A GammaRampRgbData. </returns>
    public static GammaRampRgbData Create()
    {
      return new GammaRampRgbData
             {
               Red = new ushort[256],
               Green = new ushort[256],
               Blue = new ushort[256]
             };
    }
  }
}
但请注意,这是在应用

如果你真的这么做了,我建议你真的要确保在退出之前恢复用户的gamma,否则你的应用程序会崩溃,屏幕不会永久变暗

资料来源:

  • 讨论,这是大部分算法的来源
  • 这是上述解决方案的基础
我监视事件\对象\重新排序事件

您正在使用SetWinEventHook()。此场景失败于经典的“如果两个程序执行此操作会发生什么”括号。Raymond Chen在中对此进行了很好的讨论,给出了您的方法

这比你想象的要普遍得多。每台Windows计算机都有一个执行此操作的程序,例如,运行屏幕键盘程序
Osk.exe
。有趣的实验,我预测它会严重闪烁一段时间,但假设它最终会放弃。事实上我不确定,我上次尝试这个是在Vista时间,它不会,请让我们知道

可以肯定的是,您会得出这样的结论:这不是正确的方法,因此uiAccess也是没有意义的。这里需要它来绕过UIPI并使SetWindowPos()工作。UAC的一个方面,它阻止程序试图劫持提升的程序的能力。覆盖开始窗口符合拒绝服务攻击的条件。这里更大的问题是,您的自签名证书不起作用,您必须购买一个真正的证书。每~7年会让你退回几百美元

用软件控制显示器的亮度并不是那么容易做到的。每个人都可以使用SetDeviceGammaRamp(),这也是您应该做的。MSDN文档将为您提供大量FUD,但每个主流视频适配器驱动程序都会实现它。它在游戏中很流行。一个不可避免的限制是,它仅在运行程序的桌面上处于活动状态。因此,不适用于安全桌面(屏幕保护程序和Ctrl+Alt+Del),也不适用于其他登录会话,除非它们也启动了您的程序

<> WMI太小,不能考虑。我不太清楚为什么它会经常出现故障,我想这与视频适配器和显示器之间的I2C互连通常不太理想有关。或者想要通过Fn键控制亮度的笔记本电脑,这项功能总是胜出的。或者是基于环境光自动调整亮度的Windows功能,这总是一种更理想的方式,而且很难遵循


最常见的结果很可能是对您的程序耸耸肩,对笨拙的监视器控件诅咒用户。但他会摆弄它,把它弄明白。抱歉。

为什么不使用SetMonitorBrightness?@Simon谢谢,我已经考虑过了,但是支持的非常有限。它似乎仅限于笔记本电脑显示器和其他一些显示器。我可能会在支持它的显示器上提供这个选项,但我需要一些可以在任何显示器上产生更全局性的暗淡效果的东西。我以前用我的时钟应用程序(Win32)使用定时器和设置窗口POS来实现这一点,谢谢你。正如我所提到的,我正在定期调用
SetWindowPos
,这使得我最顶级的应用程序可以超越其他最顶级的应用程序(包括Windows任务栏)。不过,这似乎不会导致它位于Windows 10的“开始”菜单上方(如果显示“开始”菜单,则位于任务栏)。使用
uiAccess=true
,它确实在开始菜单上方工作。
GammaChanger.GammaRampRgbData originalGamma;
bool success = GammaChanger.GetCurrentGamma(out originalGamma);
Console.WriteLine($"Originally: {success}");

success = GammaChanger.SetBrightness(44);
Console.WriteLine($"Setting: {success}");

Console.ReadLine();

success = GammaChanger.SetGamma(ref originalGamma);
Console.WriteLine($"Restoring: {success}");

Console.ReadLine();