Re[6]: Перехват нажатий на такскаре
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.02.09 01:42
Оценка:
Здравствуйте, Alexander Shargin, Вы писали:

AS>Этой технологии скоро 15 лет стукнет. На тот момент она позволила снизить оверхед на вызовы функций в системных процессах (поток мигрирует в сервисный процесс вместе со своим стеком, где уже лежат все переданные параметры — не надо ничего копировать и т. д.).


Э... не уж то так время летит?

AS>Windows CE и Windows Mobile — это разные операционные системы, и у каждой своя нумерация. Windows Mobile использует ядро CE, но пользовательский интерфейс и набор приложений — свои собственные. А сама CE используется во встроенных системах, там где XP/Vista Embedded тяжеловаты для использования.


Ну, не знаю, не знаю. Хэл есть только на CE. Да и софт идет на обоих. Так что скорее нужно говорить о разных версиях... ответвлениях. В прочем, я пока что не очень глубоко погрузился в это дело .

VD>>Интересно, что планируется изменить в WM7? И заведется ли он на сегодняшних девайсах?


AS>На сегодняшних девайсах — маловероятно. На девайсах ситуация вообще сложная — далеко не всегда старый девайс можно перепрошить на новую ОС, даже если аппаратно она могла бы там крутиться. Дело в том, что Windows Mobile "в чистом виде" для конечного пользователя не существует — есть лишь прошивки, в которые входит сама ось вместе с OAL, набором драйверов и т. д. Для каждого девайса прошивки свои, делают их сами производители девайсов. И так как им хочется продавать новые девайсы, выпускать новые прошивки для старых никто особо не рвётся.


Только вчера скачали и обновили прошивку для ETEN Glofish X500+. Была она на WM 6.0, стала на 6.1. Но прошивка не родная, а сделаная нашими умельцами.
Для своего HTC MAX я буквально в начале года качал новую прошивку устраняющую некоторые баги и недоработки.
Так что все не так уж страшно. Не производитель так народные умельцы сделают все что нужно .

VD>>ОК. Попробуем. Тем более, что судя по твоим словам я все смогу сделать из дотнетного процесса, что резко упрощает задачу.


AS>Расскажи потом, что получится. Дотнет на девайсах кастрированный, иногда проблемы вылезают там, где их совершенно не ждёшь. Например, вообще нельзя загрузить в нативный процесс managed DLL (наоборот — пожалуйста).


Мои трехчасовые "приседания" привели к следующему результату.
Твои слова подтвердились. У МС как всегда бардак. Но в данном случае это немного упростило мне жизнь.

Донет в таких условиях работает, но проблемы все же есть. В одном приложении не удалось создать обычный дотнетный месэдж-луп и перехватчик сообщений окна HHTaskBar (так в CE окно таскбара называется, если кто не в курсе).
Пока что тест ограничился проверкой работоспособности самой идеи.
На сегодня я реализовал перехват WM_LBUTTONDOWN у HHTaskBar и выдачу диалога спрашивающего "Пропустить нажатие дальше?" или нет. Нажимаем "Yes" и вызывается исходная оконная процедура которая выполняет обычные действия. Если нажимаем "No", то ничего не делается. Область с кнокой [OK/x] из рассмотрения исключается, так что программы по прежнему можно закрывать без предупреждения. В первом варианте на них тоже выдавались предупреждения, но оказалось, что после потери фокуса закрытие приложений не происходит (видимо делают захват мышиного ввода который обрывается с потерей фокуса). В конечном итоге меня это не трогает, так как я не собираюсь открывать диалоги. Мне нужно всего лишь обрабатывать сдвиг пальцем вправо и влево.

Собственно исходники и бинарники можно взять здесь.
http://files.rsdn.ru/73/HHTaskBar_Subclass_Test.zip
Вроде работает, но если что софтрезет вам в помощь .
Софтрезет так же потребуется чтобы завершить работы приложения, так как шататного выхода нет, а все испробованные мной таскменеджеры попросту не видят этого процесса (окон ведь в нем нет). Хотя можно пробовать снять приложение в то время когда открыт диалог (не уверен, что выйдет, так как он же из процесса таскбара лезть должен).

А вот и исходник (большая часть кода и времени занял импорт API, так что не очень ясно есть ли преимущество в дотнете или нет... дотнет пока что используется как безопасный С ):
using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.Drawing;

