Как сменить тему в Windows Mobile программноЗадался тут вопросом о программной смене темы в Windows Mobile. Оказывается это не так просто, как может показаться. Информация с MSDN’овских форумов и из самой MSDN мягко говоря не соответствует действительности. В Интернете полно топиков с подобными вопросами, но вменяемый результат выудить оттуда тоже почти нереально.

После нескольких часов мучений получил вроде вполне рабочий результат. А результатом, собственно, стал небольшой класс для установки и получения темы для Windows Mobile.

Но перед тем, как мы перейдем к рассмотрению примера, давайте, все же, определимся с последовательностью действий:

  • Имя файла текущей темы находится в реестре по адресу “HKEY_CURRENT_USER\Software\Microsoft\Today\Skin”
  • Первым шагом для смены темы в Windows Mobile является запуск утилиты \Windows\wcrload.exe c параметрами /noui /nouninstall /delete 0 “путь_к_файлу_темы.tsk” (кавычки нужны для того, чтобы правильно обрабатывались пути с пробелами. Это важно!).
  • Затем надо удалить в реестре значение по адресу “HKEY_LOCAL_MACHINE\Software\Microsoft\Color\BaseHue” иначе после применения темы изменится только фоновая картинка в Today, а цветовая схема останется прежней (тоже долго искал почему не применяется тема полностью, это тоже важный момент).
  • Затем надо удалить значение в реестре по адресу “HKEY_CURRENT_USER\Software\Microsoft\Today\UseStartImage”.
  • После этого нужно прописать прописать путь к файлу темы в реестре по адресу “HKEY_CURRENT_USER\Software\Microsoft\Today\Skin”.
  • И последнее, что нужно сделать, это разослать всем окнам сообщение об изменении настроек системы с помощью ::PostMessage(HWND_BROADCAST, WM_WININICHANGE, 0xF2, 0)

А теперь пример:

#ifndef _MOBILE_THEME_SWITCH_H
#define _MOBILE_THEME_SWITCH_H

#include <wx/wx.h>
#include <wx/filename.h>
#include <wx/msw/registry.h>

class MobileThemeSwitch
{
public:
	static wxString GetTheme()
	{
		wxString result;
		do 
		{
			wxRegKey key(wxRegKey::HKCU, wxT("Software\\Microsoft\\Today"));
			if(!key.Exists()) break;
			if(!key.Open(wxRegKey::Read)) break;
			const wxString skinValueName(wxT("Skin"));
			if(!key.HasValue(skinValueName)) break;
			if(!key.QueryValue(skinValueName, result)) break;
		} 
		while (false);
		if(result.IsEmpty()) result = _("default");
		return result;
	}
	static bool SetTheme(const wxString & value)
	{
		wxRegKey  * key(NULL);
		do 
		{
			wxString wceLoadFileName = wxT("\\Windows\\wceload.exe");
			if(!wxFileExists(wceLoadFileName)) 
			{
				wxLogDebug(_("'wceload.exe' does not exist"));
				break;
			}
			wxString commandLine = wxString::Format(
				wxT("%s /noui /nouninstall /delete 0 \"%s\""),
				wceLoadFileName.GetData(),
				value.GetData());
			long execResult = wxExecute(commandLine, wxEXEC_SYNC);
			if(execResult != 0)
			{
				wxLogDebug(_("'wceload.exe' returned error (%l)"), execResult);
				break;
			}
			key = new wxRegKey(wxRegKey::HKLM, wxT("Software\\Microsoft\\Color"));
			if(!key->Exists())
			{
				wxLogDebug(_("'Software\\Microsoft\\Color' registry key does not exist"));
				break;
			}
			if(!key->Open(wxRegKey::Write))
			{
				wxLogDebug(_("Unable to open registry key 'Software\\Microsoft\\Color'"));
				break;
			}
			const wxString baseHueValueName(wxT("BaseHue"));
			if(key->HasValue(baseHueValueName))
			{
				key->DeleteValue(baseHueValueName);
			}
			key->Close();
			wxDELETE(key);
			key = new wxRegKey(wxRegKey::HKCU, wxT("Software\\Microsoft\\Today"));
			if(!key->Exists())
			{
				wxLogDebug(_("'Software\\Microsoft\\Today' registry key does not exist"));
				break;
			}
			if(!key->Open(wxRegKey::Write))
			{
				wxLogDebug(_("Unable to open registry key 'Software\\Microsoft\\Today'"));
				break;
			}
			const wxString useStartImageValueName(wxT("UseStartImage"));
			if(key->HasValue(useStartImageValueName))
			{
				key->DeleteValue(useStartImageValueName);
			}
			const wxString skinValueName(wxT("Skin"));
			if(!key->SetValue(skinValueName, value))
			{
				wxLogDebug(_("Unable to change value 'Skin' in 'Software\\Microsoft\\Today'"));
				break;
			}
			key->Close();
			wxDELETE(key);
			::PostMessage(HWND_BROADCAST, WM_WININICHANGE, 0xF2, 0);
			return true;
		} 
		while (false);
		wxDELETE(key);
		return false;
	}
};

