Подтвердить что ты не робот

Соображения при установке рабочего WPF-приложения с помощью uiAccess = True

Фон:

У меня есть требование создать эффект затемнения на другом мониторе. Думаю, я решил это, используя окно WPF, которое обрабатывает все размеры экрана с помощью Topmost и AllowsTransparency= True. Он обладает внутренним эффектом черного свечения и к нему применяется стиль WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW (среди прочего), чтобы пользователи могли перейти к приложениям, расположенным за ним.

Я отслеживаю события EVENT_OBJECT_REORDER в Windows и вызываю SetWindowPos, чтобы заставить верхнее состояние выше других верхних окон. Кажется, что это хорошо работает до сих пор в моем доказательстве тестирования концепции.

Проблема, которую я обнаружил, заключалась в том, что это затемнение (окно) будет охватывать панель задач, но не если я нажму кнопку "Пуск" . В настоящее время я тестирую Windows 10. Если я нажму кнопку "Пуск" , это приведет к появлению меню "Пуск" и "Панель задач" над окном. Я хотел, чтобы все оставалось тусклым, всегда.

Я решил эту проблему, установив uiAccess= true в манифест приложения, создав самозаверяющий сертификат и скопировав exe на "c:\program files *". Это позволяет мне форсировать самое верхнее состояние для моего окна, даже над меню "Пуск" .

Мои вопросы:

  • Есть ли способ разместить окно над меню "Пуск" без uiAccess? Или даже другой способ принудительно уменьшить яркость экрана без использования окна (но не зависит от драйверов монитора или аппаратных возможностей)?

  • Если нет, то какие соображения мне следует иметь в виду при распространении WPF-приложения (через проект настройки WiX или что-то подобное), который должен обходить ограничения UIPI с помощью uiAccess= True? Могу ли я просто установить свой собственный сертификат в процессе установки? Будет ли пользователь сталкиваться с любыми дополнительными препятствиями? Будет ли я, как разработчик, сталкиваться с любыми дополнительными препятствиями при его создании (помимо того, что я уже упоминал)?

Спасибо!

4b9b3361

Ответ 1

Я отслеживаю события EVENT_OBJECT_REORDER

Вы используете SetWinEventHook(). Этот сценарий не соответствует классическому "что, если две программы делают это". Раймонд Чен подробно обсудил это в этом сообщении в блоге, предоставив вашему подходу посвященный пост.

Это намного чаще, чем вы могли бы предположить. На каждом компьютере Windows есть программа, которая делает это, например, запустите Osk.exe, программу на экране. Интересный эксперимент, я предсказываю, что он будет мерцать на некоторое время, но предположим, что он в конце концов сдастся. На самом деле это не так, в прошлый раз, когда я пробовал это во время Vista, и это не так, пожалуйста, сообщите нам.

Достаточно уверен, что вы поймете, что это не правильный путь, так что uiAccess тоже спор. Вы нуждались в этом здесь, чтобы обойти UIPI и сделать работу SetWindowPos(). Аспект UAC, который блокирует попытки программы захватить повышенные возможности программы. Покрытие окна "Старт" квалифицируется как атака DOS. Большая проблема заключается в том, что ваш самозаверяющий сертификат не будет работать, вам придется купить реальный. Устанавливает вам несколько сотен долларов каждые ~ 7 лет.

Управление яркостью монитора с помощью программного обеспечения не так просто сделать правильно. Все подходят для SetDeviceGammaRamp(), и это то, что вы должны делать. Документы MSDN предоставят вам много FUD, но afaik будет использовать его каждый драйвер видеоадаптера. Он был популярен в играх. Одним из неизбежных ограничений является то, что он активен только для рабочего стола, на котором запущена ваша программа. Поэтому не для безопасного рабочего стола (экранная заставка и Ctrl + Alt + Del), а не для других сеансов входа в систему, если они не запускают вашу программу.

WMI слишком шелушатся, чтобы рассмотреть. Не так уверен, почему он так часто терпит неудачу, я полагаю, что он имеет какое-то отношение к часто используемому межсетевому соединению I2C, расположенному между видеоадаптером и монитором. Или ноутбуки, которые хотят контролировать яркость нажатием клавиши Fn, эта функция всегда выигрывает. Или функция Windows, которая автоматически регулирует яркость на основе окружающего света, неизменно более желательный способ сделать это и жесткий акт, чтобы следовать.

Наиболее распространенный результат, вероятно, будет пожимать плечами в вашей программе и проклинать пользователя на неуклюжих мониторах. Но он будет играть с этим и понять это. К сожалению.

Ответ 2

Это ничего не даст о 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();

Обратите внимание, что это приложение глобальное решение локальной проблемы

Если вы поедете по этому маршруту, я бы посоветовал действительно убедиться, что вы восстанавливаете гамму пользователя перед выходом, иначе у них будет меньше опыта, чем у стекера, и ваше приложение не будет постоянно серый цвет.

Источники: