суббота, 14 июля 2007 г.

Managed C++. Проба пера

Сегодня попробовал поработать с управляемым C++.

Ну и в качестве упражнения хотелось попробовать что-то интересное, а не ограничиваться переписыванием примеров из того же MSDN.

Поэтому мой выбор пал на одну небольшую библиотеку - minifmod. Это часть библиотеки FMOD - кроссплатформенной библиотеки для работы с различными аудиоформатами.

Библиотека minifmod поставляется с открытым исходным кодом, а так же имеет в поставке статическую C-библиотеку для использования их в проектах Visual C++.

Но .NET программист не сможет использовать эту библитеку, поэтому я решил это исправить.

Действительно, достаточно типичная ситуация. Мы имеем огромную массу библиотек, написанных на C и C++, для того чтобы использовать вызовы к этим библиотекам можно использовать достаточно удобный интерфейс как Platform Invoke. Однако в моем случае это было невозможно, потому как это не *.dll, а static link library.

Было принято решение создать проект на Managed C++, который является оберткой вокруг minifmod, а так же тестовый проект Windows Forms на C# который бы использовал мою библиотеку-обертку.

Изучив исходный код minifmod я обнаружил, что сама библиотека заточена на работу с музыкальными треками как с файлами:
FMUSIC_MODULE * FMUSIC_LoadSong(signed char *name, SAMPLELOADCALLBACK sampleloadcallback);

По правде говоря меня это несколько расстроило, потому как это накладывало целый ряд ограничений на ее использование.

К счастью, я обнаружил еще механизм функций обратного вызова, которые вселили в меня надежду что не все так плохо как кажется на самом деле:
typedef void (*FMUSIC_CALLBACK)(FMUSIC_MODULE *mod, unsigned char param);

void FSOUND_File_SetCallbacks(
unsigned int (*OpenCallback)(char *name),
void (*CloseCallback)(unsigned int handle),
int (*ReadCallback)(void *buffer, int size, unsigned int handle),
void (*SeekCallback)(unsigned int handle, int pos, signed char mode),
int (*TellCallback)(unsigned int handle));

Итак, я решил реализовать класс, который бы как минимум умел бы воспроизводить XM-треки из файла, и как максимум - работать с объектом типа System.IO.Stream.

Мне очень понравился процесс создания этой оболочки. Мне даже удалось в некоторой степени "обмануть" разработчиков, и передавать не строку с путем файла а указатель на объект типа System.IO.Stream.

Вот листинг .NET класса для воспроизведения XM-файлов:
namespace NModPlayer
{
public ref class ModPlayer : public IDisposable
{
private:
Stream^ _stream;
FMUSIC_MODULE* _mod;
GCHandle _gcHandle;

public:

ModPlayer(Stream^ stream)
{
_stream = stream;
FSOUND_File_SetCallbacks(memopen, memclose, memread, memseek, memtell);
}

void BeginPlay()
{
GCHandle _gcHandle = GCHandle::Alloc(_stream);
void* pStream = (void*)GCHandle::ToIntPtr(_gcHandle);

_mod = FMUSIC_LoadSong((char*)pStream, NULL);
FMUSIC_PlaySong(_mod);
}

void StopPlay()
{
if (_mod)
{
FMUSIC_FreeSong(_mod);
}

if (_gcHandle.IsAllocated)
{
_gcHandle.Free();
}
}

~ModPlayer()
{
StopPlay();
}
};
}

Обратите внимание на конструкцию вида:
GCHandle _gcHandle = GCHandle::Alloc(_stream);
void* pStream = (void*)GCHandle::ToIntPtr(_gcHandle);

_mod = FMUSIC_LoadSong((char*)pStream, NULL);

Сдесь мне пришел в голову один хак. Поскольку ответственность по реализации callback-функций я взял на себя, то я могу совершенно спокойно передавать что угодно.

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

Реализация callback-функций выглядит следующим образом:
Stream^ handleToStream(unsigned int handle)
{
GCHandle gcHandle = GCHandle::FromIntPtr((IntPtr)(void*)handle);
Stream^ stream = (Stream^)gcHandle.Target;
return stream;
}

unsigned int memopen(char* instance)
{
return (unsigned int)((void*)instance);
}

void memclose(unsigned int)
{
// Do nothing because GC will free all allocated memory
}

int memread(void *buffer, int size, unsigned int handle)
{
Stream^ stream = handleToStream(handle);

if (stream->Position + size >= stream->Length)
{
size = (int)(stream->Length - stream->Position);
}

array^ managedBuffer = gcnew array(
(unsigned int)size);

stream->Read(managedBuffer, 0, size);

Marshal::Copy(managedBuffer, 0, (IntPtr)buffer, managedBuffer->Length);

return size;
}

void memseek(unsigned int handle, int pos, signed char mode)
{
Stream^ stream = handleToStream(handle);

if (mode == SEEK_SET)
{
stream->Position = pos;
}
else if (mode == SEEK_CUR)
{
stream->Position += pos;
}
else if (mode == SEEK_END)
{
stream->Position = stream->Length + pos;
}

if (stream->Position > stream->Length)
{
stream->Position = stream->Length;
}
}

int memtell(unsigned int handle)
{
Stream^ stream = handleToStream(handle);

return (int)stream->Position;
}

В фунции memopen вроде бы передают указатель на строку, но мы то знаем, что мы туда отдали ;)

В принципе реализация остальных функций не требует особых пояснений.

Так что теперь любой .NET программист сможет спокойно использовать эту библиотеку, потому как в его распоряжении окажется обычная .NET сборка с классом ModPlayer.

Вот например я этот класс использовал в Windows Form приложении:
namespace NMiniFMOD.Sample
{
public partial class FormMain : Form
{
ModPlayer _modPlayer;

public FormMain()
{
InitializeComponent();
}

private void FormMain_Shown(object sender, EventArgs e)
{
using (Stream stream = new MemoryStream(Resources.MusicTrack))
{
_modPlayer = new ModPlayer(stream);
_modPlayer.BeginPlay();
}
}

private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
_modPlayer.Dispose();
}
}
}

Если интересно, могу выложить полный исходный код.

среда, 4 июля 2007 г.

Браузерная головная боль

Сегодня обнаружил, что IE6 и, что более удручающее, IE7 содержат аналогичные проблемы с закачкой файлов по HTTP.

Дело в том, что при попытке выкачать файл очень большого размера, скажем более 4,5 Гб, Internet Explorer испытывает арифметические переполнение из-за того, что пришедший Content-Length превышает максимальный размер 4-х байтового беззнакового целого числа (unsigned int).

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

Аналогичные опыты были проведены с Opera 8.5 - результат аналогичен IE6. К счастью Opera 9.21 никаких проблем в связи с этим не испытывает.

Ну и самое главное это то, что Mozilla Firefox 1.5 и 2.0 работают так, как должен работать самый лучший браузер в мире :)

И при этом не следует забывать, что Mozilla Firefox 1.5 является достаточно старой версией браузера.

пятница, 29 июня 2007 г.

Дискуссия Java VS .NET

Не могу сказать что я обожаю holy wars, но сам лично стараюсь в них не ввязываться.
Сегодня у меня состоялась дискуссия по ICQ с одним моим знакомым, и я, с его позволения, решил опубликовать наш диалог.

Будем надеяться, что кому-то это покажется познавательным.

