Поделиться через


Перенос примера приложения редактора фотографий UWP на Windows App SDK (C++/WinRT)

В этом разделе описывается пример приложения C++/WinRT UWP Photo Editor и перенос его в Windows App SDK.

Внимание

Рекомендации и стратегии для подхода к процессу миграции и настройке среды разработки для миграции см. в статье "Общая стратегия миграции".

Установка средств для Windows App SDK

Сведения о настройке компьютера разработки см. в статье Install tools for the Windows App SDK.

Внимание

Вы найдете темы заметок о новом выпуске совместно с темой каналы выпуска Windows App SDK. Существуют релизные заметки для каждого канала. Не забудьте проверить все ограничения и известные проблемы в этих примечаниях к выпуску, так как они могут повлиять на результаты исследования данного примера и/или запуска перенесенного приложения.

Создание проекта

  • В Visual Studio создайте проект C++/WinRT из шаблона проекта WinUI Blank App (Packaged). Назовите проект PhotoEditor, снимите флажок Поместить решение и проект в одном каталоге. В качестве целевой версии вы можете выбрать последний выпуск (не предварительную версию) операционной системы клиента.

Примечание.

Мы будем ссылаться на версию UWP проекта (клонированную из его репозитория) как на исходное решение/проект. Мы будем называть версию Windows App SDK целевым решением/проектом.

Порядок переноса кода

MainPage — это важная и видная часть приложения. Но если бы мы начали переносить это, то вскоре мы поняли, что MainPage имеет зависимость от представления DetailPage; а затем, что DetailPage имеет зависимость от модели Photo. Поэтому для этого пошагового руководства мы рассмотрим этот подход.

  • Начнем с копирования файлов ресурсов.
  • Затем мы переносим модель фото .
  • Далее мы переносим класс App (так как для этого требуются некоторые члены, добавляемые в него, от этого будет зависеть DetailPage и MainPage).
  • Затем мы начнем миграцию представлений, начиная с DetailPage.
  • И мы завершим работу, перенеся представление MainPage.

Мы будем копировать все файлы исходного кода

В этом пошаговом руководстве мы будем копировать файлы исходного кода с помощью File Explorer. Если вы предпочитаете копировать содержимое файла , затем см. раздел Приложение: копирование содержимого файлов модели Photo в конце этой темы, чтобы увидеть пример, как это можно сделать для Photo (и затем вы можете применить аналогичную процедуру к другим типам в проекте). Этот вариант включает гораздо больше шагов, однако.

Копирование файлов активов

  1. В клоне исходного project, в File Explorer найдите папку Windows-appsample-photo-editor>PhotoEditor>Assets. В этой папке вы найдете восемь файлов ресурсов. Выберите эти восемь файлов и скопируйте их в буфер обмена.

  2. Кроме того, в File Explorer теперь найдите соответствующую папку в созданном целевом project. Путь к этой папке — PhotoEditor>PhotoEditor>Assets. Вставьте в эту папку файлы ресурсов, скопированные только что, и примите запрос на замену семи файлов, которые уже существуют в назначении.

  3. В целевом проекте в Visual Studio в Обозревателе решений разверните папку Assets. Добавьте в эту папку ресурсный файл bg1.png, который вы только что вставили. Наведите указатель мыши на ресурсные файлы. Миниатюры будут отображаться для каждого, подтверждая, что вы правильно заменили или добавили файлы ресурсов.

Перенести модель фотографии

Фотография — это класс среды выполнения, представляющий фотографию. Это модель (в смысле моделей, представлений и моделей представления).

