Программа смены фона рабочего стола

от автора

Добрый день. У меня, на домашнем компе, в качестве обоев установлен черный цвет, и я к нему привык, мне удобно работать с таким фоном. На моем не домашнем компе, стоят обои, довольно светлые. При смене обоев на черный цвет, через некоторое время, обои возвращаются. Поменять обои на черный цвет не трудно: ПКМ->Персонализация->итд . Но я решил менять фон рабочего стола программно. Исходный текст на GitHub’е (https://github.com/bvr63/ChangeDesktopColor). Написана на C++, в VS2026. Программа циклически меняет фон рабочего стола: Файл ->Solid Color->Файл ->Solid Color->…

Ниже опишу некоторые моменты.
Программа консольная, для windows.При запуске программы я не хочу видеть окно консоли => окно консоли надо скрыть. В программе есть 2 варианта скрыть окно.

  1. HWND hWnd = GetConsoleWindow();
    ShowWindow(hWnd, SW_HIDE);
    Получить хендл окна, затем скрыть окно. При этом варианте окно создается, потом скрывается, есть эффект мелькания.

  2. Использовать директивы для линкера
    #pragma comment(linker, "/subsystem:windows")
    #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
    Первая директива предписывает линкеру собрать приложение как Windows GUI и искать точку входа WinMain(). Вторая директива — скрывает консоль и предписывает линкеру искать main(). При таком решении окно консоли вообще не создается, и как следствие, нет мелькания. Эти директивы применяются для всех конфигураций, кроме Debug.

#ifndef _DEBUG//скрыть консольное окно: либо программно скрыть его сразу после запуска //ShowWindow(hWnd, SW_HIDE), либо настроить проект так, чтобы оно вообще не создавалось // /subsystem:windows#pragma comment(linker, "/subsystem:windows")#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")#endif // _DEBUG

Менять фон рабочего стола можно через COM или WINAPI. В программе определена #define MyCOM для выбора используемой технологии.
//Если определена MyCOM, то обновление фона через СОМ, иначе через WINAPI
//#define MyCOM
Мне WINAPI больше нравится, я использовал его. COM оставил для информации.

В программе определено 3 конфигурации: Debug, Release, TEST. Конфигурация TEST наследована от Release. Для конфигурации TEST в свойствах проекта в разделе C/C++ -> Препроцессор -> Определения препроцессора добавлена _TEST .

Добавление _TEST

Добавление _TEST

Опишу TEST. Release отличается от TEST тем, что файлы обоев могут располагаться на разных дисках, в разных папках. При конфигурации TEST, файлы обоев должны располагаться в папке с программой и должны быть 0.jpg, 1.jpg, 2.jpg, 3.jpg, 4.jpg.
Если раскомментировать  #define _TEST, то можно отлаживать фактически конфигурацию TEST в конфигурации Debug.

#ifdef _TESTswitch (*argv[1]) {case '0':filePath = dir.wstring() + L"\\0.jpg";fileName = L"0.jpg";break;case '1':filePath = dir.wstring() + L"\\1.jpg";fileName = L"1.jpg";break;.....................................................            default:filePath = dir.wstring() + L"\\4.jpg";fileName = L"4.jpg";break;}#elseswitch (*argv[1]) {case '0':filePath = L"C:\\folder\\subfolder\\subfolder\\filename0.jpg";fileName = L"filename0.jpg";break;case '1':filePath = L"C:\\folder\\subfolder\\subfolder\\filename1.jpg";fileName = L"filename1.jpg";break;.....................................................            default:filePath = L"C:\\folder\\subfolder\\subfolder\\filename4.jpg";fileName = L"filename4.jpg";break;}#endif //_TEST

При запуске программы, определяем папку расположения программы. При запуске из командной строки argv[0]=»», поэтому используем GetModuleFileName. Для конфигурации Release нет необходимости определять папку запуска программы, так как файлы обоев содержат полные пути к файлам. Присваиваем filePath и fileName значения, которые будут использоваться по умолчанию.

#ifdef _TEST//Получаем путь расположения программыwchar_t pathTmp[MAX_PATH] = { L'\0' };GetModuleFileName(NULL, pathTmp, MAX_PATH);//std::filesystem::path dir = ((std::filesystem::path)argv[0]).parent_path();//из командной строки не работаетstd::filesystem::path dir = ((std::filesystem::path)pathTmp).parent_path();filePath = dir.wstring() + L"\\0.jpg";fileName = L"0.jpg";#elsefilePath = L"C:\\folder\\subfolder\\subfolder\\filename0.jpg";fileName = L"filename0.jpg";#endif // _TEST

Так как программа для личного использования, расположение файлов обоев прописаны в самом тексте
filePath = dir.wstring() + L»\\0.jpg»; //для конфигурации TEST
fileName = L»0.jpg»; //для конфигурации TEST
filePath = L»C:\\folder\\subfolder\\subfolder\\filename0.jpg»; //для конфигурации Release
fileName = L»filename0.jpg»; //для конфигурации Release

Проверяем, что сейчас установлено в качестве обоев

wchar_t wallFile[MAX_PATH] = { L'\0' };SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallFile, 0);wchar_t* ptr = wcsstr(wallFile, fileName.c_str());