static class Program
{
  // this is the new wndproc, just show a messagebox on left button down:
  private static int MyWndProc(IntPtr hWnd, int msg, int wParam, int lParam)
  {
    switch (msg)
    {
      case WM_LBUTTONDOWN:
        var rc = GetWindowRect(hWnd);
        var xPos = LoWord(lParam); 
        var yPos = HiWord(lParam);

        // if click in close button...
        if (xPos > rc.Width - rc.Height)
          break; // do default behavior

        // asc user for do default behavior
        if (MessageBox.Show( "Пропустить нажатие дальше?",  "Злостный перехватчик", 
          MessageBoxButtons.YesNo, MessageBoxIcon.Question, 
          MessageBoxDefaultButton.Button1) == DialogResult.Yes
        )
          break;
        else
          return 0;

      default:
        break;
    }

    return CallWindowProcCE(_oldWndProc, hWnd, msg, wParam, lParam);
  }
  
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [MTAThread]
  static void Main()
  {
    var hhTaskBar = FindWindowCE("HHTaskBar", null);

    if (hhTaskBar != IntPtr.Zero)
    {
      _newWndProc = MyWndProc;
      _oldWndProc = SetWindowLongCE(hhTaskBar, GWL_WNDPROC, _newWndProc);

      MessageBox.Show(string.Format("handle is 0x{0:X} oldWndProc is 0x{1:X}",
        hhTaskBar.ToInt32(),
        _oldWndProc.ToInt32()));
    }

    Thread.Sleep(Timeout.Infinite); // kipe address space of process!!!
  }

  // Import of system API...

  [DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
  private static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);

  [DllImport("coredll.dll", SetLastError = true, EntryPoint = "GetWindowLong")]
  private static extern IntPtr GetWindowLongCE(IntPtr hWnd, int nIndex);

  [DllImport("coredll.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
  private static extern IntPtr SetWindowLongCE(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

  [DllImport("coredll.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
  private static extern IntPtr SetWindowLongCE(IntPtr hWnd, int nIndex, Win32WndProc newProc);

  [DllImport("coredll.dll", EntryPoint = "CallWindowProc")]
  private static extern int CallWindowProcCE(IntPtr wProc, IntPtr hWnd, int uMsg, int wParam, int lParam);

  [DllImport("coredll.dll")]
  [return: MarshalAs(UnmanagedType.Bool)]
  static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

  static RECT GetWindowRect(IntPtr hWnd)
  {
    RECT rect = new RECT();

    GetWindowRect(hWnd, out rect);

    return rect;
  }

  static int LoWord(int lParam)
  {
    return lParam & 0xFFFF;
  }

  static int HiWord(int lParam)
  {
    return 16 >> lParam;
  }

  private delegate int Win32WndProc(IntPtr hWnd, int Msg, int wParam, int lParam);

  const int GWL_WNDPROC = -4;
  const int WM_LBUTTONDOWN = 0x0201;
  static Win32WndProc _newWndProc = null;
  static IntPtr _oldWndProc = IntPtr.Zero;
}

/// <summary>Win32 RECT structure</summary>
public struct RECT
{
  public RECT(int left, int top, int right, int bottom)
  {
    this.Left = left;
    this.Top = top;
    this.Right = right;
    this.Bottom = bottom;
  }

  public RECT(System.Drawing.Point topLeft, System.Drawing.Size size)
  {
    this.Left = topLeft.X;
    this.Top = topLeft.Y;
    this.Right = topLeft.X + size.Width;
    this.Bottom = topLeft.Y + size.Height;
  }

  public int Left;
  public int Top;
  public int Right;
  public int Bottom;

  public int Width { get { return Right - Left; } }
  public int Height { get { return Bottom - Top; } }

  public Size Size { get { return new Size(Width, Height); } }

  public Point Location { get { return new Point(Left, Top); } }

  // Handy method for converting to a System.Drawing.Rectangle
  public Rectangle ToRectangle()
  { return Rectangle.FromLTRB(Left, Top, Right, Bottom); }

  public static RECT FromRectangle(Rectangle rectangle) 
  {
    return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
  }

  public override int GetHashCode() 
  {
    return Left ^ ((Top << 13) | (Top >> 0x13))
      ^ ((Width << 0x1a) | (Width >> 6))
      ^ ((Height << 7) | (Height >> 0x19));
  }

  #region Operator overloads

  public static implicit operator Rectangle( RECT rect )
  {
    return rect.ToRectangle();      
  }

  public static implicit operator RECT( Rectangle rect )
  {
    return FromRectangle(rect);
  }

  #endregion
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.