Копирование файлов исходного кода фотографии

  1. В вашем клоне исходного проекта в Проводнике, найдите папку Windows-appsample-photo-editor>PhotoEditor. В этой папке вы найдете три файла исходного кодаPhoto.idl, Photo.hа Photo.cppтакже эти файлы вместе реализуют класс среды выполнения Photo. Выберите эти три файла и скопируйте их в буфер обмена.

  2. В Visual Studio щелкните правой кнопкой мыши целевой проект и выберите Открыть папку в Проводнике. Откроется целевая папка project в File Explorer. Вставьте в эту папку только что скопированные три файла.

  3. Вернитесь в Solution Explorer с выбранным целевым узлом проекта и убедитесь, что Показать все файлы включена. Щелкните правой кнопкой мыши три файла, которые вы только что вставили, и щелкните Include In Project. Переключатель "Показать все файлы " отключен.

  4. В исходном проекте в Solution Explorer, Photo.h и .cpp вложены в Photo.idl, чтобы указать, что они создаются на основе (зависят от него). Если вам нравится это расположение, то вы можете сделать то же самое в целевом project, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxproj (сначала необходимо Сохранить все в Visual Studio). Найдите следующее:

    <ClInclude Include="Photo.h" />
    

    И замените его следующим образом:

    <ClInclude Include="Photo.h">
      <DependentUpon>Photo.idl</DependentUpon>
    </ClInclude>
    

    Повторите это для Photo.cpp и сохраните и закройте файл project. Когда вы возвращаетесь к Visual Studio, нажмите Reload.

Перенос исходного кода фотографии

  1. В Photo.idl найдите имя пространства имен Windows.UI.Xaml (которое является пространством имен для XAML UWP), и измените его на Microsoft.UI.Xaml (которое является пространством имен для XAML WinUI 3).

Примечание.

Тема Сопоставление API UWP с Windows App SDK предоставляет сравнение API UWP с их эквивалентами в Windows App SDK. Приведенное выше изменение является примером изменения имени пространства имен, необходимого во время процесса миграции.

  1. Добавьте Photo.cpp#include "Photo.g.cpp" в существующие директивы include сразу после#include "Photo.h". Это одно из различий в именах папок и файлов (C++/WinRT), которые следует учитывать между проектами UWP и Windows App SDK.

  2. Сделайте следующее средство поиска и замены (регистр соответствия и целое слово) в содержимом всего исходного кода в файлах, которые вы только что скопировали и вставили.

    • Windows::UI::Xaml =>Microsoft::UI::Xaml
  3. Из pch.h в исходном project скопируйте приведенные ниже элементы и вставьте их в pch.h в целевом project. Это подмножество файлов заголовков, включенных в исходный проект; это только заголовки, которые необходимы для поддержки кода, который мы перенесли до настоящего момента.

    #include <winrt/Microsoft.UI.Xaml.Media.Imaging.h>
    #include <winrt/Windows.Storage.h>
    #include <winrt/Windows.Storage.FileProperties.h>
    #include <winrt/Windows.Storage.Streams.h>
    
  4. Теперь убедитесь, что вы можете создать целевое решение (но пока не запускайте его).

Перенос класса App

Никаких изменений не требуется для целевого проекта App.idl и App.xaml. Но нам необходимо изменить App.xaml.h, а также App.xaml.cpp, чтобы добавить некоторые новые члены в класс App. Мы будем делать это так, чтобы можно было продолжать работу после каждого раздела (за исключением последнего раздела, который касается App::OnLaunched).

Предоставление доступа к объекту основного окна

На этом шаге мы внесем изменение, описанное в Change Windows.UI.Xaml.Window.Current to App.Window.

В целевом проекте App сохраняет основной объект окна в своём закрытом элементе данных window. Далее в процессе миграции (когда мы переносим использование Window.Current из исходного проекта), будет удобно, если этот window является статическим членом данных и может быть доступен через метод доступа. Таким образом, мы будем вносить эти изменения далее.

  • Так как мы делаем окно статическим, мы должны инициализировать его App.xaml.cpp вместо инициализатора элемента по умолчанию, который в настоящее время использует код. Ниже приведены те изменения, которые выглядят в App.xaml.h и App.xaml.cpp.

    // App.xaml.h
    ...
    struct App : AppT<App>
    {
         ...
         static winrt::Microsoft::UI::Xaml::Window Window(){ return window; };
    
    private:
         static winrt::Microsoft::UI::Xaml::Window window;
    };
    ...
    
    // App.xaml.cpp
    ...
    winrt::Microsoft::UI::Xaml::Window App::window{ nullptr };
    ...
    

App::OnNavigationFailed

Пример приложения "Редактор фотографий" использует логику навигации для перехода между MainPage и DetailPage. Дополнительные сведения о Windows App SDK приложениях, которым требуется навигация (и те, которые нет), см. в разделе Необходимо реализовать навигацию по страницам?.

