.NET
September 2009 3

Использование P/Invoke: прячем кнопку Пуск и панель задач в Windows

На Хабре уже было несколько статей, рассказывающих об использовании механизма P/Invoke в проектах на C#. В основном, в статьях был сильный уклон в сторону теории и приводились небольшие показательные примеры.
Я же хочу показать более наглядный пример, показывающий возможности неуправляемого кода — мы будем прятать кнопку Пуск и панель задач.

Впервые о возможности использовать системные функции Windows в своих программах я узнал при изучении Visual Basic. Эта тема настолько захватила меня, что я стал собирать в коллекцию примеры использования функций Windows API. Сначала я собирал примеры все подряд. Затем стал подходить к делу более разборчиво и стал отбирать те функции и примеры, которые использовал сам в своих проектах. За несколько лет я набрал более 500 функций, и все примеры к ним для удобства оформил в виде CHM-файла. Демо-версию справочника можно до сих пор скачать с сайта http://rusproject.narod.ru/guide.htm.
Когда появилась платформа .NET Framework и языки C# и Visual Basic.NET, то плавно мигрировал на эти языки. И вот здесь мне очень пригодился свой справочник по функциям Windows API. Как пишут во многих умных книжках, язык C# вобрал в себя лучшее из Java, C++, Visual Basic. В отношении P/Invoke можно сказать, что технология вызова неуправляемого кода из управляемого кода была взята из Visual Basic. Если на C++ вызов системных функций прозрачен для программиста, то для C# нужную функцию требуется объявить (аналог Declare в VB 6.0). Из своей коллекции примеров я решил показать проект, позволяющий скрывать кнопку Пуск и панель задач в Windows XP/Vista/7.

На самом деле, пример не имеет какой-то практической ценности (во всяком случае мне не удалось найти ему стоящее применение). Единственное, что приходит на ум — создание шуточной программы, которая 1 апреля скроет привычные элементы интерфейса на столе неподготовленного пользователя. Но, с другой стороны, пример дает некоторое представление об устройстве Windows, эффектен в глазах начинающих программистов и дает более полное представление об используемых функциях.
Итак, начнем с теории. Во-первых, кнопка Пуск, панель задач, область уведомлений и область часов — это все окна. А значит, получив доступ к такому окну, можно изменить привычное поведение элемента интерфейса. Второе — все эти окна принадлежат определенным классам. И, именно, по имени классов и можно найти дескрипторы требуемых нам окон, чтобы проделать над ними эксперименты. Вот названия классов:
  • Shell_TrayWnd — панель задач
  • Button — кнопка Пуск
  • TrayNotifyWnd — область уведомлений со значками и часами
  • TrayClockWClass — часы

Теперь переходим к функциям. Для наших целей нам понадобятся функции FindWindows, FindWindowEx и ShowWindow. Я не буду здесь приводить описания функций — вы можете самостоятельно узнать о них в интернете или в моем справочнике. Приведу только их объявления и парочку сопутствующих констант:
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string ClassName, string WindowName);

[DllImport("user32.dll")]
private static extern IntPtr FindWindowEx(
  IntPtr hwndParent, IntPtr hwndChildAfter,
  string className, string windowName);

[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;


* This source code was highlighted with Source Code Highlighter.


А далее все очень просто. С помощью функций FindWindow и FindWindowEx по имени класса находятся нужные дескрипторы, которые затем используются в функции ShowWindow для скрытия или показа нужного элемента.

bool show = false;

// Прячем панель задач и кнопку Пуск
private void butHideTaskbar_Click(object sender, EventArgs e)
{
  // Получим дескриптор панели задач      
  IntPtr taskBarWnd = FindWindow("Shell_TrayWnd", null);
  // Получим дескриптор кнопки ПУСК      
  IntPtr startWnd = FindWindow("Button", null);

  // Прячем/показываем панель задач
  if (taskBarWnd != IntPtr.Zero)
  {
    ShowWindow(taskBarWnd, show ? SW_SHOW : SW_HIDE);
  }

  // Прячем/показываем кнопку ПУСК
  if (startWnd != IntPtr.Zero)
  {
    ShowWindow(startWnd, show ? SW_SHOW : SW_HIDE);
  }
  show = !show;
}

// Прячем отдельные части панели задач
private void butHide_Click(object sender, EventArgs e)
{
  // Описатель панели задач      
  IntPtr taskBarWnd = FindWindow("Shell_TrayWnd", null);
  //ShowWindow(taskBarWnd, SW_HIDE);

  // Описатель области уведомлений
  IntPtr tray = FindWindowEx(taskBarWnd, IntPtr.Zero, "TrayNotifyWnd", null);

  // Прячем область уведомлений
  // ShowWindow(tray, SW_HIDE);

  // Описатель системных часов
  IntPtr trayclock = FindWindowEx(tray, IntPtr.Zero, "TrayClockWClass", null);
  // Прячем системные часы
  ShowWindow(trayclock, SW_HIDE);
}

* This source code was highlighted with Source Code Highlighter.


Для примера я расположил две кнопки на форме. При нажатии на первую кнопку исчезает панель задач и кнопка Пуск одновременно. При желании, вы можете закомментировать отдельные строчки кода и скрывать только панель задач или только кнопку Пуск. Повторное нажатие на кнопку позволит снова увидеть любимую кнопку Пуск и панель задач.

Upd. Важное дополнение: в Windows XP и ниже кнопка Пуск является дочерним окном панели задач, и для получения ее описателя нужно использовать код
<code>
IntPtr startWnd = FindWindowEx(taskBarWnd, IntPtr.Zero, "BUTTON", null);
</code>


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

Без скриншотов, наверное, не обойтись. Здесь я скрыл панель задач, но оставил кнопку Пуск

А здесь я скрыл только область часов.


Несколько слов о кнопке Пуск. В Windows 98/XP скрыть кнопку можно было без проблем (смотри замечание выше). В Windows Vista вместо прямоугольной кнопки появилась круглая и изменилось ее поведение. Она уже не является дочерним окном панели задач. И приведенный здесь код работает наполовину. Можно скрыть одновременно панель задач и Пуск, или только панель задач. А скрыть Пуск, но оставить панель задач не получается. Кнопка не исчезает, а скукоживается. В поисках решения проблемы я набрел на парочку статей на CodeProject (http://www.codeproject.com/KB/miscctrl/Hide_Vista_Start_Orb_Simp.aspx и http://www.codeproject.com/KB/miscctrl/hide_vista_start_orb.aspx. Но, к сожалению, у меня эти примеры не заработали. Они предназначены для американской Windows 7, на русской Windows эффекта не наблюдается (попытка изменить в коде слово Start на Пуск успеха не имела).

Этот и другие примеры с функциями Windows API в среде .NET Framework можно найти в моем справочнике на сайте http://developer.alexanderklimov.ru/guide.php. Сам справочник платный, но, если вы обладаете свободным временем, то можете найти примеры к любой функции в интернете (Гугл вам в помощь). А также могу посоветовать очень полезный ресурс на английском pinvoke.net: the interop wiki!, на котором по принципу Википедии энтузиасты добавляют примеры, связанные с P/Invoke.
Счастливого вам программирования!

+3
6.4k 12
Support the author
Comments 31