Дмитрий: Ты можешь в двух словах обрисовать отличие Явы от Шарпа?
status_alexus: Могу
status_alexus: Java - это консервативная выжимка из C++. Выкинули все редко используемое, которое только добавляло головную боль программистам. Язык реально кроссплатформенный это большой плюс. Язык открыли по лицензии GPL, поэтому это только пойдет ему на пользу, его буду портировать и дальше. Основная GUI библиотека - Swing - выглядит как не родная под целевой OC.
status_alexus: Есть еще либа SWT - приложения выглядят как родные на Windows, Mac OS, Linux
status_alexus: Есть Java Community Process - это группа по развтию технологий языка. Одновременно хорошо и плохо. Хорошо, потому что язык будет более стабильным, не будут прикручивать сомнительные фичи, которые "перегрузят" язык. Плохо, потому что добавляет инертность к введению новых языковых конструкций.
Дмитрий: эм, ну а указателей ведь там нет, и есть GC, как в Шарпе? а нотация венгерская, как в Си?
status_alexus: C# основной язык платформы .NET
Таким образом, зная Framework Class Library можно писать на любом языке. Сейчас реально можно писать на C#, Visual Basic .NET, Iron Python и это огромный плюс.
status_alexus: Говорят что .NET вроде как тоже кроссплатформенный, но реально пока нет.
Дмитрий: А это, Mono?
status_alexus: Есть разработка в виде Mono, но нужно прилагать усилия, чтобы приложение везде работало.
Дмитрий: а, даже так...
status_alexus: Уже в версии 2.0 C# начал "обрастать" сомнительными, на мой взгляд, возможностями.
status_alexus: Мне очень не нравится Nullable типы. Я считаю что это лишнее. Это моя точка зрения.
Дмитрий: Так это для баз данных, имхо
status_alexus: Указателей в C# нет. GC есть. Соответственно, код стабилен и управляем.
status_alexus: да для баз.
Дмитрий: Тем не менее, Ява умирает. Почему?
status_alexus: Ха. Пошутил :)
Дмитрий: Чем Шарп её обскакал?
status_alexus: Java никогда не умрет ;)
Дмитрий: Ну эта... на твоей фирме 2 года назад на Яве было 20% проектов, сейчас - меньше 5% ;)
status_alexus: .NET ее обскакал кроссязыковостью, пиаром, а также тем, что в Microsoft к разработчикам относятся лучше, чем наверное к пользвователям ;)
Дмитрий: Хе... Как по мне, так для программирования в среде .NET во сто крат лучше использовать шарп, а не что-либо иное
status_alexus: Э тут ты не прав. Не всегда C# тебе подойдет.
Дмитрий: А когда не подойдёт? Когда в большом тиме и не с нуля пишешь?
status_alexus: Мне как-то пришлось использовать язык IronPython - это реализация языка Python под платформу .NET. Есть Windows Service который в зависимости от пришедших данных поднимает ту или иную Assembly, в которой есть реализация некоторого интерфейса. Так вот фабричный метод реализован на языке IronPython. Таким образом, мы можем позволить Deployment новой компоненты без останова сервиса, наживую.
status_alexus: Достаточно выложить новую Assembly и новый скрипт с фабричным методом.
Дмитрий:Мммм, не понял... Это только IronPython позволяет? Помимо возможностей CLR?
status_alexus: IronPython это язык интерпретируемый. А C# копилируется в MSIL
Дмитрий: Оригинально... Я-то думал, что все языки на платформе .NET компилируются в MSIL

Большое спасибо Дмитрию за поставленные вопросы.

воскресенье, 24 июня 2007 г.

Eclipse CDT

На выходных, я как обычно занимаюсь всякими бесполезностями :)

Вот решил тряхнуть стариной и поковырять немного Eclipse как среду разработки для языка C++.

Нашел достаточно внятную заметку, как эту штуку можно настроить вместе с компилятором MinGW. К сожалению, я решил съэкономить на трафике и воспользовался достаточно устаревшей MinGW версии 3.0.8.

В качестве полигона для тестирования я взял свой старый проект Gruz2. Это небольшая игрулька, написаная на C++ и имеет зависимость на библиотеку SDL. Еще несколько лет назад я добился, чтобы она собиралась на Visual C++ 7, Borland C++ Builder 5 и MinGW 3.0.8 (из-под среды Dev-C++) и GCC 3.x под Mandriva Linux 10.1.

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

К сожалению мне так и не удалось осилить настройку линковщика, и рабочей версии приложения так и не получилось :(

Я уже было совсем расстроился от того, что мне ничего большего чем Hello World не удалось выжать, однако я вспомнил еще об одном моем древнем поделии под названием SmallBasic.

Это реализация интерпретатора подмножества языка Basic. Главная его "фишка" заключалась в том, что он имел зависимости только на C++ Standard Template Library и при этом тянул на аж 4 класса из 1363 строк :)

Простите за такие жуткие цифры, этот проект был наколбашен достаточно давно.

Вот как раз этот проект успешно собрался и даже удалось его немного подебажить.

В целом впечатления довольно положительные. Жалко только что MinGW - самый "медленный" C++ компилятор в мире :) Ну уж очень долго он собирает проект.