Таким образом, члены, которые будут перенесены в следующих нескольких разделах, существуют для поддержки навигации в приложении.

  1. Начнем с миграции обработчика событий OnNavigationFailed . Скопируйте объявление и определение этой функции-члена из исходного проекта и вставьте их в целевой проект (в App.xaml.h и App.xaml.cpp).

  2. В коде, который вы вставили в App.xaml.h, измените Windows::UI::Xaml на Microsoft::UI::Xaml.

App::CreateRootFrame

  1. Исходный project содержит вспомогательные функции с именем App::CreateRootFrame. Скопируйте объявление и определение вспомогательной функции из исходного проекта и вставьте их в целевой проект (в App.xaml.h и App.xaml.cpp).

  2. В коде, который вы вставили в App.xaml.h, измените Windows::UI::Xaml на Microsoft::UI::Xaml.

  3. В коде, который вы вставили в App.xaml.cpp, измените два вхождения Window::Current() на window (это имя члена данных класса App, который мы видели ранее).

App::OnLaunched

Целевой проект уже содержит реализацию обработчика событий OnLaunched. Его параметр является константной ссылкой на Microsoft::UI::Xaml::LaunchActivatedEventArgs, что правильно для Windows App SDK (в отличие от исходного проекта, который использует Windows::ApplicationModel::Activation::LaunchActivatedEventArgs, что правильно для UWP).

  • Необходимо просто объединить два определения (исходное и целевое) OnLaunched, чтобы App::OnLaunched в App.xaml.cpp в целевом проекте должен выглядеть следующим образом. Обратите внимание, что он использует window (вместо Window::Current()версии UWP).

    void App::OnLaunched(LaunchActivatedEventArgs const&)
    {
         window = make<MainWindow>();
    
         Frame rootFrame = CreateRootFrame();
         if (!rootFrame.Content())
         {
             rootFrame.Navigate(xaml_typename<PhotoEditor::MainPage>());
         }
    
         window.Activate();
    }
    

Данный код делает App зависимым от MainPage, поэтому мы не сможем выполнить сборку, пока не перенесем DetailPage, а затем MainPage. Когда мы сможем строить снова, мы об этом сообщим.

Перенос представления DetailPage

DetailPage — это класс, представляющий страницу редактора фотографий, где эффекты Win2D переключаются, задаются и объединяются. Перейдите на страницу редактора фотографий, выбрав эскиз фотографии в MainPage. DetailPage — это представление (в смысле моделей, представлений и моделей представлений).

Ссылка на пакет NuGet Win2D

Для поддержки кода в DetailPage исходный проект имеет зависимость от Microsoft.Graphics.Win2D. Поэтому нам также потребуется зависимость от Win2D в нашем целевом проекте.

  • В целевом решении в Visual Studio щелкните Tools>NuGet Package Manager>Manage NuGet Packages for Solution... >Browse. Убедитесь, что флажок "Включить предварительную версию " снят и введите или вставьте Microsoft.Graphics.Win2D в поле поиска. Выберите правильный элемент в результатах поиска, проверьте PhotoEditor project и щелкните Install, чтобы установить пакет.

Копирование файлов исходного кода DetailPage

  1. В вашем клоне исходного проекта в Проводнике, найдите папку Windows-appsample-photo-editor>PhotoEditor. В этой папке вы найдете четыре файла исходного кода DetailPage.idl, DetailPage.xamlDetailPage.hиDetailPage.cpp; эти файлы вместе реализуют представление DetailPage. Выберите эти четыре файла и скопируйте их в буфер обмена.

  2. В Visual Studio щелкните правой кнопкой мыши целевой проект и выберите Открыть папку в Проводнике. Откроется целевая папка project в File Explorer. Вставьте в эту папку только что скопированные четыре файла.

  3. По-прежнему в File Explorer измените имена DetailPage.h и DetailPage.cpp на DetailPage.xaml.h и DetailPage.xaml.cpp соответственно. Это одно из различий в именах папок и файлов (C++/WinRT), которые следует учитывать между проектами UWP и Windows App SDK.

  4. Вернитесь в Solution Explorer с выбранным целевым узлом проекта и убедитесь, что Показать все файлы включена. Щелкните правой кнопкой мыши четыре файла, которые вы только что вставили (и переименовали), и щелкните Include In Project. Переключатель "Показать все файлы " отключен.

  5. В исходном проекте в Solution ExplorerDetailPage.idl вложено под DetailPage.xaml. Если вам нравится это расположение, то вы можете сделать то же самое в целевом project, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxproj (сначала необходимо Сохранить все в Visual Studio). Найдите следующее:

    <Midl Include="DetailPage.idl" />
    

    И замените его следующим образом:

    <Midl Include="DetailPage.idl">
      <DependentUpon>DetailPage.xaml</DependentUpon>
    </Midl>
    

