Содержание


Работаем с Mono

Часть 5. Перенос приложения на основе WinForms на платформу Mono

Серия контента:

Этот контент является частью # из серии # статей: Работаем с Mono

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Работаем с Mono

Следите за выходом новых статей этой серии.

Введение в WinForms

Библиотека Windows Forms – это один из компонентов Microsoft .NET Framework, отвечающий за реализацию графического интерфейса пользователя на платформе .NET. Хотя в настоящий момент уже представлена библиотека Windows Presentation Foundation, которая должна её заменить, Windows Forms все еще широко используется при разработке приложений для Microsoft Windows. Windows Forms является управляемой надстройкой над стандартным графическим интерфейсом Windows GDI и реализует все его возможности.

Форма – это одно из ключевых понятий в Windows Forms. Под формой подразумевается пространство, которое используется для взаимодействия с пользователем (вывод информации и/или сбор данных). Для создания приложения на основе Windows Forms потребуется:

  • создать форму;
  • добавить в форму элементы управления;
  • реализовать обработку действий пользователя.

Элемент управления (control) – это GUI-компонент, использующийся для отображения информации или ввода данных пользователем. В состав библиотеки Windows Forms входит множество различных элементов управления. К ним относятся:

  • области для ввода текста (TextBox);
  • кнопки (Button);
  • выпадающие списки (ComboBox);
  • переключатели (CheckBox, RadioButton);
  • класс UserControl для создания собственных элементов.

Также существуют элементы–контейнеры, позволяющие управлять размещением GUI-компонентов в форме.

Когда пользователь выполняет какое-либо действие с формой или элементом управления, то создается соответствующее событие (event). Приложение может «подписаться» на доставку оповещений об определенных событиях, и при возникновении события будет вызываться код для его обработки.

Реализация WinForms в Mono

В последних версиях Mono поддерживается практически полный набор классов пространства имен System.Windows.Forms и System.Drawing. При этом данная реализация не опирается на другие библиотеки типа Gtk#. Это связано с тем, что у каждой GUI-платформы (GNome, KDE, Windows) имеются отличия в реализации элементов управления, их отображении и функциональности. Например, переключатель (CheckBox) в Gtk# отличается от переключателя в Win32 GDI. Библиотека Windows Forms в Mono разрабатывалась с таким расчетом, чтобы максимально унифицировать поведение приложений при запуске под Linux, Windows или Mac OS X.

Использование инструмента MoMA для переноса приложений

В первой статье этой серии рассматривался инструмент Mono Migration Analyzer (MoMA), который позволяет проанализировать бинарную сборку на совместимость с различными версиями Mono. Так как в октябре 2010 года вышла новая версия Mono 2.8, обновленная версия MoMA доступна по этому адресу. Эта версия выполняет анализ сборки на совместимость с Mono 2.8. Для проверки совместимости с более старыми версиями на странице архива MoMA можно скачать файлы определений для предыдущих версий и скопировать их в каталог defs установленной версии MoMA.

Проблемы, возникающие при переносе приложений на Mono

Библиотека Windows Forms – не самый удачный выбор для разработки приложений, которые должны запускаться на различных ОС. Даже при разработке Mono-приложения в ОС Windows, чтобы обеспечить запуск приложения в Linux или Mac OS X, лучше воспользоваться Gtk#. Однако, если требуется перенести уже готовую программу, то переписывать ее с нуля будет не самым правильным решением. В таком случае лучше внести в программу некоторые изменения, чтобы обеспечить возможность запуска под Mono.

Текущие версии Mono (на данный момент актуальными считаются версии 2.6 и 2.8) предлагают практически полную поддержку Windows Forms, но при переносе программы на более старые версии Mono могут возникнуть проблемы. Чтобы обеспечить приложению на Windows Forms возможность запуска под управлением Mono, уже при его разработке нужно учесть несколько факторов.

