уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

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

Действительно, на текущий момент, да и в обозримом будущем, самом популярной ОС в мире есть и будет Windows. Но не всё так просто.  Мне допустим, ещё месяц с небольшим назад вполне комфортно и уютно было работать в Delphi 2010. Сегодня я в срочном порядке изучаю тонкости работы в Lazarus потому, что на второй работе на часть машин установили Linux. А прикладных программ, которыми мы повседневно пользуемся (кстати, разработанных нами же под Windows) естественно в Linux не наблюдается. Вот и первая причина — банальная необходимость, несмотря на то, что в Мире «рулит» Windows.

Вторая причина, по которой может потребоваться разработка мультиплатформенного приложения — расширение пользовательской аудитории вашей программы. Например, тот же популярный браузер FireFox вряд ли бы имел такую популярность сегодня, если б разрабатывался только под Linux-системы.

Ну, и третью причину можно назвать одним словом — реклама. Большей степенью это относится конечно к приложениям целевой аудиторией которой являются пользователи ОС Linux. Кстати, разработку программ под Windows+Linux в свое время высказал один из участников Linux-движения в РФ. Дословно идея была выражена следующим образом:

Пишите коммерческие программы. И для linux, и для windows.
Если вы пишете что-то прикладное для linux, то подумайте о том, не могли бы вы написать проприетарную версию своей программы для windows. У нас сегодня для этого есть множество инструментов. С их помощью вы можете создавать windows-программы, (почти) ничего не меняя в своем коде. Создание коммерческих версий программ поможет познакомить больше пользователей с реальным миром linux, не покидая привычной им системы. Кроме того — пусть пользователи windows оплачивают наше развитие. И пусть они знают — перейдя на linux, они получат уже знакомые им программы — бесплатно. Но таких программ должно быть как можно больше! (оригинал статьи)

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

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

1. Сколько операционных систем необходимо учитывать при разработке?

Однозначного ответа на этот вопрос, в принципе, не существует. Можно было бы сказать «Учитывайте все ОС», но зачем? По-моему количество ОС, учитываемых при разработке программы определяется только целевой аудиторией. Зачем, допустим мне учитывать в разработке Mac OS? Переход на Linux был для нас необходимостью и врядли когда-то моя организация сподобится купить всем по Mac’у. Следовательно об особенностях этой операционной системы я даже не задумываюсь.  Другое дело, если ведется разработка, например, нового Интернет-браузера и для большей популярности необходимо учесть все популярные операционные системы.

2. Работа с файлами и директориями.

При работе с файлами и папками важно учитывать не связанные с конкретной платформой разделители пути и окончания строк. Например, в Lazarus объявлены следующие константы для работы с файлами и директориями в Linux

DriveSeparator = '/';
ExtensionSeparator = '.';
PathSeparator = ':';
AllowDirectorySeparators : set of char = ['','/'];
AllowDriveSeparators : set of char = [];

Отдельное внимание следует уделить окончаниям строк:

  • Line feed (LF, #10 ) Linux, BSDs, Unix, …
  • Carriage return + Line feed (CRLF #13#10) MS Windows
  • Carriage return (CR #13) Mac OS Classic

Для работы с окончаниями строк в Lazarus можно использовать константу, которая определяется в зависимости от ОС:

DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsLF; //для Linux, Unix...
TTextLineBreakStyle = (tlbsLF,tlbsCRLF,tlbsCR);

Кроме этого, для кодирования имён файлов и строк вы можете использовать следующие функции и процедуры Lazarus из модуля FileUtil:

function NeedRTLAnsi: boolean ; // true, если система кодирования не является UTF-8
procedure SetNeedRTLAnsi (NewValue: boolean ) ;
function UTF8ToSys ( const s: string):string ; //как UTF8ToAnsi
function SysToUTF8 ( const s: string ) : string ; //как AnsiToUTF8
function UTF8ToConsole ( const s: string ) : string ; 
//преобразует UTF8-строку в кодировку консоли (использует Write, WriteLn)
//Операции с файлами
function FileExistsUTF8 ( const Filename: string ) : boolean ;
function FileAgeUTF8 ( const FileName: string ) : Longint ;
function DirectoryExistsUTF8 ( const Directory: string ) : boolean;
function ExpandFileNameUTF8 ( const FileName: string ) : string;
function ExpandUNCFileNameUTF8 ( const FileName: string ) : string;
function ExtractShortPathNameUTF8 ( Const FileName : String ) : string ;
function FindFirstUTF8 ( const Path: string ; Attr: Longint ; out Rslt: TSearchRec ) : Longint;
function FindNextUTF8 ( var Rslt: TSearchRec ) : Longint;
procedure FindCloseUTF8 ( var F: TSearchrec );
function FileSetDateUTF8 ( const FileName: String ; Age: Longint ) : Longint ;
function FileGetAttrUTF8 ( const FileName: String ) : Longint ;
function FileSetAttrUTF8 ( const Filename: String ; Attr: longint ) : Longint ;
function DeleteFileUTF8 ( const FileName: String ) : Boolean ;
function RenameFileUTF8 ( const OldName, NewName: String ) : Boolean ;
function FileSearchUTF8 ( const Name, DirList : String ) : String ;
function FileIsReadOnlyUTF8 ( const FileName: String ) : Boolean ;
function GetCurrentDirUTF8: String ;
function SetCurrentDirUTF8 ( const NewDir: String ) : Boolean;
function CreateDirUTF8 ( const NewDir: String ) : Boolean ;
function RemoveDirUTF8 ( const Dir: String ) : Boolean ;
function ForceDirectoriesUTF8 ( const Dir: string ) : Boolean ;
// Окружение
function ParamStrUTF8 (Param: Integer ) : string;
function GetEnvironmentStringUTF8 ( Index: Integer ) : string;
function GetEnvironmentVariableUTF8 ( const EnvVar: string ) : String;
function GetAppConfigDirUTF8 ( Global: Boolean ) : string ;
//Другие функции
function SysErrorMessageUTF8 ( ErrorCode: Integer ) : String;

Зачастую текстовые файлы хранятся в кодировке той операционной системы в которой они были созданы. Например, для Windows — это одна из кодовых страниц, а для Linux, Unix и MacOSUTF8. На сегодняшний день нет 100% возможности сходу программно определить кодировку текста в файле. Но в Lazarus есть замечательный модуль LConvEncoding в котором присутствуют функции цель которых «угадать кодировку текста»:

function GuessEncoding (сопзЬ S: String): String;
function GetDefaultTextEncoding: string ;

А также функции для перекодирования текста:

function ConvertEncoding (const FromEncoding, ToEncoding: String): String;
function UTF8BOMToUTF8 ( const s: string ) : string ; // UTF8 с BOM
function ISO_8859_1ToUTF8 ( const s: string ) : string ; //Центральная Европа
function CP1250ToUTF8 ( const s: string ) : string ;// Центральная Европа
function CP1251ToUTF8 ( const s: string ) : string ;// кириллица
function CP1252ToUTF8 ( const s: string ) : string ; //латиница 1
... ...
function UTF8ToUTF8BOM ( const s: string ) : string ; // UTF8 с BOM
function UTF8ToISO_8859_1 ( const s: string ) : string ; //Центральная Европа
function UTF8ToCP1250 ( const s: string ) : string ; //Центральная Европа
function UTF8ToCP1251 ( const s: string ) : string ; //кириллица
function UTF8ToCP1252 ( const s: string ) : string ; //латиница 1

3. Файлы конфигурации

Не знаю как Вы, а я при работе с файлами конфигурации для программ часто грешил кодом, наподобие:

var Path: string;
begin
[...]
Path:=ExtractFilePath(Application.ExeName)+...
[...]
end;

Программирование для Linux и Windows потихоньку приучает к порядку. Дело в том, что более правильно хранить файлы конфигурации там, где рекомендуется, а не там где хочется. Не могу утверждать со 100% уверенностью (если не прав — поправьте), но по-моему Windows Vista просто так не позволит программе создать новый файл там, где хочется, но без проблем допускает создание файла в директории пользователя. То же самое может случиться и при работе в Linux, т.к. для работы, например с каталогом /usr/lib необходимо иметь привилегии главного администратора.
Чтобы избежать досадных ошибок при работе с конфигурационными файлами можно воспользоваться функциями Lazarus из модуля SysUtils GetAppConfigDir и GetAppConfigFile.
Функция GetAppConfigDir имеет всего один параметр Global:boolean. Если он равен True, то функция возвращает каталог для хранения файлов конфигурации для всех пользователей, иначе — каталог для текущего пользователя.
GetAppConfigFile(Global, SubDir: boolean) возвращает имя файла, в котором можно хранить параметры конфигурации. Параметр Global определяет, будет ли это глобальный файл конфигурации (значение True) или файл конкретного пользователя (значение False). Если SubDir равен True, то будет перед именем файла будет вставляться название каталога. По умолчанию расширение файла конфигурации .cfg.
Особое внимание следует обратить на то, что использование утилиты UPX для сжатия исполняемого файла препятствует правильной работе приведенных выше функций.
Также следует отметить, что приведенный Выше отрывок программного кода может работать некорректно, если программа запущена под Linux. В зависимости от версии Linux программа может вернуть значения каталог, в котором находится символическая ссылка на бинарный файл.

4. Файлы приложения.

С файлами конфигурации разобрались. Как быть с дополнительными файлами (изображениями, mp3, XML и т.д.), которые необходимы для работы приложения. Функций, подобных GetAppConfigDir для решения этой проблемы нет. Можно, конечно всё, что необходимо грузить в каталог с программой, но, например в Linux такие фалы хранить в специальных директориях, которые могут выглядеть как:
/USR/Share/APP_NAME или /Opt/APP_NAME
и не требовать, чтобы пользователь имел привилегии суперпользователя.

5. Избегайте использования специфичных функций.

Все мы (я имею в виду тех, кто много лет разрабатывал приложения только для Windows) настолько привыкли к использованию некоторых функций из WinAPI или WinInet, что уже практически на автомате вставляем необходимые модули в uses. При разработке мультиплатформенных приложений к вопросу использования тех или иных модулей следует относиться весьма и весьма осторожно. Если есть возможность избежать использования какой-либо функции, которая специфична для операционной системы — то её лучше не использовать. Например функцию ShellExecute из ShellAPI можно заменить использованием объекта TProcess, который не привередлив к ОС. Если же использование како-либо функции крайне необходимо при запуске в определенной ОС, то тут, как я уже упоминал, следует использовать директивы компилятора

{$IFDEF ...} [...] {$ENDIF}

Так же следует в принципе забыть о том, чтобы использовать в приложениях Windows COM Automation.
Ну и последнее, что стоит упомянуть касается проекта в целом. Вполне возможно, что возникнет ситуация, когда Вам просто не обойтись без использования того же Microsoft Word при работе приложения под Windows или использовать функциональность Internet Explorer. В этом случае наиболее удобно и целесообразно выделять все функции и процедуры для таких специфических задач в отдельные модули и именовать эти модули соответствующим образом.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
3 2 голоса
Рейтинг статьи
Подписаться
Уведомить о
3 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
sbseo
sbseo
08/02/2010 08:03

С вашего позволения я немного прокомментирую. На сегодняшний день нет 100% возможности сходу программно определить кодировку текста в файле. Формат UTF-8 файла позволяет вставить BOM сигнатуру в начало файла, чтобы 100% идентифицировать кодировку. Тем более, если это ваши файлы. Программирование для Linux и Windows потихоньку приучает к порядку. Дело в том, что более правильно хранить файлы конфигурации там, где рекомендуется, а не там где хочется. Ну если уж пошла речь о порядке, то MicroSoft официально призывает НЕ использовать файлы конфигурации. Если бы не обратная совместимость, то они давно бы уже убрали апи работы с INI файлами. Все настройки и конфигурации… Подробнее »

ansi
ansi
26/02/2014 21:35

Lazarus. Требуется сохранить текст из Memo в файл с кодировкой cp1251. Колдовал с UTF8Decode, UTF8ToAnsi, UTF8ToSys, UTF8ToConsole, UTF8Encode, AnsiToUTF8, SysToUTF8…..).
Сохраняет файл в UTF8 и хоть ты что делай. Пример: Memo1.Lines.SaveToFile(UTF8ToAnsi(‘c:\test.txt’));
Подскажите как сохранить текст, а то Windows XP не воспринимает юникод в командной строке (chcp 65001 не предлагать, не работает, chcp 65001 && [command] тоже пробовал).