Сохраните и закройте файл project. Когда вы возвращаетесь к Visual Studio, нажмите Reload.

Перенос исходного кода DetailPage

  1. В DetailPage.idl, найдите Windows.UI.Xamlи измените это на Microsoft.UI.Xaml.

  2. В DetailPage.xaml.cpp измените #include "DetailPage.h" на #include "DetailPage.xaml.h".

  3. Сразу после этого добавьте #include "DetailPage.g.cpp".

  4. Для того чтобы вызов статического метода App::Window (который мы собираемся добавить) успешно скомпилировался, в DetailPage.xaml.cpp добавьте #include "App.xaml.h" непосредственно перед #include "Photo.h".

  5. Выполните указанные ниже действия поиска и замены (регистр соответствия и целое слово) в содержимом исходного кода в только что скопированных и вставленных файлах.

    • В DetailPage.xaml.h и .xaml.cpp, Windows::UI::Composition =>Microsoft::UI::Composition
    • В DetailPage.xaml.h и .xaml.cpp, Windows::UI::Xaml =>Microsoft::UI::Xaml
    • In DetailPage.xaml.cpp, Window::Current() =>App::Window()
  6. Из pch.h в исходном project скопируйте приведенные ниже элементы и вставьте их в pch.h в целевом project.

    #include <winrt/Windows.Graphics.Effects.h>
    #include <winrt/Microsoft.Graphics.Canvas.Effects.h>
    #include <winrt/Microsoft.Graphics.Canvas.UI.Xaml.h>
    #include <winrt/Microsoft.UI.Composition.h>
    #include <winrt/Microsoft.UI.Xaml.Input.h>
    #include <winrt/Windows.Graphics.Imaging.h>
    #include <winrt/Windows.Storage.Pickers.h>
    
  7. Кроме того, в верхней части pch.h, сразу после #pragma onceэтого добавьте следующее:

    // This is required because we are using std::min and std::max, otherwise 
    // we have a collision with min and max macros being defined elsewhere.
    #define NOMINMAX
    

Мы пока ещё не можем приступить к работе, но сможем после миграции MainPage (которая будет следующей).

Перенос вида MainPage

Главная страница приложения представляет представление, которое вы увидите сначала при запуске приложения. Это страница, которая загружает фотографии из Библиотеки изображений и отображает плиточное представление миниатюр.

Копирование файлов исходного кода MainPage

  1. Аналогично тому, что вы сделали с DetailPage, теперь скопируйте MainPage.idl, MainPage.xaml, MainPage.h и MainPage.cpp.

  2. Переименуйте файлы .h и .cpp на .xaml.h и .xaml.cpp соответственно.

  3. Включите все четыре файла в целевой проект, как и раньше.

  4. В исходном проекте в Solution ExplorerMainPage.idl вложено под MainPage.xaml. Если вам нравится этот порядок, то вы можете сделать то же самое в целевом проекте, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxproj. Найдите следующее:

    <Midl Include="MainPage.idl" />
    

    И замените ее на:

    <Midl Include="MainPage.idl">
      <DependentUpon>MainPage.xaml</DependentUpon>
    </Midl>
    

Перенос исходного кода MainPage

  1. В MainPage.idl выполните поиск Windows.UI.Xaml, и измените оба вхождения на Microsoft.UI.Xaml.

  2. В MainPage.xaml.cpp измените #include "MainPage.h" на #include "MainPage.xaml.h".

  3. Сразу после этого добавьте #include "MainPage.g.cpp".

  4. Для того чтобы вызов статического метода App::Window (который мы собираемся добавить) успешно скомпилировался, в MainPage.xaml.cpp добавьте #include "App.xaml.h" непосредственно перед #include "Photo.h".

На следующем шаге мы добавим изменения, описанные в разделе ContentDialog и Popup.

  1. Таким образом, по-прежнему в MainPage.xaml.cppметоде MainPage::GetItemsAsync сразу после строки ContentDialog unsupportedFilesDialog{};добавьте эту строку кода.

    unsupportedFilesDialog.XamlRoot(this->Content().XamlRoot());
    
  2. Выполните указанные ниже действия поиска и замены (регистр соответствия и целое слово) в содержимом исходного кода в только что скопированных и вставленных файлах.

    • В MainPage.xaml.h и .xaml.cpp, Windows::UI::Composition =>Microsoft::UI::Composition
    • В MainPage.xaml.h и .xaml.cpp, Windows::UI::Xaml =>Microsoft::UI::Xaml
    • In MainPage.xaml.cpp, Window::Current() =>App::Window()
  3. Из pch.h в исходном project скопируйте приведенные ниже элементы и вставьте их в pch.h в целевом project.

    #include <winrt/Microsoft.UI.Xaml.Hosting.h>
    #include <winrt/Microsoft.UI.Xaml.Media.Animation.h>
    #include <winrt/Windows.Storage.Search.h>
    

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

Обновите MainWindow

  1. Удалите MainWindow.xamlStackPanel и его содержимое, так как нам не нужен пользовательский интерфейс в MainWindow. Это оставляет только пустой элемент Window .

  2. Удалите в MainWindow.idl заполнитель Int32 MyProperty;, оставив только конструктор.

  3. В MainWindow.xaml.h и MainWindow.xaml.cpp, удалите объявления и определения заполнителя MyProperty и myButton_Click, оставив только конструктор.

Изменения в миграции, необходимые для различий в модели потоков

Эти два изменения в этом разделе необходимы из-за разницы в модели потоков между UWP и Windows App SDK, как описано в разделе модели потоков ASTA и STA. Ниже приведены краткие описания причин проблем, а затем способ устранения каждого из них.

Главная Страница

MainPage загружает файлы изображений из папки Pictures вызывает StorageItemContentProperties.GetImagePropertiesAsync для получения свойств файла изображения, создает объект модели Photo для каждого файла изображения (сохранение этих же свойств в члене данных), и добавляет этот объект Photo в коллекцию. Коллекция объектов Photo привязана к GridView в пользовательском интерфейсе. От имени этого GridView, MainPage обрабатывает событие ContainerContentChanging, и для этапа 1 этот обработчик вызывает корутину, которая вызывает StorageFile.GetThumbnailAsync. Этот вызов GetThumbnailAsync приводит к перекачиваемым сообщениям (он не возвращается немедленно и выполняет все его асинхронное выполнение) и приводит к повторному входу. В результате коллекция объектов в GridView изменена в процессе выполнения макета, что вызывает сбой.

Если мы закомментируем вызов StorageItemContentProperties::GetImagePropertiesAsync, то мы не получаем сбой. Но реальное исправление заключается в том, чтобы вызов StorageFile.GetThumbnailAsync был явно асинхронным путем совместного ожидания wil::resume_foreground непосредственно перед вызовом GetThumbnailAsync. Это работает, потому что wil::resume_foreground назначает код, следующий за ним, задачей в DispatcherQueue.

Ниже приведен код для изменения:

// MainPage.xaml.cpp
IAsyncAction MainPage::OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    ...
    if (args.Phase() == 1)
    {
        ...
        try
        {
            co_await wil::resume_foreground(this->DispatcherQueue());
            auto thumbnail = co_await impleType->GetImageThumbnailAsync(this->DispatcherQueue());
            image.Source(thumbnail);
        }
        ...
    }
}

Фото

Свойство Photo::ImageTitle привязано к пользовательскому интерфейсу, поэтому пользовательский интерфейс вызывает функцию доступа для этого свойства при необходимости его значения. При попытке получить доступ к ImageProperties.Title из этой функции доступа в потоке пользовательского интерфейса мы получаем нарушение доступа.

Таким образом, мы можем получить доступ к Title однократно из конструктора Photo, и сохранить его в элементе данных m_imageName, если он не пуст. Затем в функции доступа Photo::ImageTitle необходимо только обращаться к члену данных m_imageName.

Ниже приведен код для изменения:

// Photo.h
...
Photo(Photo(Windows::Storage::FileProperties::ImageProperties const& props,
    ...
    ) : ...
{
    if (m_imageProperties.Title() != L"")
    {
        m_imageName = m_imageProperties.Title();
    }
}
...
hstring ImageTitle() const
{
    return m_imageName;
}
...

Это последние изменения, которые необходимо внести для переноса примера приложения "Редактор фотографий". В разделе "Тестирование перенесенного приложения" мы убедимся, что мы правильно выполнили действия.

Известные проблемы

Проблема с типом приложения (влияет только на предварительную версию 3)

Если вы воспользовались данным примером с использованием шаблона проекта из VSIX для Windows App SDK версия 1.0 Preview 3, вам необходимо будет внести небольшую коррекцию в PhotoEditor.vcxproj. Вот как это сделать.

В Visual Studio в Solution Explorer щелкните правой кнопкой мыши узел project и щелкните Unload Project. Теперь PhotoEditor.vcxproj открыт для редактирования. В качестве первого дочернего элемента Project добавьте элемент PropertyGroup следующим образом:

<Project ... >
    <PropertyGroup>
        <EnableWin32Codegen>true</EnableWin32Codegen>
    </PropertyGroup>
    <Import ... />
...

Сохраните и закройте PhotoEditor.vcxproj. Щелкните правой кнопкой мыши узел project и щелкните Reload Project. Теперь заново соберите проект.

Тестирование перенесенного приложения

Теперь создайте project и запустите приложение, чтобы протестировать его. Выберите изображение, задайте уровень масштабирования, выберите эффекты и настройте их.

Приложение: копирование содержимого файлов модели Photo

Как мы уже говорили ранее, вы можете скопировать файлы исходного кода самостоятельно или содержимое файлов исходного кода. Мы уже показали, как скопировать файлы исходного кода самостоятельно. Поэтому в этом разделе приведен пример копирования содержимого файла.

В исходном project в Visual Studio найдите папку PhotoEditor (универсальная версия Windows)>Models. В этой папке содержатся файлы Photo.idlи Photo.hPhoto.cpp, которые вместе реализуют класс среды выполнения Photo.

Добавьте IDL и создайте заглушки

В целевом проекте в Visual Studio добавьте новый элемент Midl-файл (.idl) в проект. Присвойте новому элементу имя Photo.idl. Удалите содержимое Photo.idlпо умолчанию.

Из исходного project в Visual Studio скопируйте содержимое Models>Photo.idl и вставьте их в файл Photo.idl, который вы только что добавили в целевой project. В вставленном коде выполните поиск Windows.UI.Xaml, и измените это на Microsoft.UI.Xaml.

Сохраните файл.

Внимание

Мы собираемся выполнить сборку вашего целевого решения. Сборка не будет выполняться до завершения на этом этапе, но продвинется достаточно, чтобы выполнить необходимую работу для нас.

Теперь создайте целевое решение. Несмотря на то, что процесс сборки не завершится, он необходим, так как создаст файлы исходного кода (заглушки), которые нам нужны для начала работы над внедрением модели Photo.

В Visual Studio щелкните правой кнопкой мыши целевой проект и выберите Открыть папку в Проводнике. Откроется целевая папка project в File Explorer. Перейдите в папку Generated Files\sources, чтобы оказаться в \PhotoEditor\PhotoEditor\PhotoEditor\Generated Files\sources. Скопируйте файлы-заглушки Photo.h и .cpp и вставьте их в папку проекта, которая теперь находится на два уровня выше в \PhotoEditor\PhotoEditor\PhotoEditor.

Вернитесь в Solution Explorer с выбранным целевым узлом проекта и убедитесь, что Показать все файлы включена. Щелкните правой кнопкой мыши только что вставленные файлы (Photo.h и .cpp) и щелкните Include In Project. Переключатель "Показать все файлы " отключен.

Вы увидите static_assert в верхней части содержимого Photo.h и .cpp, который нужно удалить.

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

Перенос кода в заглушки

Скопируйте содержимое Photo.h и .cpp из исходного проекта в целевой проект.

Отсюда оставшиеся шаги для переноса скопированного кода такие же, как в разделе Миграция исходного кода Фото.