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

Сразу же в моей голове появилась идея: "Почему бы не поставить эту картинку на фон рабочего стола?". Ведь у нас есть горячие клавиши, правда? Но вот беда – ожидается общественное негодование и взгляды укоризны. Сквозь страхи и сомнения родилась невероятная идея: я напишу код, который будет мгновенно менять фон рабочего стола по нажатию горячих клавиш! Да, именно так, уважаемые читатели, я решил смело потрясти мир своим выбором обоев.

И вот, внезапно, на сцене появляется наш герой – P/Invoke! Если вы, как и я, не были в ладах с C/C++, не отчаивайтесь – P/Invoke стоит на страже ваших инноваций. Дело в том, что у меня не было ни малейшего понятия о C/C++, но это не остановило меня. Сила P/Invoke заключается в том, что он позволяет вам вызывать функции из низкоуровневых библиотек так, будто они были написаны на чистом C#. Вот как это работает:

Регистрация горячих клавиш

Мы нацелились на две магические комбинации клавиш: одну для эротического фона (да-да, я так и назвал) и другую для обычного. Итак, RegisterHotKey из "user32.dll" становится нашим верным спутником:

[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

// Настройки для магических клавиш.
public const int MOD_ALT = 0x0001;
public const int MOD_CONTROL = 0x0002;
public const int MOD_SHIFT = 0x0004;

// Заклинания в виде клавиш. На этот раз мы выбрали "B" и "C".
public const int VK_B = 0x42;
public const int VK_C = 0x43;

// Ритуальные команды для вызова заклинаний. Наши числа имеют особое значение.
public const int WallpaperErroticCommand = 11; // Пора призвать эротику!
public const int WallpaperNormalCommand = 12; // А здесь - обычные обои.

if (RegisterHotKey(IntPtr.Zero, WallpaperErroticCommand, MOD_ALT | MOD_CONTROL, VK_B)
    && RegisterHotKey(IntPtr.Zero, WallpaperNormalCommand, MOD_SHIFT, VK_C))
{
    Console.WriteLine("Горячие клавиши успешно зарегистрированы. Время для волшебства!");
    Console.WriteLine("Нажмите Ctrl+Alt+B, чтобы погрузиться в мир эротики. Это наш секрет!");
    Console.WriteLine("Нажмите Shift+C, чтобы вернуться к обычным обоям. Магия тоже может быть невидимой.");
}
else
{
    Console.WriteLine("Не удалось зарегистрировать горячие клавиши. Магия чуть подвела нас.");
}

Получение сообщений

И вот он, грядущий момент! Наш код ждет, когда кто-то нажмет горячие клавиши. GetMessage, снова из "user32.dll", оказывается в роли почтового голубя, который приносит нам эти важные сообщения:

[DllImport("user32.dll")]
public static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);

public const int WM_HOTKEY = 0x0312;

MSG msg;
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
{
    // Дело в том, что когда наступает момент и горячая клавиша нажимается, 
    // код ощущает, что что-то важное произошло. Все благодаря таинственному WM_HOTKEY, 
    // сообщению, которое как будто шепчет: "Друг мой, нажата горячая клавиша!".
    if (msg.message != WM_HOTKEY) continue;
    
    // ...
}

Однако перед тем, как зажечь огонь фонов рабочего стола, я вынужден был призвать на помощь двух верных спутников – MSG и POINT.

// Структура MSG – это настоящий посланник между мирами.
// Она приносит с собой множество важных данных и оставляет на них свой отпечаток.
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
    public IntPtr hwnd;        // Окно, через которое проникают тайные сообщения.
    public uint message;       // Секретный код сообщения, как магическая формула.
    public IntPtr wParam;      // Внутренний атрибут для заклинаний.
    public IntPtr lParam;      // Еще одна загадочная составляющая.
    public uint time;          // Время, в которое произошла магия.
    public POINT pt;           // Точка, из которой мы начинаем свое магическое путешествие.
    public uint lPrivate;      // Загадочное личное свойство, которое всегда вызывает вопросы.

    // Магическая формула, раскрывающая суть MSG.
    public readonly override string ToString() =>
        $"hwnd: {hwnd}, message: {message}, wParam: {wParam}, lParam: {lParam}, time: {time}, pt: {pt}, lPrivate: {lPrivate}";
}

