У каждого из нас есть свои маленькие радости, даже на фоне серой рутины. Для меня, как для обычного смертного, это был стандартный фон рабочего стола в 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)
ris58h
23.08.2023 12:35+1я решил смело потрясти мир своим выбором обоев.
Но выходит так что вы как раз решили наоборот.
Offtopic:
Было бы интересно генерировать обои по содержимому буфера обмена. Новое содержимое - новая картинка.
domix32
23.08.2023 12:35Интересно конечно, но у винды уже есть несколько десктопов, которые можно переключать включая горячие клавиши для переключения (Win+Ctrl+Left/Right). Так что не знаю что вы там наоживляли.
impwx
Почему "обратную" комбинацию выбрали именно Shift+C? Каждый раз, когда вы будете набирать заглавную букву С, обои будут слетать?
Alexsey
Открою тайну - есть люди, которые набирают заглавные буквы двойным нажатием на caps lock :)
AgentFire
Popou Автор
ПХах, просто не подумал об этом
qw1
А можно перехватывать все буквы A-Z, и на каждую ставить свою картинку? Или только комбинации с shift/ctrl?
naumenkoff
Любую кнопку, какую захочешь, можно привязать. Фоновый поток и мультимедиа таймеры разрешением в 1 мс в помощь, да и я больше приверженец того, что кнопки хукать лучше с помощью GetAsyncKeyState и GetForegroundWindow для проверки активного окна.
qw1
Это надо самому крутить цикл, можно потерять нажатия, если не успел обработать.
Мне больше нравится подписка на сообщение WM_INPUT