Вот такие фичи у CDT попали под угол моего зрения:
  • подсветка синтаксиса
  • code complete
  • outline полей и функций активного *.cpp или *.h файла
  • вкладки c открытыми файлами
  • список задач (TODO, предупреждения, ошибки)
  • console output во вкладке
  • подсветка ошибок и предупреждений (насколько я понял поддерживается парсинг GCC/MinGW и Visual C++)
  • поддержка нескольких targets (типа можно настроить на сборку под GCC, MinGW, Visual C++ 7, Borland C++ да еще и под разными ОСями что бы скучно не было)
  • дебагер с показом переменных области видимости и Expression (это типа Watch в Visual Studio)
  • генератор makefile для MinGW и GCC
  • парсер makefile (outline подсветка синтаксиса и т.п.)
И обязательно, документальное подтверждение моих мук:

Аудиопроигрыватель для Linux

Сегодня у меня был очень приятный опыт использования сразу двух программ в Linux. :)

Первая это Exaile - аудиплеер. Достаточно продуманный интерфейс. Функционально программа, в некоторой степени, напоминает iTunes, однако не обременена такими вещами как интерфейс для покупки песен через интернет.
Очень удобный, на мой взгляд, фичер - боковая панель с деревом, контекст которого можно менять. Т.е. вы можете в дереве группировать аудиозаписи по исполнителю, альбому или жанру.
Кроме того, все плейлисты отображаются как табуляторы, таким образом у меня под рукой интерфейс ко всей моей аудиотеке.

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

В общем я, как пользователь достаточно нетривиального Windows-плеера Foobar2000, был очень доволен этим линуховым зверем.

Второй проблемой этого плеера стало отсутствие нормальной поддержки кирилицы в ID3 тегах версии 1. Как многие знают, большинство MP3 файлов имеют теги, которые сохранены в кодировке Windows-1251. А вот в Linux большинство софта по понятным причинам не любит эту кодировку.

Поэтому вторая программа - EasyTAG была ключиком к решению этой проблемы.

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

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

Для решения проблемы с кодировками я настроил эту программу чтобы она читала все ID3 теги в кодировке Windows-1251, и принудительно записывала их в Unicode (UTF-8). Кроме того, я был вынужден запретить сохранение ID3 тегов версии 1, т.к. он не поддерживает многобайтовых кодировок.

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

Еще одной очень полезной особенностью программы EasyTAG является то, что уровень ее подсказок об той или иной настройке представляет из себя компактную энциклопедию по обработке ID3 тегов :)

В общем ОС Linux постепенно "обрастает" действительно классным софтом. Так что даже .NET программист не гнушается этой интересной операционкой ;)

Скриншоты прилагаются:

воскресенье, 17 июня 2007 г.

Кому нужен WinRAR?

Сегодня в очередной раз порадовался своему основному архиватору.



Последний год, или может быть два, я использую архиватор 7-Zip.



Во-первых, алгоритм упаковки LZMA который лег в основу формата 7z дает просто потрясающую степень сжатия. Правда платить за это придется высокоми требования на распаковку, и, особенно, на упаковку. Однако, в настоящее время это не является большой проблемой. И самое главное, что этот формат лицензирован по GNU LGPL, что дает вам полное право использовать этот формат где вам угодно. И как закономерность данного лицензирования - кроссплатформенность.



Сегодня нужно было упаковать игру Quake 2 (ну нужна она мне и все тут :) ), так я был просто поражен результатом.



Оригинальный размер дистрибутива был 210 Мб, который после упаковки превратился в ничтожные 79 Мб. Фантастика, не правда ли ;)



Особенно если учесть что ребята из ID Software молодцы. Они все игровые данные упаковывают в один файл pak.



Правда ложкой дегтя можно назвать требования по ОЗУ для работы с архивами в режиме ультра сжатия: для упаковки требуется 709 Мб, а для распаковки - 66 Мб.



Но согласитесь, на то он и архив. Чтобы хорошенько упаковать и записать на диск.





Powered by ScribeFire.

суббота, 9 июня 2007 г.

Тестовый пост

Прошу прощения за "левый" пост, просто проверяю работу плагина для Firefox





Powered by ScribeFire.