// А вот и наш друг POINT, тоже со своими тайнами.
// Он умеет указывать на самые глубокие секреты координат.
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int x;              // Секретная координата X, на которой начинается магия.
    public int y;              // И Y, естественно, не менее загадочный.

    // Интригующая загадка, которую POINT оставляет нам в наследство.
    public readonly override string ToString() =>
        $"x: {x}, y: {y}";
}

Установка обоев

Да-да, самая волнительная часть! Определяем, какой фон стоит поставить, и SystemParametersInfo возвращает нам победу или патовую ситуацию:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);

// Готовимся к великому замаху - выбору обоев!
var imagePath = msg.wParam switch
{
    WallpaperErroticCommand => WallpaperErroticPath,
    WallpaperNormalCommand => WallpaperNormalPath,
    _ => throw new InvalidOperationException($"Недопустимое значение команды {nameof(msg.wParam)}:{msg.wParam}")
};

// Поднимаем путеводный факел - адрес изображения!
var imagePtr = Marshal.StringToHGlobalUni(imagePath);

// Открываем ворота в мир фантазий - установка обоев!
if (!SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, imagePtr, 0))
{
    Console.WriteLine("Не удалось установить фон рабочего стола. Жизнь не по детски сложна, верно?");
}
else
{
    Console.WriteLine("Фон успешно установлен. Помните, обои – это ваше зеркало в мир!");
}

// Освобождаем место для других идей - освобождение памяти!
Marshal.FreeHGlobal(imagePtr);

Результат

И вот, наконец, наступило время раскрыть завесу и рассказать о результатах магических воздействий!

До нажатия Ctrl+Alt+B:

После нажатие Ctrl+Alt+B

И так, подготовьтесь к следующей главе моей истории. Нажимаю Shift+C:

Представьте, как будто мир вокруг меняется с каждым нажатием клавиши, как будто прикосновение волшебной палочки!

Заключение

Итак, что у нас получается? Не волнуйтесь, если мир C/C++ вам кажется далеким и непонятным, это совсем не повод опускать руки! P/Invoke – это наш надежный союзник в этом мире низкоуровневых библиотек. Так что даже если вы совсем не связаны с этими технологиями, не унывайте! Не бойтесь претворять ваши идеи в жизнь, даже если они затрагивают такой обыденный момент, как выбор обоев рабочего стола. Помните, одним маленьким шагом в коде вы можете придать жизнь всему окружающему миру!

Комментарии (12)


  1. impwx
    23.08.2023 12:35
    +1

    Почему "обратную" комбинацию выбрали именно Shift+C? Каждый раз, когда вы будете набирать заглавную букву С, обои будут слетать?


    1. Alexsey
      23.08.2023 12:35

      Открою тайну - есть люди, которые набирают заглавные буквы двойным нажатием на caps lock :)


      1. AgentFire
        23.08.2023 12:35
        +5


    1. Popou Автор
      23.08.2023 12:35

      ПХах, просто не подумал об этом


    1. qw1
      23.08.2023 12:35

      А можно перехватывать все буквы A-Z, и на каждую ставить свою картинку? Или только комбинации с shift/ctrl?


      1. naumenkoff
        23.08.2023 12:35

        Любую кнопку, какую захочешь, можно привязать. Фоновый поток и мультимедиа таймеры разрешением в 1 мс в помощь, да и я больше приверженец того, что кнопки хукать лучше с помощью GetAsyncKeyState и GetForegroundWindow для проверки активного окна.


        1. qw1
          23.08.2023 12:35

          Это надо самому крутить цикл, можно потерять нажатия, если не успел обработать.
          Мне больше нравится подписка на сообщение WM_INPUT


  1. ris58h
    23.08.2023 12:35
    +1

    я решил смело потрясти мир своим выбором обоев.

    Но выходит так что вы как раз решили наоборот.

    Offtopic:

    Было бы интересно генерировать обои по содержимому буфера обмена. Новое содержимое - новая картинка.


    1. Popou Автор
      23.08.2023 12:35

      А это идея....


      1. qw1
        23.08.2023 12:35
        +2

        Текст из буфера обмена передаётся в stable diffusion, и результат ставится на рабочий стол )))


        1. s207883
          23.08.2023 12:35
          +3

          Какие хтонические обои получатся


  1. domix32
    23.08.2023 12:35

    Интересно конечно, но у винды уже есть несколько десктопов, которые можно переключать включая горячие клавиши для переключения (Win+Ctrl+Left/Right). Так что не знаю что вы там наоживляли.