Если в качестве обоев используется файл, то в wallFile будет имя файла, если используется сплошной цвет, то wallFile=»». В 3 строчке ищем вхождение fileName в wallFile. Если вхождение не найдено (используется сплошной цвет или другой файл), указатель ptr будет нулевым.
Кстати, строчки 1, 2 можно использовать для определения какой файл используется для обоев.

В зависимости от ptr и существует ли файл filePath устанавливаем либо файл в качестве обоев, либо сплошной цвет. И рассылаем сообщения всем окнам SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDWININICHANGE);

if (!ptr && std::filesystem::exists(filePath)) {BOOL result = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (void*)filePath.c_str(), SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);}else {//При этом варианте окно создается, потом скрывается, есть эффект мелькания//    HWND hWnd = GetConsoleWindow();//    ShowWindow(hWnd, SW_HIDE);//Задаем цветCOLORREF blackColor = RGB(0, 0, 0);#ifdef MyCOM//Изменение фона через COMHRESULT hr = ChangeDesktopBackgroundColor(blackColor);if (SUCCEEDED(hr)) {SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDWININICHANGE);}#else//Изменение фона через WINAPIif (SetDesktopSolidColor(blackColor)) {SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDWININICHANGE);}#endif // MyCOM    }

На этом собственно все о программе, ниже несколько слов о запуске и использовании шпаргалок в качестве обоев.

При компиляции в конфигурации TEST, файлы фона рабочего стола должны иметь названия 0.jpg, 1.jpg, 2.jpg, 3.jpg, 4.jpg и располагаться в папке программы. Запуск программы: ChangeDesktopColor.exe Х, где Х от 0 до … .

Примеры запуска

Файл для обоев

ChangeDesktopColor.exe

0.jpg

ChangeDesktopColor.exe 0

0.jpg

ChangeDesktopColor.exe 1

1.jpg

ChangeDesktopColor.exe 2

2.jpg

ChangeDesktopColor.exe 3

3.jpg

ChangeDesktopColor.exe 4

4.jpg

………………………….

………………………….

ChangeDesktopColor.exe 999

4.jpg

ChangeDesktopColor.exe 1 2

0.jpg

Исполняемый файл ChangeDesktopColor.exe скомпилированный в конфигурации TEST с образцами файлов обоев доступен по ссылке. Проверить целостность файла можно алгоритмом SHA256 (в PowerShell перейти в папку с программой и выполнить Get-FileHash ChangeDesktopColor.exe -Algorithm SHA256). Хэш файла ChangeDesktopColor.exe в таблице ниже.

Algorithm

Hash

SHA256

659323F608340C48DF42089E50D1561377A5E744A4FE7528F6F34018A57FEE54

Немножко про файл 0.jpg. Сейчас много мышек с большим количеством дополнительных кнопок. Используя фирменный софт, на дополнительные кнопки можно повесить комбинации клавиш, макросы, и тд и тп. Эти установки можно сохранять в разные файлы как разные профили и во время работы с разными программами/играми загружать необходимый профиль.
Я использую мышку Razer NAGA X. У нее 12 боковых кнопок + кнопка модификатор, которая еще добавляет 12 кнопок (аналог Shift на клавиатуре). Что бы они всегда были перед глазами, я сделал такой файл-шпаргалку и использую его как фон рабочего стола.

Комбинации клавиш на боковых кнопках мыши.

Комбинации клавиш на боковых кнопках мыши.

Можно наделать таких шпаргалок, и загружать их при необходимости. И еще раз про шпаргалки — файл 4.jpg — шутка, но почему бы и нет ?!

Спасибо за внимание, буду рад, если мой труд был не напрасен и кому-то окажется полезным.

ссылка на оригинал статьи https://habr.com/ru/articles/1022508/