Добрый день. У меня, на домашнем компе, в качестве обоев установлен черный цвет, и я к нему привык, мне удобно работать с таким фоном. На моем не домашнем компе, стоят обои, довольно светлые. При смене обоев на черный цвет, через некоторое время, обои возвращаются. Поменять обои на черный цвет не трудно: ПКМ->Персонализация->итд . Но я решил менять фон рабочего стола программно. Исходный текст на 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 _TEST
		switch (*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;
		}
#else
		switch (*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";
#else
	filePath = 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
		//Изменение фона через COM
		HRESULT hr = ChangeDesktopBackgroundColor(blackColor);
		if (SUCCEEDED(hr)) {
			SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDWININICHANGE);
		}
#else
		//Изменение фона через WINAPI
		if (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 - шутка, но почему бы и нет ?!

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