#endif

Скачать исходный код примера + исполняемый файл для Windows Mobile 6.0.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

Your email address will not be published. Required fields are marked *

О.

Отображаем анимированный GIF под Windows Mobile

Как-то печально обстоят дела с отображением анимации на устройствах под управлением Windows Mobile. Искал решение на .NET Compact Framework, нашел на Stack Overflow. Там предлагают писать собственный контрол, который будет делить изображение на кадры и отображать их с заданной периодичностью. Там в ответах ссылка на статью в MSDN. Собственно, везде это решение рекомендуют, но мне оно как-то не очень понравилось ввиду того, что позволяет отображать только специально подготовленное изображение, что во многих случаях очень неудобно.

А вот для тех, кто пишет ПО для Windows Mobile на C++ с wxWinCE эта проблема решается намного проще, с помощью wxAnimationCtrl. Вобще никаких лишних телодвижений делать не надо:

void wxAnimateMobileMainFrame::OnOPENClick( wxCommandEvent& event )
{
	wxFileDialog * dlg = new wxFileDialog(this, wxFileSelectorPromptStr, wxEmptyString,
		wxEmptyString, _("GIF Files (*.gif)|*.gif"));
	if(dlg->ShowModal() == wxID_OK)
	{
		m_AnimationCtrl->LoadFile(dlg->GetPath());
		m_AnimationCtrl->Play();
	}
	dlg->Destroy();
}

Из полезных возможностей:

  • Загрузка GIF-изображений с любым количеством кадров
  • Поддержка различных интервалов задержки для различных кадров
  • Циклическое воспроизведение (прописывается в самом GIF-файле)

Оказывается, все-таки, для некоторых задач C++ пользовать удобнее (хотя все равно для меня остается загадкой почему в .NET CF этого функционала нет).

Исходный код примера можно загрузить здесь.

П.

Первое приложение для Windows Mobile на C++/wxWinCE

В прошлый раз я рассказывал о том как собрать библиотеку wxWinCE для разработки приложений для Windows Mobile.

Сегодня мы поговорим о том, как начать программировать с использованием этой библиотеки и как создать простейшее приложение для Windows Mobile с ее помощью.

Итак, что у нас уже должно быть: собранная библиотека wxWinCE, создана переменная окружения WXWIN, которой присвоен путь к дистрибутиву wxWinCE, файлы статических библиотек размещены в папках:

  • $(WXWIN)/lib/evc_armv4_lib для платформы PocketPC 2003
  • $(WXWIN)/lib/evc_armv4t_lib для платформы Windows Mobile 6

Для начала нам необходимо создать новый проект. Для этого в Visual Studio выбираем пункт меню File -> New -> Project… В диалоговом окне создания нового проекта переходим в раздел Visual C++ -> Smart Device и указываем тип проекта Win32 Smart Device Project.

В мастере настройки свойств проекта выбираем платформы, для которых нам необходимо собрать наше приложение.

В разделе Application Settings выбираем тип проекта Windows Application и в Additional Options устанавливаем маркер на Empty Project.

Жмем Finish.

В созданный проект добавляем новый cpp-файл и пишем в нем следующее:

#include <wx/wx.h>

class wxWinCETestMainFrame : public wxFrame
{
protected:
	void OnExit(wxCommandEvent & event)
	{
		Close();
	}
public:
	wxWinCETestMainFrame()
		: wxFrame(NULL, wxID_ANY, _("wxWinCE Test"))
	{
		wxMenuBar * menuBar = new wxMenuBar;
		wxMenu * fileMenu = new wxMenu;
		fileMenu->Append(wxID_EXIT, _("Exit"));
		menuBar->Append(fileMenu, _("File"));
		SetMenuBar(menuBar);

		Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, 
			wxCommandEventHandler(wxWinCETestMainFrame::OnExit));
	}
};

class wxWinCETestApp : public wxApp
{
public:
	virtual bool OnInit()
	{
		wxWinCETestMainFrame * frame = new wxWinCETestMainFrame;
		SetTopWindow(frame);
		frame->Show();
		return true;
	}
};

IMPLEMENT_APP(wxWinCETestApp)

Открываем окно свойств проекта, переходим в раздел C/C++ -> General и в список каталогов для поиска заголовочных файлов (Additional Include Directories) добавляем следующие значения:

  • Для платформы PocketPC 2003
    • Debug
      • $(WXWIN)\include
      • $(WXWIN)\lib\evc_armv4_lib\winced
    • Release
      • $(WXWIN)\include
      • $(WXWIN)\lib\evc_armv4_lib\wince
  • Для платформы Windows Mobile 6
    • Debug
      • $(WXWIN)\include
      • $(WXWIN)\lib\evc_armv4t_lib\winced
    • Release
      • $(WXWIN)\include
      • $(WXWIN)\lib\evc_armv4t_lib\wince

В разделе Linker -> General в свойстве Additional Library Directories дописываем путь к статическим библиотекам wxWinCE:

  • $(WXWIN)\lib\evc_armv4_lib для платформы PocketPC 2003
  • $(WXWIN)\lib\evc_armv4t_lib для платформы Windows Mobile 6

В разделе C/C++ -> Code Generation устанавливаем свойство Enable C++ Exceptions в No (то же значение что и в проекте самой библиотеки wxWinCE) и для каждой конфигурации каждой платформы значение свойства Runtime Library устанавливаем в то значение, которое оно имеет в проекте библиотеки wxWinCE. Значение обоих этих свойств у нашего проекта и у проекта библиотеки должны быть одинаковыми.

В разделе C/C++ -> Preprocessor значение свойства Preprocessor Definitions для каждой конфигурации для каждой платформы устанавливаем то, которое оно имеет в проекте библиотеки wxWinCE. Лучше всего значение этого свойства скопировать с одного проекта в другой.

Теперь нам необходимо добавить в проект файл ресурсов (.rc) и в нем прописать следующее:


#include <wx/msw/wx.rc>

Это очень важный шаг настройки проекта, но о нем очень часто забывают. Зачем же это нужно? Затем что файл wx/msw/wx.rc содержит описание некоторых ресурсов приложения, которые используются при создании строк меню и панелей инструментов. Если этот файл не включить в сборку, то при создании строк меню будут постоянно возникать ошибки.

После того, как мы добавили файл ресурсов, в свойствах проекта идем в раздел Resources -> General и в Additional Include Directories добавляем значение $(WXWIN)\include.

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

После того как приложение успешно собрано, мы должны получить что-то подобное:

Скриншот минимального приложения для wxWinCE
Скриншот минимального приложения для wxWinCE

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

Напоследок хотелось бы сказать вот что: процесс настройки проекта на первом этапе знакомства с wxWidgets может показаться довольно сложным, но так кажется только вначале. Сам же процесс написания кода, добавления новых компонентов на формы, создание обработчиков событий довольно прост, намного проще чем с использованием MFC, особенно если для создания графического интерфейса использовать DialogBlocks, о котором я расскажу в следующий раз.