Кросс-платформенная разработка — Windows Mobile и Windows (.NET Compact Framework, C#)

    Не так много разработчиков осознают, что разрабатывая приложения для платформы Windows Mobile с использованием Compact Framework, у них существуют шансы собрать это же приложение под десктоп версию Windows! Я и сам об этом долгое время только задумывался, предполагая, что подобная возможность есть, но не рассматривал её как нечто, хоть сколько-нибудь реальное.

    Существует несколько важных аспектов, которые нужно понимать, чтобы успешно организовать кросс-платформенную сборку. Я потратил прилично времени, собирая обломки знаний в разных местах сети, причём местами было настолько нетривиально, что решил поделиться с хабрасообществом тонкостями.


    Во-первых, во-вторых, в-третьих...


    Во-первых, необходимо изначально создавать приложение для Windows Mobile (т.е. это основная платформа). Это действительно важно. Причин несколько, но основная заключается в том, что Compact Framework на то и компактный, что там существенно меньше классов и свойств у классов. Т.е. совместимость с десктопом есть, но односторонняя, т.е. только в сторону десктопа.

    Во-вторых, нужно понимать, что отличия в приложении всё-таки будут и их придётся программировать отдельно. Например, стандартное меню, находящееся внизу на Windows Mobile автоматически перемещается наверх, и там Cancel и More выглядят не очень привлекательно. Далее, на десктопе в принципе нет InputPanel. Т.е. по сути нужно быть готовым к инструкциям компилятору #if #else #endif.

    В-третьих, надо также готовиться к тому, что будут некоторые ограничения, касающиеся дизайна форм. А именно, нельзя открывать форму визуальным редактором при десктопе, выбранном в качестве текущего таргета — сразу же в *.Designer.cs налетит множество свойств, не поддерживаемых мобильным фреймворком — придётся откатывать.

    В-четвёртых, придётся вручную править *.csproj файлы и видеть в Solution Explorer-е жёлтые треугольники — это нормально :)

    В пятых, не все сборки и неймспесы на 100% работают на десктопе. Например, я совершенно не уверен в том, что SQL Server Compact собирается на десктопе. Не проверял, поэтому не обещаю. Точно знаю, что с SQLite всё хорошо (хотя и придётся попотеть немного).

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

    Попробуем разобраться с основными тонкостями.

    Desktop Target


    Начнём с того, что у нас должен быть некоторый проект, созданный для Compact Framework. Создадим новый таргет через Build->Configuration Manager:
    Configuration Manager

    После этого добавим символ условной компиляции в свойствах проекта:
    Conditional Statement Symbol

    Начало положено. Посмотрим теперь, что делать, чтобы обеспечить подключение правильных сборок в нужном таргете. По умолчанию в нашем csproj файле нет никаких разделений по таргетам:

     <ItemGroup>
      <Reference Include="mscorlib" />
      <Reference Include="System" />
      <Reference Include="System.Data" />
      <Reference Include="System.Drawing" />
      <Reference Include="System.Windows.Forms" />
      <Reference Include="System.Xml" />
     </ItemGroup>


    * This source code was highlighted with Source Code Highlighter.


    Чтобы иметь полный контроль, необходимо организовать примерно следующий финт:
     <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
      <Reference Include="mscorlib">
       <Private>False</Private>
      </Reference>
      <Reference Include="Microsoft.WindowsMobile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\..\..\..\Program Files\Windows Mobile 6 SDK\Managed Libraries\Microsoft.WindowsMobile.dll</HintPath>
      </Reference>
      <Reference Include="Microsoft.WindowsMobile.Status, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\..\..\..\Program Files\Windows Mobile 6 SDK\Managed Libraries\Microsoft.WindowsMobile.Status.dll</HintPath>
      </Reference>
      <Reference Include="Microsoft.WindowsCE.Forms">
       <Private>True</Private>
      </Reference>
      <Reference Include="System" />
      <Reference Include="System.Data">
       <Private>False</Private>
      </Reference>
      <Reference Include="System.Windows.Forms" />
      <Reference Include="System.Drawing" />
      <Reference Include="System.Data.SQLite, Version=1.0.60.0, Culture=neutral, PublicKeyToken=1fdb50b1b62b4c84, processorArchitecture=MSIL">
       <Private>True</Private>
       <HintPath>..\..\..\..\..\Program Files\SQLite.NET\bin\CompactFramework\SQLite.Interop.060.DLL</HintPath>
      </Reference>
     </ItemGroup>

     <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Desktop|AnyCPU' ">
      <Reference Include="mscorlib">
       <Private>False</Private>
       <HintPath>C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll</HintPath>
      </Reference>
      <Reference Include="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86">
       <HintPath>C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll</HintPath>
      </Reference>
      <Reference Include="System.Windows.Forms">
       <HintPath>C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll</HintPath>
      </Reference>
      <Reference Include="System.Drawing">
       <HintPath>C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll</HintPath>
      </Reference>
      <Reference Include="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86" />
      <Reference Include="System.Data.SQLite, Version=1.0.60.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
       <Private>True</Private>
       <HintPath>..\..\..\..\..\Program Files\SQLite.NET\bin\System.Data.SQLite.DLL</HintPath>
      </Reference>
     </ItemGroup>


    * This source code was highlighted with Source Code Highlighter.


    Как видно, мы разделяем блоки ItemGroup по таргетам и указываем разные пути до сборок. Признаюсь, что именно здесь я возился дольше всего в моём проекте. Правильные версии выдирались прямо из сообщений об ошибках компиляции ;) Т.е. компилятор ругался, что не находятся такие-то нужные сборки и я подставлял правильные значения. И именно здесь я несколько раз порывался бросить всё, т.к. гуглятся подобные ошибки очень плохо.

    В результате хитрых манипуляций с csproj файлом получается такая ерунда в Solution Explorer:
    Solution Explorer Warnings

    Это нормально, т.к. Visual Studio не совсем пригодна к таким извращениям (хотя и позволяет их в итоге).

    #if #endif


    Когда сборки подключаются правильные, мы имеем возможность для каждой платформы использовать тот максимум, который каждая из платформ поддерживает. Напомню, однако, что автоматическая кодогенерация дизайнера форм нам тут всё портит. Поэтому необходимо, по возможности, сначала всё надизайнить, а потом уже править только руками — ведь при перегенерации *.Designer.cs, студия не сохраняет наши правки и добавления #if endif участков.

    Немного хитрый момент. Мой проект поддерживает как QVGA, так и VGA разрешение, однако, в Designer.cs у форм свойство ClientSize всегда соответствует QVGA разрешению. На десктопе же иметь окно размером 240х268 как-то неправильно, особенно, когда есть VGA-скин. Поэтому в конструкторе после InitializeComponent() я помещаю участок условной компиляции:

        Size vertSize = new Size(480, 560);
        Size horisSize = new Size(640, 480);


    [...]
          InitializeComponent();
    #if Desktop
          this.ClientSize = vertSize;
          this.FormBorderStyle = FormBorderStyle.Fixed3D;
          this.MaximizeBox = false;
          this.MouseWheel += new MouseEventHandler(MainController_MouseWheel);
    #endif


    * This source code was highlighted with Source Code Highlighter.


    Как видно, у меня объявлено две переменные типа Size. Зачем это нужно? Просто-напросто, у меня по F9 происходит переключение ClientSize, для эмуляции смены ориентации экрана. Полезно, когда необходимо протестировать OnResize. Да и в конце концов, есть же нетбуки с 800х480, для них ландшафтная ориентация — единственно возможная, чтобы всё поместилось на экране :)

    Также видно, что MouseWheel тоже обрабатывается только на десктопе.

    System.Diagnostics.Conditional


    Есть удобный способ указывать, для какого таргета собирать некоторый метод:
    [Conditional(“Desktop”)]
    public void SomeDesktopMethod()
    {
    }

    * This source code was highlighted with Source Code Highlighter.


    В данном способе хорошо то, что вызовы данного метода могут существовать в коде, но при наличии данного атрибута, вызовы будут просто проигнорированы на других таргетах! Альтернатива с использованием #if #endif предполагает, что везде, где нужен вызов, необходимо проставить проверку, чтобы компилировать или не компилировать вызов на нужной платформе.

    Отладка на десктопе


    Существует два способа отладки. Первый (неудобный) заключается в том, мы идём в bin\Desktop\, запускаем exe-файл и потом в студии говорим Debug — Attatch to process. Этот способ сначала кажется единственно возможным. Но! Есть мега-хак, неофициальный и неподдерживаемый способ (который, тем не менее, работает как в VS2005, так и в VS2008). Способ следующий:
    1. В студии открыть Tools/Options, далее в дереве выбрать Device Tools/Devices.
    2. В выпадающем меню выбрать платформу, для которой необходимо организовать отладку на десктопе. Это будет необходимо повторить для всех платформ, где необходимо.
    3. Далее нужно выбрать любой из эмуляторов и нажать [Save As…]. Удобно назвать копию “My Computer”.
    4. Теперь надо закрыть студию и открыть %USERPROFILE%\Local Settings\Application Data\Microsoft\CoreCon\1.0\conman_ds_platform.xsl файл в текстовом редакторе.
    5. В файле необходимо найти <DEVICE …> элемент, соответствующий свежесозданному «дивайсу»
    6. Далее добавляем следующую строку — <PROPERTY ID=«IsDesktopDevice» Name=«IsDesktopDevice»>true</PROPERTY>
      сразу после тега <PROPERTYCONTAINER>.
    7. Сохраняем conman_ds_platform.xsl и перезапускаем студию


    Ну вот, теперь нам доступен отладчик и все вкусности десктопной отладки.

    Заключение


    Чтож, в статье перечислены основные подводные камни, с которыми я столкнулся в процессе сборки своего проекта под Windows. Далее всё было делом техники — ловил исключения и разбирался в их причине. Среди них были не найденные пути, которые, очевидно, на десктопе отличаются и т.д., но это всё уже было ничто, по сравнению с начальными проблемами.

    PS. Почти всё, что описано выше, я выстрадал в результате долгих поисков, и вот, в самом конце, когда я искал способ отладки на десктопе, я натолкнулся на шикарную статью Дениела Моса о кросс-платформенной компиляции для Compact Framework :) Моя статья ни в коем случае не является переводом, однако, не могу не дать на неё ссылку.
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 23

      0
      Кхем, даже не знаю зачем это, java2me не портируют же на пк.
        +2
        Могу сказать, зачем это лично мне :)

        Разрабатывая свой продукт для Windows Mobile, сталкиваешься с тем, что появляются люди, которые бы хотели пользоваться твоим продуктом на десктопе. Серьёзно. Полностью написать другое приложение, положим, просто нет ни времени, не желания. В моём случае, я потратил около 5 часов на то, чтобы всё собралось и заработало. Учитывая, что на разработку в целом было потрачено более 800 часов, можно сказать, что десктоп версия появилась даром.

        Безусловно, можно сказать, что от десктоп приложений люди могут ждать большего или совсем другого, но, поверьте, это действительно удобно. Более того, даже тестировать десктоп версию проще, чем мобильную, т.к. не нужно поднимать эмулятор. Естественно, что особенности (или косяки) фреймворков не удастся отловить, но вот ошибки в логике приложения — легко! Доказано неоднократными багрепортами от пользователей десктоп версии.
          0
          Можете привести пример приложения?
            0
            Отписал в личку. Я просто через некоторое время напишу о приложении отдельную статью, не хочу раньше времени светить.
              0
              jeje
              Сколько возьмешь за инсайдерскую инфу? :) Шутка
                0
                1$ за один символ с личного сообщения, символы отдаются в random порядке :D
                  0
                  остается надеятся, что автор топика был не слишком многословен :)
        • НЛО прилетело и опубликовало эту надпись здесь
            +1
            Интересно, но вы немного пошли по своему пути.

            Когда мы делали проект под windows mobile 6, никаких извращений с csproj файлами не делали. Проект собирался и выполнялся как есть.

            По поводу отладки вы написали довольно интересный вариант, но простое решение лежит на поверхности:
            либо ставим эмулятор нужной платформы (windows mobile 5 входит в комплект со студией, wm6 надо качать) либо используем wm девайс. Далее идём tools->connect to device, выбираем и нажимаем F5 (start debugging). Студия сама деплоит проект на выбранную платформу и подключается к процессу. Всё работает на ура.

            При портировании на десктоп довольно редко используются те же формы, поскольку на большом брате разрешение побольше и намного больше возможностей визуального оформления. На выход приходит паттерн MVC: контроллеры, бизнес логика и уровень БД у вас общие, только делаете разные формы для разных платфор и никакого геморроя с #if #endif.

            Да, и SQL Server Compact работает на десктопе, т.е. не придётся переписывать DA при портировании на десктопный вариант.
              +1
              Отладка в эмуляторе — это одно, отладка десктопной сборки — другое. Эмулятор меня не устраивает тем, что даже на 2.5ГГц Core2Duo приложение визуально работает несколько хуже, чем на живом дивайсе :)

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

              Про формы — в моём конкретном случае так сложилось, что у меня полностью свой GUI фреймворк, поэтому на десктопе просто используется VGA-скин, а весь внешний вид фактически такой же, как на дивайсе — поэтому мне вообще не нужно делать разные формы для разных платформ, только немного #if #endif.
                0
                А в чём проблема отладки версии для десктопа? Опять же, из моего опыта, никаких танцев с бубнами/конфигурациями не было. Запускаете и дебажите. Или, может, я что-то не так делал?
                  0
                  Хм. Проект, изначально созданный для смарт-дивайса, должен быть куда-то задеплоен, чтобы автоматически началась отладка по F5. Деплоиться ему можно только в выбранный из списка дивайс. Без описанного в статье хака, в списке либо реальный дивайс (подключённый через ActiveSync), либо эмулятор.

                  Я так понимаю, что если проект отдельно сделать для десктопа и использовать общий с мобильным устройством BLL, то тогда да, оттуда в эмулятор вообще никак не попасть. Но у меня-то соль в том, что я только таргет при сборке меняю, а файл проекта один, общий.

                  Возможно, у Вас на других этапах были танцы с бубном, но Вы подзабыли? Пока не вижу другого объяснения.

                  Короче говоря, то, о чём Вы говорите, больше похоже на общий BLL и абсолютно разный GUI.
                    0
                    Изначально мы сделали wm приложение с 3-х слойной архитектурой. Когда потребовалось портировать под десктоп — создали новый UI, остальные компоненты не трогали.
                    Также было 2 проекта для запуска wm и десктоп приложений. Отсюда никаких бубнов при дебаге не было. Нужно поработать на wm — запускаем wm проект, и наоборот.

                    Вы просто по-другому это реализовали, отсюда и небольшие манипуляции с конфигами.
                      0
                      Да, именно. В моём опыте ценно, что UI полностью общий, т.е. трудозатраты на поддержку двух платформ минимальны.
              0
              и, в-седьмых, коммерческие пользовательские приложения практически никогда (не припоминаю ни одного, честно говоря) не пишутся на .NET CF ;)
                0
                Если уточнить что под «коммерческими пользовательскими приложениями» вы имеете ввиду шареваре и прочие прикладные софтины рассчитанные на широкий круг юзеров, то с вашими словами можно будет согласиться ;) Но рынок ими не ограничивается ведь.
                  0
                  да, я примерно их и имею в виду — в Top 100 самых продаваемых программ под WM едва ли наберётся пяток написанных на .NET
                    0
                    Ну так ведь и для десктопа на C# далеко не сразу начали активно программировать. Но сейчас-то это совсем не так.

                    Лично в моей ситуации попасть в top100 просто физически невозможно — сегмент узковат :)

                    Кстати, если, например, сравнить различный софт от Spb Software House ( судя по профилю, Вы оттуда :) ). Сегмент юзеров Mobile Shell однозначно шире, чем Wireless Monitor или Finance, верно? И причина ведь очевидна!

                    И если для Wireless Monitor, вероятнее всего, платформа критична, то для Finance — очень сомневаюсь. Конечным юзерам Finance с большой вероятностью всё равно, дотнет там или нет. Т.к. приложение работает и выполняет свои функции.
                      0
                      вы забываете про один из важнейших факторов ПО для end-user'-ов — скорость запуска и работы, которая является слабой стороной .NET'а. Применительно к тому же финансу можно вспомнить Quick Entry (маленькое приложение, с помощью которого можно добавить транзакцию в пару кликов) — если оно будет запускаться за 10 секунд, то немногим пользователям это понравится.
                      да и с совместимостью у .NET CF не всё идеально…
                        0
                        Я не забываю, я смирился. Но только со скоростью запуска. С ней почти ничего нельзя поделать. Отчасти спасает демонстрация прогресса загрузки. А ещё спасает модель выгрузки приложений из памяти в Windows Mobile — после запуска и сворачивания, повторное разворачивание происходит моментально.

                        А вот со скоростью работы я решил вопрос полностью — всё летает от и до. (Ну и что, что из родных контролов только TextBox и InputPanel)

                        Quick Entry вспомнить, к сожалению, не мог, т.к. с Finance знаком только по описанию, но идею улавливаю, безусловно — я ведь сам такоей же end-user, как и все.
                      0
                      Кстати, а вот ещё момент интересный. На том же Андроиде мы вынужденны программировать на чём? Верно, на Java. А чем Java (в общемировом масштабе) отличается от .Net? В случае с Андроидом отличие большое — это единственный способ писать программы для него (если не считать веб-приложений, которые почти что не считаются).

                      А в реальности-то Java — ни разу не нативный язык для устройств и этим не сильно отличается от .Net…
                        0
                        Про Java vs .NET в мировом машстабе я сейчас не настроен холиварить, извините ;)

                        С андроидом, кстати, всё гораздо сложнее и интереснее одновременно — там практически все приложения (втч, скажем, звонилка) написаны на Java — и ничего, прекрасно работают, так что вопрос нативности весьма спорен…

                        >А в реальности-то Java — ни разу не нативный язык для устройств
                        ru.wikipedia.org/wiki/Jazelle ;)
                          0
                          Да я и не ради холивара этот момент вспомнил :)

                          Про сложнее и интереснее — не спорю. Гугл не по делу не выпендривается. Мне кажется, что тут вопрос в поддержке Java на системном уровне в Android… Отчасти можно сравнить с поддержкой .NET в десктоп Windows.

                          Кста, из той же википедийной статьи — там крутая концовка:
                          >> Набор команд RCT не привязан жёстко к языку Java и может использоваться для компиляции байт-кодов прочих интерпретируемых языков, таких как Perl, Python, а также языков, поддерживаемых технологией .NET фирмы Microsoft.

                          Т.е. нас может то же счастье ждать и в CF.NET? Кайф :) Другой вопрос, что может не значит будет… Мы и Windows Mobile 7 вряд ли дождёмся в обозримом будущем :(

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое