Как слушать трансляции сообщений Windows в .NET - PullRequest
5 голосов
/ 17 февраля 2012

У меня есть Object (т.е. не форма), которая хочет прослушивать широковещательные сообщения из Windows, например ::

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

т.е. Есть ли класс WindowsListener?

Бонусная болтовня

В прежние времена в других средах разработки платформа обеспечивала функцию AllocateHwnd:

HWND listener = AllocateHWnd(ListenerWindowProc);

где ListenerWindowProc была моя оконная процедура метод:

private void ListenerWindowProc(ref Message msg)
{
    switch (msg.msg)
    {
       case WM_SETTINGCHANGE: 
       {
          ...
       }
       break;
       case WM_POWERBROADCAST:
       {
          ...
       }
       break;
       case WM_THEMECHANGED: 
       {
          ...
       }
       break;
       ...
   }
   DefWindowProc(msg);
}

Секретным соусом была функция AllocateHwnd:

Псевдо-код:

public static HWND AllocateHWnd(WndMethod method)
{
   HWND result;
   WNDCLASS tempClass;
   Boolean classRegistered;

   UtilWindowClass.hInstance := HInstance;
   Boolean classRegistered = GetClassInfo(HInstance, UtilWindowClass.lpszClassName, tempClass);
   if (!classRegistered || (tempClass.lpfnWndProc != @DefWindowProc)
   {
      if classRegistered
      {
         UnregisterClass(utilWindowClass.lpszClassName, HInstance);
         RegisterClass(utilWindowClass);
      }
      result = CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
            '', WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, null);
      if (!Method != null)
         SetWindowLong(result, GWL_WNDPROC, UInt32(MakeObjectInstance(Method)));
   }
}

С намного большим секретным кодом, связанным с UtilWindowClass.

И когда вы закончили, вы бы DeallocateHwnd :

DeallocateHwnd(listenerWindow);

Я знаю, что где-то в глубине .NET Framework они будут отвечать на WM_SETTINGCHANGE и обновлять внутренние структуры данных .NET. я бы украл этот код, но Reflector не находит ссылки на WM_SETTINGCHANGE; предположительно потому, что декомпилированный код не показывает имя константы , только константу 0x001A .

Обновление :

Примечание : Этот объект должен быть автономным. Любой, кто использует класс, не должен изменять свое приложение, чтобы мой класс возвращал правильные данные. Он должен прослушивать трансляции из самой Windows и не требовать, чтобы разработчик модифицировал свое приложение, чтобы прослушивать определенные сообщения для меня (то есть он не должен нарушать инкапсуляцию сложной или сложной операции)

Например:

public class FrobbingGrobber: IDisposable
{
    private IntPtr hwnd = IntPtr.Zero;

    public FrobbingGrobber
    {
       _hwnd = AllocateHwnd(listenerWindowProc);
    }

    protected virtual void listenerWindowProc(ref Message msg)
    {
       switch (msg.msg)
       {
          case WM_DwmColorizationColorChanged, WM_DwmCompositionChanged: 
          {
              UpdateColorizationColor();
          }
          break;
       }
       DefWindowProc(msg);
    }

    public void UpdateColorizationColor()
    {
        NativeMethods.DwmGetColorizationColorParameters_UndocumentedExport137(ref _color);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected void Dispose(Boolean safeToDisposeManagedObjects)
    {
       if (_hwnd != 0)
       {
          DeallocateHwnd(_hwnd);      
          _hwnd = 0;
       }

       if (safeToDisposeManagedObjects)
          GC.SuppressFinalize(this);
    }

    public ~FrobbingGrobber
    {
       //If the user forgot to call Dispose i could (correctly) leak a handle, 
       //or i could fix their mistake for them
       Dispose(false);
    }

1 Ответ

7 голосов
/ 17 февраля 2012

Я предполагаю, что приведенные вами примеры были просто такими: примеры.Потому что многие из них имеют управляемые эквиваленты, которые уже оборачивают все это для вас.Как WM_POWERBROADCAST обернуто Microsoft.Win32.SystemEvents.PowerModeChanged событие WM_SETTINGCHANGED эквивалентно Microsoft.Win32.SystemEvents.UserPreferenceChanged.

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

Используйте NativeWindow класс , чтобы упростить себе задачу.В этом случае вам не нужно все, что предоставляется классом Form, все, что вам нужно, это что-то, чтобы обернуть CreateWindowEx и предоставить оконную процедуру.Просто создайте окно без флага WS_VISIBLE, потому что вы не хотите, чтобы оно появлялось на экране.

.NET Framework делает это повсеместно.Например, внутренний класс TimerNativeWindow используется для System.Windows.Forms.Timer.Если вы хотите проверить реализацию для себя в Reflector, начните искать там.Вы должны быть в состоянии искать константы, но углубление в иерархию классов, для которых вы знаете, что должно быть такое уведомление, обработанное внутри, является, как правило, более разумным способом поиска.Класс SystemEvents (описанный выше) также является хорошим местом для начала поиска стратегий реализации.

Обратите внимание, что здесь нельзя использовать окно только для сообщений (HWND_MESSAGE), поскольку они не будут получать трансляции .TimerNativeWindow, о котором я упоминал выше , делает , потому что он не заботится о событиях трансляции, поэтому не копируйте и не вставляйте код оттуда!

...