Иногда возникает проблема, связанная с отсутствием реализации того или иного метода. В случае если не реализован обязательный метод, потребуется изменить код и удалить все обращения к этому методу. Но чаще всего нереализованными оказываются необязательные методы. Например, в Windows Forms существует стандартное диалоговое окно настроек печати PrintDialog, у которого есть свойство UseExDialog. Это свойство определяет, должно ли диалоговое окно отображаться в стиле Windows XP, и с точки зрения функциональности не несет никакой нагрузки, поэтому в текущей версии Mono его изменение ни на что не влияет. В предыдущих версиях Mono реализация для этого свойства отсутствовала, что могло привести к ошибке и закрытию приложения. Обойти это можно тремя путями:

  1. закомментировать вызов нереализованного метода;
  2. условной компиляцией в зависимости от системы;
  3. проверкой в процессе выполнения программы.

У первого способа есть один недостаток. Бывает, что метод или свойство не несут функциональной нагрузки, но в Windows они могут повлиять на внешний вид пользовательского интерфейса. Поэтому можно рассмотреть второй способ, когда выбор используемой функциональности зависит от целевой платформы. Для этого достаточно определить какой-либо макрос (например, USE_MONO, как показано в листинге 1), и в зависимости от его значения собирать различные версии приложения.

Листинг 1. Использование макроса для условной компиляции
#if !USE_MONO
// вызываем неподдерживаемый в Mono метод
#endif

В данном случае может возникнуть другая проблема, так как будет утрачено мультиплатформенное преимущество Mono. При использовании условной компиляции потребуется создать несколько сборок приложения для разных платформ, и приложение, скомпилированное без определения макроса USE_MONO, не сможет работать в среде Mono.

Самым лучшим является третий способ, при котором имеется одна сборка для разных платформ, но в процессе выполнения выполняется проверка, на какой платформе был произведен запуск, как показано в листинге 2:

Листинг 2. Проверка типа платформы во время работы приложения
//метод, проверяющий, что приложение запущено на платформе Mono
public static bool IsOnMono()
{
    return Type.GetType(“Mono.Runtime”) != null;
}

//в другом месте кода
if (!IsOnMono())
{
    // вызов метода, неподдерживаемого в Mono
}

Более серьезной проблемой при переносе приложений в Mono может стать использование в приложении вызовов функций из неуправляемого кода (так называемые P/Invokes). Дело в том, что в библиотеке Windows Forms (входящей в состав .NET Framework) заложена только базовая функциональность и некоторых функций для работы с пользовательским интерфейсом, существующих в Win32 GDI, в управляемом коде нет. Для этого разработчики используют механизм вызова функций из неуправляемого кода, импортировав их непосредственно из динамической библиотеки (DLL), где они реализованы.

В качестве примера можно рассмотреть open-source элемент управления Ribbon, реализующий функциональность ленты и впервые появившийся в Microsoft Office 2007. Анализ сборки в MoMA показывает, что в коде элемента управления производится 37 вызовов из неуправляемого кода, как показано на рисунке 1.

Рисунок 1. Краткие результаты анализа сборки при помощи MoMA
Рисунок 1. Краткие результаты анализа сборки при помощи MoMA
Рисунок 1. Краткие результаты анализа сборки при помощи MoMA

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

Рисунок 2. Подробные результаты анализа сборки при помощи MoMA
Рисунок 2. Подробные результаты анализа сборки при помощи MoMA
Рисунок 2. Подробные результаты анализа сборки при помощи MoMA

Обойти эту проблему можно несколькими способами.

Во-первых, если вызов не является критически необходимым, с ним можно поступить, как было описано выше: закомментировать или вызывать только при условии, что программа запущена в среде, где не существует неуправляемой библиотеки, откуда производится вызов. При этом нельзя выполнить только проверку на наличие Mono, так как в среде Microsoft Windows могут сосуществовать Mono и необходимые библиотеки.

Во-вторых, если вызов необходим, можно рассмотреть возможность его замены на функциональность, доступную из управляемого кода. В этом поможет как знание классов .NET Framework, так и опыт, уже накопленный в Mono-сообществе. На сайте PINVOKE.NET собрана подборка функций, не имеющих прямой реализации в .NET Framework, разбитых по библиотекам. Найдя нужную функцию можно посмотреть ее описание, пример вызова из управляемого кода, а в некоторых случаях и вариант замены на реализацию на основе управляемого кода.

