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

时间:2016-02-27 16:00:10

标签: c# .net wpf ui-automation uipi

背景

我需要在另一台显示器上创建调光效果。我想我通过使用WPF窗口解决了这个问题,该窗口占用了TopmostAllowsTransparency = True的整个屏幕尺寸。它具有内部黑色发光效果,并且应用了样式WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW(以及其他内容),以允许用户单击其后面的应用程序。

我监视Windows中的EVENT_OBJECT_REORDER事件,并调用SetWindowPos强制将Topmost状态置于其他Topmost窗口之上。到目前为止,在我的概念验证测试中似乎运作良好。

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

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

我的问题:

  • 有没有办法在没有uiAccess的情况下将窗口放在“开始”菜单上?或者甚至是另一种在不使用窗口的情况下强制屏幕昏暗的方式(但不依赖于显示器驱动程序或硬件功能)?

  • 如果没有,在分发WPF应用程序(通过WiX安装项目或类似的东西)时,我需要考虑哪些因素绕过UIPI限制uiAccess = True?我可以在安装过程中简单地安装自签名证书吗?用户是否会遇到任何其他障碍?作为一名开发人员,我在构建时会遇到任何其他障碍(除了我已经提到过的内容)吗?

谢谢!

2 个答案:

答案 0 :(得分:4)

  

我监控EVENT_OBJECT_REORDER事件

您正在使用SetWinEventHook()。此方案未能通过经典的“如果两个程序执行此操作”括号。 Raymond Chen在this blog post中很好地讨论了这个问题,给你的方法dedicated post

这比你想象的要普遍得多。每台Windows机器都有一个程序,例如,运行Osk.exe,屏幕键盘程序。有趣的实验,我预测它会暂时闪烁一段时间,但假设它最终会放弃。实际上并不确定它确实如此,上次我尝试这是在Vista时间,它不会,请告诉我们。

相当肯定你会得出结论,这不是正确的方法,所以uiAccess也没有实际意义。你需要它绕过UIPI并使SetWindowPos()工作。 UAC的一个方面是阻止程序试图劫持提升程序的功能。覆盖“开始”窗口可以作为DOS攻击。这里更大的问题是你的自签名证书不起作用,你必须买一个真正的证书。让你每7年回来几百美元。

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

WMI太难以考虑了。不太确定它为什么经常失败,我认为它与视频适配器和显示器之间通常不那么出色的I2C互连有关。或者想要通过Fn击键控制亮度的笔记本电脑,该功能总是获胜。或者Windows功能可以根据环境光线自动调整亮度,总是更理想的方式来实现这一点,并且需要采取艰难的行动。

最常见的结果可能是对您的程序耸耸肩,以及笨拙的监视器控件上的用户诅咒。但他会弄清楚它并弄明白。遗憾。

答案 1 :(得分:3)

这不会回答有关uiAccess=true的任何内容,但是......

调暗屏幕

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

例如,请使用以下帮助程序类:

/// <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]
             };
    }
  }
}

static void Main()中结合以下内容,程序将更改亮度,直到用户退出应用程序:

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();

请注意,这是应用global solution to a local problem

如果你选择了这条路线,我建议确实确保你在退出之前恢复用户的伽玛值,否则他们会留下比你的应用程序更糟糕的经验坠毁,屏幕没有永久变暗。

来源: