Войти
ПрограммированиеСтатьиОбщее

Введение в программирование для магазина Windows

Внимание! Этот документ ещё не опубликован.

Автор:

Всем доброго времени суток!

Хочу рассказать Вам как написать своё первое приложение для магазина Windows на языке программирования C++.

Введение

Для того, чтобы начать писать приложения для магазина Windows, нужно следующее:

  1. Операционная система Windows 8
  2. Среда разработки для магазина Windows (подойдёт Visual Studio 2012 Express для Windows 8)
  3. Лицензия разработчика (выдаётся бесплатно)

А если Вы хотите разместить своё приложение в магазине Windows, то Вам нужно приобрести годовую подписку разработчика приложений для магазина Windows (на 21.02.2013 она стоила около 1500 рублей).

Установка нужного программного обеспечения не должна вызвать трудностей. При первом запуске Visual Studio Вам предложат зарегистрироваться и получить лицензию разработчика.

Разрабатывать приложение для магазина Windows мы будем на языке C++, используя Direct3D.

Управляемый C++

Хочу обратить Ваше внимание на то, что при разработке приложения для магазина Windows мы будем иметь дело со структурами и классами Windows API, за временем жизни которых следит специальный менеджер. Такой код называется управляемым. Для тех кто не знаком с этим, рассмотрим всё на практике. В неуправляемом C++ указатель мы объявляем следующим образом:

MyClass* p;

В управляемом C++ это делается другим способом:

MyClass^ p;

Всё что поменялось, так это символ *, на символ ^. Так же и создание таких классов выглядит по разному. В неуправляемом C++ мы создаём класс следующим образом:

MyClass* p=new MyClass();

В управляемом C++ это делается иначе:

MyClass^ p=ref new MyClass();

Обратите внимание, перед служебным словом new добавилось другое служебное слово - ref. Созданные объекты через ref new не нужно удалять вручную (забудьте про delete), когда они буду не нужны, они удалятся сами. Но и объявление управляемых классов от неуправляемых отличается. В неуправляемом C++ объявление класса делается следующим образом:

class MyClass
{
};

В управляемом C++ это делается так:

ref class MyClass
{
};

Разницу легко заметить (перед ключевым словом class добавилось ключевое слово ref). Все объекты, объявленные с помощью ключевого слова ref могут быть созданы только в куче (оператор ref new). Если же Вам нужно создать объект на стеке, то для этого используется другое ключевое слово при объявлении объекта:

value struct MyStruct
{
};

Перед ключевым словом struct добавили ключевое слово value. Поменялись и ссылки. В неуправляемом C++ ссылка обозначалась символом &, а в управляемом - %.

Ну и последнее что стоит сказать, так это про стандарт C++11 (C++0x). Стандарт позволяет использовать служебное слово final в объявлении класса, чтобы не иметь возможность от него наследоваться. Всё это будет работать, но только в случае неуправляемого кода. В управляемом коде аналогичный результат даёт служебное слово sealed (однако его же можно использовать и в неуправляемом C++). Выглядит это следующим образом:

ref class MyClass sealed
{
};

ИНФОРМАЦИЯ: Visual Studio вводит ещё одно служебное слово при объявлении объектов - abstract. Используя это служебное слово, Вы запретите создавать экземпляр данного объекта. Работает и для управляемого и для неуправляемого кода.

Совсем не обязательно использовать везде и повсюду управляемый C++. Я вот использую его только при работе со структурами и классами Windows API, свои же классы я пишу на неуправляемом C++.

Точка доступа и поставщики представлений

У каждой программы должна быть точка доступа (то место, с которого программа начинает свою работу). Выглядит она следующим образом:

[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^ _args)
{
  return 0;
}

Обратите внимание на [Platform::MTAThread]. Этой конструкцией мы разрешаем потоку нашего приложения управлять некоторыми другими потоками. А теперь рассмотрим параметр функции.

Windows API предоставляет нам, для удобства работы, пространство имён Platform с полезными классами (все они управляемые, то есть создавать через ref new и не удалять вручную). Как Вы видите в коде выше, сейчас мы используем от туда два управляемых класса: Array и String. Array - это массив, а String - это строка юникода (UTF-16LE). Получается что мы получаем массив из строк, который содержит параметры командной строки. На этом всё! Перейдём к тому, что должна сделать наша главная функция.

И так, что нам нужно для нормальной инициализации программы. Нам нужна фабрика, которая будет выдавать нам поставщиков представления (некая сущность, которая реализует поведение нашей программы). Для этого нам нужно создать управляемый класс и наследовать его от интерфейса IFrameworkViewSource. Мы реализуем необходимые его функции и скармливаем приложению. А лежит этот интерфейс в пространстве имён Windows::ApplicationModel::Core. Благо реализовать нам надо всего одну функцию, приступим к объявлению:

ref class ViewSource sealed : public Windows::ApplicationModel::Core::IFrameworkViewSource
{
  public:
    virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(void);
};

Реализовать нам надо функцию CreateView(), задача которой создать поставщика представления (логика нашей программы) и выдать нам на него интерфейс управления. Давайте объявим класс поставщика представления:

ref class View sealed : public Windows::ApplicationModel::Core::IFrameworkView
{
  public:
    virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ _applicationView);
    virtual void Load(Platform::String^ _str);
    virtual void Run(void);
    virtual void SetWindow(Windows::UI::Core::CoreWindow^ _window);
    virtual void Uninitialize(void);
};

Реализовать нам надо пять функций. Вот как они описаны в документации:

Пока мы реализуем их пустыми. В дальнейшем вложим в них некоторое поведение. Реализация:

void View::Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ _applicationView)
{
}

void View::Load(Platform::String^ _str)
{
}

void View::Run(void)
{
}

void View::SetWindow(Windows::UI::Core::CoreWindow^ _window)
{
}

void View::Uninitialize(void)
{
}

А теперь напишем реализацию для фабрики поставщиков представления:

Windows::ApplicationModel::Core::IFrameworkView^ ViewSource::CreateView(void)
{
  return ref new View();
}

Как видите, тут мы создаём наш класс с логикой программы (которой пока, собственно, и нет). Теперь осталось подправить главную функцию программы:

[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^ _args)
{
  Windows::ApplicationModel::Core::CoreApplication::Run(ref new ViewSource());
  return 0;
}

Тут стоит рассмотреть класс CoreApplication. В документации сказано что он позволяет приложениям обрабатывать изменения состояния, управлять окнами и интегрироваться с различными платформами пользовательского интерфейса. А его метод Run() выполняет внешнюю фабрику поставщиков представлений для получения поставщика представлений. Короче говоря, после всех этих операций у нас дело дойдёт до того, что последовательно запустятся функции Initialize(), SetWindow() и Run() у класса View (если мы что-то нахимичим в функции Initialize(), но у нас ещё и функция Load() запустится).

События

Теперь пришло время подписаться на несколько событий чтобы иметь возможность контролировать приложение в связи с изменившимися обстоятельствами. Сразу хочу сказать, что я не буду использовать никакие имена пространств по умолчанию (using namespace) чтобы никто не запутался какой класс откуда взялся (жаль только что это приведёт к больно длинному коду). Для начала рассмотрим три события:

  1. Активация представления
  2. Приостановка приложения
  3. Возобновление приложения

Перейдём к нашему объявлению класса View и допишем три функции:

void onActivated(Windows::ApplicationModel::Core::CoreApplicationView^ _applicationView,Windows::ApplicationModel::Activation::IActivatedEventArgs^ _args);
void onSuspending(Platform::Object^ _object,Windows::ApplicationModel::SuspendingEventArgs^ _args);
void onResuming(Platform::Object^ _object,Platform::Object^ _args);

Теперь реализуем их:

void View::onActivated(Windows::ApplicationModel::Core::CoreApplicationView^ _applicationView,Windows::ApplicationModel::Activation::IActivatedEventArgs^ _args)
{
  Windows::UI::Core::CoreWindow::GetForCurrentThread()->Activate();
}

void View::onSuspending(Platform::Object^ _object,Windows::ApplicationModel::SuspendingEventArgs^ _args)
{
}

void View::onResuming(Platform::Object^ _object,Platform::Object^ _args)
{
}

В момент активации представления вида мы активируем окно. Здесь всё!

Но сами по себе эти функции не сработают, нам надо на них подписаться. Сделаем мы это в момент инициализации:

void View::Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ _applicationView)
{
  _applicationView->Activated+=ref new Windows::Foundation::TypedEventHandler<Windows::ApplicationModel::Core::CoreApplicationView^,Windows::ApplicationModel::Activation::IActivatedEventArgs^>(this,&View::onActivated);
  Windows::ApplicationModel::Core::CoreApplication::Suspending+=ref new Windows::Foundation::EventHandler<Windows::ApplicationModel::SuspendingEventArgs^>(this,&View::onSuspending);
  Windows::ApplicationModel::Core::CoreApplication::Resuming+=ref new Windows::Foundation::EventHandler<Platform::Object^>(this,&View::onResuming);
}

Теперь не мешало бы уметь определять когда окно закрывается, когда меняется его размер и когда оно пропадает и снова появляется в поле видимости. Для этого объявим ещё несколько функций в нашем представлении вида:

void onSizeChanged(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::WindowSizeChangedEventArgs^ _args);
void onVisibilityChanged(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::VisibilityChangedEventArgs^ _args);
void onWindowClosed(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::CoreWindowEventArgs^ _args);

Реализуем их:

void View::onSizeChanged(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::WindowSizeChangedEventArgs^ _args)
{
}

void View::onVisibilityChanged(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::VisibilityChangedEventArgs^ _args)
{
}

void View::onWindowClosed(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::CoreWindowEventArgs^ _args)
{
}

Пока мы оставляем их без поведения, так как нам не с чем пока взаимодействовать. Но кое что скажу сразу. Если на счёт изменения размеров тут каждый поймёт зачем это нужно, то изменение видимости позволит нам не рисовать кадр если окно не видно. Ну а при получении события закрытия окна нам смело можно завершать работу приложения (например сохранить какие-то данные на жёстком диске). Позже мы вернёмся к ним. А теперь подпишемся на них (а раз они получают изменение окна, то где нам их подписывать?!):

void View::SetWindow(Windows::UI::Core::CoreWindow^ _window)
{
  _window->SizeChanged+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::WindowSizeChangedEventArgs^>(this,&View::onSizeChanged);
  _window->VisibilityChanged+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::VisibilityChangedEventArgs^>(this,&View::onVisibilityChanged);
  _window->Closed+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::CoreWindowEventArgs^>(this,&View::onWindowClosed);
}

Для простоты, я покажу работу с Direct2D, через инициализацию Direct3D. Но для работы с Direct2D нам нужно подписаться на ещё одно событие. Добавляем:

void onLogicalDpiChanged(Platform::Object^ _object);

Реализуем:

void View::onLogicalDpiChanged(Platform::Object^ _object)
{
}

В дальнейшем она будет взаимодействовать с Direct2D для изменения количества dpi (количество точек на линейный дюйм). Подписываемся там же, где и три вышестоящих события:

Windows::Graphics::Display::DisplayProperties::LogicalDpiChanged+=ref new Windows::Graphics::Display::DisplayPropertiesEventHandler(this,&View::onLogicalDpiChanged);

И осталось самое главное - пользовательское взаимодействие с экраном. Подпишемся ещё на пять событий:

  1. Вхождение указателя в область окна (PointerEntered)
  2. Указатель покинул область окна (PointerExited)
  3. Вождение указателя по области окна (PointerMoved)
  4. Прикосновение с экраном (PointerPressed)
  5. Завершение прикосновения с экраном (PointerReleased)

Добавляем:

void onPointerEntered(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args);
void onPointerExited(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args);
void onPointerMoved(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args);
void onPointerPressed(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args);
void onPointerReleased(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args);

Реализуем:

void View::onPointerEntered(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args)
{
}

void View::onPointerExited(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args)
{
}

void View::onPointerMoved(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args)
{
}

void View::onPointerPressed(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args)
{
}

void View::onPointerReleased(Windows::UI::Core::CoreWindow^ _window,Windows::UI::Core::PointerEventArgs^ _args)
{
}

Теперь подпишемся (всё там же):

_window->PointerEntered+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::PointerEventArgs^>(this,&View::onPointerEntered);
_window->PointerExited+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::PointerEventArgs^>(this,&View::onPointerExited);
_window->PointerMoved+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::PointerEventArgs^>(this,&View::onPointerMoved);
_window->PointerPressed+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::PointerEventArgs^>(this,&View::onPointerPressed);
_window->PointerReleased+=ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow^,Windows::UI::Core::PointerEventArgs^>(this,&View::onPointerReleased);

В общем то и всё! Получилось очень много пустого, но с возможностью внести логику.

Продолжение статьи здесь.

#windows, #Windows 8, #Windows 8 Store, #Windows Store, #WinRT, #магазин Windows, #магазин Windows 8

21 февраля 2013 (Обновление: 24 фев. 2013)

Комментарии [5]