Еще существует возможность подобрать требуемую функциональность среди библиотек, доступных в среде, отличной от Windows, благодаря тому, что Mono, как и .NET Framework, допускает вызовы из неуправляемого кода. Для поиска функций в различных библиотеках используется специальный конфигурационный файл, который должен иметь имя как у исполняемого файла (полностью) и дополнительное расширение .config. Например, если файл приложения называется app.exe, то его конфигурационный файл должен называться app.exe.config. Для подмены библиотеки с нужной функцией в конфигурационном файле прописывается заменяемая библиотека (атрибут dll) и заменяющая (атрибут target), как показано в листинге 3.

Листинг 3. Конфигурационный файл для замены библиотек
<configuration>
  <dllmap dll=”gdi32.dll” target=”libgdi32.so” />
</configuration>

При этом поиск неуправляемых методов, импортируемых из библиотеки gdi32.dll, будет производиться в библиотеке libgdi32.so. Более подробно этот прием будет рассматриваться в одной из следующих статей цикла.

Если функция не имеет аналогов в системе, где запускается приложение, то существует еще одна возможность не реализовывать ее с нуля. Для этого используется набор библиотек WINE, предназначенный для запуска Windows-приложений в среде Linux и других свободных операционных систем. Если требуется функция из набора стандартных библиотек Windows, то ее исходный код можно найти в WINE и скопировать в отдельную библиотеку, которая будет подключаться через конфигурационный файл.

Ограничения инструмента MoMA

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

Для нормальной работы приложения на разных ОС необходимо учитывать регистр при наименовании файлов, так как в Windows регистр не имеет значения при написании имен файлов, в отличие от Linux-систем. Также есть и различия в символе, разделяющем каталоги в пути к файлу. В Windows разделителем является обратная косая черта (\), а в Linux – прямая (/). Чтобы избежать создания разных сборок для разных систем необходимо использовать метод Path.DirectorySeparatorChar.

Есть путь к файлу – ./Res/Img/Img.jpg, в исходном коде его можно представить следующей строкой:

string strFilePath = Application.ExecutablePath + 
  string.Format(“{0}Res{0}Img{0}Img.jpg”, Path.DirectorySeparatorChar);

Визуальное редактирование форм

Хотя использование Windows Forms для мультиплатформенных приложений – это не самая лучшая идея, но при необходимости можно создавать и редактировать формы в визуальном редакторе даже в среде Linux.

В Windows при разработке для платформы Mono можно использовать open-source интегрированную среду разработки (IDE) Sharp Develop со встроенным инструментарием для визуального построения интерфейса Windows Forms. В MonoDevelop IDE подобный инструмент отсутствует, но можно использовать MWF-Designer. Хотя этот проект развивается не очень активно и обладает некоторыми недостатками, его можно использовать для проектирования форм. Для запуска MWF-Designer необходимо загрузить свежую версию с сайта и распаковать ее в отдельный каталог.

Для сборки MWF-Designer используется команда make, а получившийся исполняемый файл будет помещен в каталог build. Оттуда его можно будет запустить командой: mono mwf-designer.exe.

На рисунке 3 показано окно MWF-Designer после создания нового проекта. С левой стороны — палитра стандартных элементов управления, в центре — шаблон формы, справа — набор свойств и событий для элемента формы или самой формы.

Рисунок 3. Главное окно MWF-Designer c новым проектом
Рисунок 3. Главное окно MWF-Designer c новым проектом
Рисунок 3. Главное окно MWF-Designer c новым проектом

На рисунке 4 показана форма, в которую уже добавлено несколько элементов управления. После настройки их свойств и сохранения MWF-Designer сгенерирует файлы с исходным кодом, который можно будет добавить в проект.

Рисунок 4. Форма с элементами управления
Рисунок 4. Форма с элементами управления
Рисунок 4. Форма с элементами управления

Заключение

Как показывает практика, текущая версия Mono обеспечивает поддержку Windows Forms на уровне, достаточном для запуска большинства приложений. При переносе приложений, написанных с использованием .NET Framework, на Mono необходимо учесть несколько моментов, которые могут привести к проблемам. Однако, как рассказывается в этой статье, этих проблем можно легко избежать, если уделить им дополнительное внимание при разработке приложения и использовать специальные инструменты, такие как MoMA.


Ресурсы для скачивания


Похожие темы

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=742897
ArticleTitle=Работаем с Mono: Часть 5. Перенос приложения на основе WinForms на платформу Mono
publish-date=07262011