barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
Показаны сообщения с ярлыком C. Показать все сообщения
Показаны сообщения с ярлыком C. Показать все сообщения

пятница, 13 апреля 2012 г.

Скробблинг на last.fm с MTP-девайсов в Linux

Скробблить всё, что слушаешь, на ласт - достаточно навязчивая привычка. Т.к. львиную долю музыки я слушал на плеере (сейчас правда перешел на андроидофон), возникло желание как-то вытягивать с него проигранные треки и скробблить на ласт. Встал  вопрос - каким же образом?
Плеер мой - старичок Creative Zen V, никакой операционки в нём и в помине нет, поэтому написанием приложения под неё не отделаться. Писать свою прошивку тоже желания не было =) Надежду во мне посеял тот факт, что плеер сохраняет для каждого трека число его проигрываний, которое можно посмотреть, зайдя в свойства трека в интерфейсе плеера. Осталось теперь научиться получать это число с ПК, и желательно, уметь ещё и обнулять его после скробблинга. Потеря времени проигрывания терка для меня лично была некритична - я готов был скробблить все загруженные с девайса треки "пачкой", начиная с некоторого заданного момента времени.
Т.к. ПК видит мой плеер как MTP-устройство, я сразу решил посмотреть, можно ли по MTP видеть число воспроизведений. Оказалось можно - это умеет даже проигрыватель Amarok. Раз умеет амарок - значит научусь и я, даже если придется покопаться в сорцах амарока. Копаться правда не пришлось - то ли в зависимостях, то ли где-то ещё, я увидел, что для работы с MTP-устаройствами он использует библиотеку libmtp, обладающую, как оказалось, отличной документацией с достаточным количеством примеров.  Так что встала задача разработки приложения, извлекающего проигрывания в некоторую БД, из которой потом уже можно сробблить. 
В итоге получилась консольная программка, извлекающая все треки с девайса и сохраняющая их в БД sqlite, после чего сбрасывающая счетчики проигрываний в плеере. По каким-то причинам, правда, я позже решил отказаться от sqlite, и стал вместо вставки данных в БД формировать SQL-дамп, который можно потом загрузить куда угодно (точнее в ту СУБД, которая съест используемый синтаксис).
Теперь дело встало за малым - извлечь инфу из БД, и заскробблить на ласт. Тут я решил поискать опенсорсный скробблер, т.к. копать REST-API ласта не хотелось. Вариант нашелся, имя ему - lastfmsubmitd, его можно поставить из репов Debian / Ubuntu. В его комплект, помимо демона-скробблера, входит вспомогательный скрипт-скробблер, принимающий параметры скроблимого трека через аргументы командной строки. Задача формирования последовательности вызовов этого скробблера (в виде sh-скрипта) на основании инфы из базы легла на php-скрипт, который написался достаточно быстро. 
В итоге последовательность действий для сробблинга получилась такая:
1) Подключаю девайс, запускаю свою C++-программку, формирую SQL дамп
2) Гружу дамп в MySQL
3) Правлю в php-скрипте время начала скробблинга, запускаю его, получаю на выходе sh-скрипт.
4) Запускаю sh, после того, как он отработает, запускаю демон скробблера lastfmsubmitd. И вуаля - треки улетают на ласт.
Сложновато конечно вышло =) Но работает, и с учетом того, что повторял я эти действия не очень часто - пару раз в неделю, меня это не особо напрягало. Ну а потом я разжился андридофоном (вернее, купил уже 4ый по счету андроидофон, на котором качество проигрывания музыки меня наконец-то устроило), и необходимость скробблить с плеера отпала - сробблеров под андроид полным полно.
Сорцы своих "творений" я выложил на github - вдруг кому будет интересно: https://github.com/barbitoff/mtpScrobblingUtils.

воскресенье, 20 ноября 2011 г.

MS Visual Studio 2010: cl.exe и "mspdb100.dll not found".

Проблема: при вызове компилятора MS Visual Studio 2010 cl.exe из командной строки получаем ошибку: "The application has failed to start bacause mspdb100.dll was not found ...".
Решение:  перед вызовом непосредственно компилятора cl.exe необходимо запустить в той же командной строке vcvars32.bat.

пятница, 28 октября 2011 г.

js-ctypes в Firefox 3.6

В Firefox 3.6 и Gecko 1.9.2 js-stypes ещё был немного недоразвит, но всё же работоспособен.
Для передачи C-функции параметров-строк (или для возврата строк), использующих Юникод (т.е. wchar на стороне C), можно использовать ctypes.ustring вместо появившегося позже типа ctypes.jschar.ptr.
Также в 3.6 нет вообще указателей, т.е. нельзя получить из типа указатель на него, обратившись к полю ptr, и мне кажется, что обойти это ограничение нельзя.
Для типов ctypes.int и ctypes.long имеются аналоги ctypes.int16_t и ctypes.int32_t.

среда, 26 октября 2011 г.

Создание BSTR из nsAString и наоборот

BSTR - тип, используемый для строк в COM.
nsAString - тип, используемый для строк в XPCOM от мозиллы.

bstr_string = SysAllocString(nsastring_string.BeginReading());

И наоборот:

nsastring_string.Assign((wchar_t*)bstr_string ,wcslen(bstr_string ));

(предполагается, что в качестве nsAString::char_type используется PRUnichar, который, в свою очередь, является wchar_t).

вторник, 25 октября 2011 г.

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

BSTR  - тип данных, применяемый в С/С++ для строк при работе с COM. Создается строка типа BSTR вызовом:
BSTR bstrString = SysAllocString(new OLECHAR(L'Hello world'));
или
wchar_t *someStr; ...
BSTR bstrString = SysAllocString(someStr);

Подробнее про тип BSTR прочитать можно тут: http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx

Создание расширения Firefox на C++ c использованием XPCOM

Внимание: использование XPCOM уже не рекомендуется разработчиками Mozilla (как минимум из-за того, что придется перекомпилировать бинарные компоненты с выходом новой версии FF, которые теперь будут выходить часто). В версиях FF 4+ можно использовать гораздо более простой способ - js-ctypes (https://developer.mozilla.org/en/js-ctypes/Using_js-ctypes). js-ctypes, с небольшими изменениями, работает даже в FF 3.6.20 (например, там нет ctypes.char, но есть ctypes.ustring).

Уф, один рабочий день и наконец-то получилось понять, как же все-таки писать расширения для FF с использованием XPCOM. MDN оказался куда хуже чем я ожидал - туториалы все древние и не работают с новыми версиями xulrunner-sdk, а то, что удалось накопать по новому движку Gecko2 - толком не описано, поэтому пришлось искать решение практически методом тыка.

Задача для начала простая - написать расширение, к которому можно было обратиться из JavaScript`а на веб-странице  вызвать метод echo, принимающий на входе строку и возвращающий её же.

Для начала качаем Gecko SDK, он же xulrunner-sdk: http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/7.0.1/sdk/xulrunner-7.0.1.en-US.win32.sdk.zip. Затем - проект-пример: https://developer.mozilla.org/samples/xpcom/xpcom-test.zip. Проект создан для старой версии xulrunner, поэтому компилироваться не будет, но всё же удобнее использовать его чем начинать с нуля. 
Пусть xulrunner-sdk распакован в "O:\xulrunner-sdk", а проект-пример - в O:\xpcom-test.

Во-первых, нужно описать интерфейс будущего расширения на языке idl (это не майкросовтовский IDL, а мозилловский собственный). Создаем файл (пусть он будет называться echo.idl, его нужно положить в O:\xpcom-test) со следующим содержимым (uuid можно сгенерировать любым генератором UUID):
#include "nsISupports.idl"
[scriptable, uuid(27110427-209a-4f4b-b829-8ca5ebe356a6)]
interface ISpecialThing : nsISupports
{
 AString echo(in AString srcStr);
};
В проект нужно внести следующие изменения:
  • Открываем xpcom-test/xpidl-build.bat и меняем его содержимое на:
    ..\xulrunner-sdk\bin\xpidl.exe -m header -I..\xulrunner-sdk\idl echo.idl
    ..\xulrunner-sdk\bin\xpidl.exe -m typelib -I..\xulrunner-sdk\idl echo.idl
    Запускаем батник, находясь в O:\xpcom-test (создадутся файлы echo.xpt и echo.h).
  • Открываем проект xpcom-test (я использовал MS VS 2005: во-первых, другого под рукой не нашлось, да и проект подойдет для него без конвертации). 
    • Заходим в свойства проектаC/C++ > General > Additional Include Directories, меняем на "..\xulrunner-sdk\include".
    • Затем - в Linker > General > additional Library Directories, ставим "..\xulrunner-sdk\lib"
    • Linker > Input > Additional Dependencies, "nspr4.lib xpcom.lib xpcomglue_s.lib mozalloc.lib"
  • Исключаем из проекта все файлы, кроме созданного нами idl. В свойствах idl-файла отменяем его компиляцию ("Exclude from build" = Yes). Включаем в проект созданный в пункте 1 h-файл с заголовком интерфейса. В комментариях этот файл содержит заготовку для класса, реализующего этот интерфейс.
  • Создаем класс, реализующий описанный в h-файле интерфейс. Пусть он будет называться echoImpl. Ниже приведен код заголовочного файла этого класса:
#include "echo.h"
#include "nsStringAPI.h"
#define ECHO_CONTRACTID "@some.domain.com/echosample;1"
#define ECHO_CLASSNAME "echosample"
#define ECHO_CID { 0xe99152f7, 0x73f7, 0x45d3, { 0x8e, 0x94, 0x26, 0x3e, 0x21, 0x10, 0x63, 0x57 } }
static const nsCID kEchoCID = IACSIGN_CID;
class echoImpl: public ISpecialThing
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_ISPECIALTHING
  echoImpl();
  ~echoImpl();
};
Define`ами в данном файле задает 3 важных параметра будущего компонента:  
    • ECHO_CONTRACTID - contract id компонента, имеющий вид домен/модуль/компонент;версия и используемый для идентификации компонента в массиве Components.classes при обращении из JavaScript.
    • ECHO_CLASSNAME - имя класса компонента (не нашел пока, где оно используется при эксплуатации компонента)
    •  ECHO_CID - уникальный GUI класса компонент. Сгенерировать его можно любым средством генерации GUID (например, http://www.guidgenerator.com/), после чего представить в указанном виде.
Теперь - само тело класса:
#include "echoImpl.h"
#include "nsStringAPI.h"
#include "nsIClassInfoImpl.h"
#include "nsMemory.h"
NS_IMPL_CLASSINFO(echoImpl, NULL, 0, ECHO_CID)
NS_IMPL_ISUPPORTS1_CI(echoImpl, ISpecialThing) 
echoImpl::echoImpl(){}
echoImpl::~echoImpl(){} 
NS_IMETHODIMP echoImpl::echo(const nsAString & srcStr, nsAString & _retval NS_OUTPARAM)
{
return NS_StringCopy(_retval,srcStr);
}
Тут все предельно просто - пара макросов и собственно метод echo.
  • Теперь необходимо описать модуль - код, который будет сообщать FF о написанном нами модуле. Создаем файл echoModule.cpp со следующим наполнением (код взят из примера http://mxr.mozilla.org/mozilla-central/source/xpcom/sample/, который, правда, не является 100% рабочим):
#include "echoImpl.h"
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"
////////////////////////////////////////////////////////////////////////
// With the below sample, you can define an implementation glue
// that talks with xpcom for creation of component nsSampleImpl
// that implement the interface nsISample. This can be extended for
// any number of components.
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the object nsSampleImpl
//
// What this does is defines a function nsSampleImplConstructor which we
// will specific in the nsModuleComponentInfo table. This function will
// be used by the generic factory to create an instance of nsSampleImpl.
//
// NOTE: This creates an instance of nsSampleImpl by using the default
// constructor nsSampleImpl::nsSampleImpl()
//
NS_GENERIC_FACTORY_CONSTRUCTOR(echoImpl);
// The following line defines a kNS_SAMPLE_CID CID variable.
NS_DEFINE_NAMED_CID(ECHO_CID);
// Build a table of ClassIDs (CIDs) which are implemented by this module. CIDs
// should be completely unique UUIDs.
// each entry has the form { CID, service, factoryproc, constructorproc }
// where factoryproc is usually NULL.
nsresult myF (nsISupports* aOuter,
                 const nsIID& aIID,
                 void** aResult) {
*aResult = new echoImpl();
return NS_OK;
}
static const mozilla::Module::CIDEntry kSampleCIDs[] = {
{ &kEchoCID , false, NULL, myF },
    { NULL }
};
// Build a table which maps contract IDs to CIDs.
// A contract is a string which identifies a particular set of functionality. In some
// cases an extension component may override the contract ID of a builtin gecko component
// to modify or extend functionality.
static const mozilla::Module::ContractIDEntry kSampleContracts[] = {
    { ECHO_CONTRACTID , &kEchoCID },
    { NULL }
};
// Category entries are category/key/value triples which can be used
// to register contract ID as content handlers or to observe certain
// notifications. Most modules do not need to register any category
// entries: this is just a sample of how you'd do it.
// @see nsICategoryManager for information on retrieving category data.
static const mozilla::Module::CategoryEntry kSampleCategories[] = {
    //{ "my-category", "my-key", ECHO_CONTRACTID },
    { NULL }
};
static const mozilla::Module kSampleModule = {
    mozilla::Module::kVersion,
    kSampleCIDs,
    kSampleContracts,
    kSampleCategories
};
// The following line implements the one-and-only "NSModule" symbol exported from this
// shared library.
NSMODULE_DEFN(nsSampleModule) = &kSampleModule;
// The following line implements the one-and-only "NSGetModule" symbol
// for compatibility with mozilla 1.9.2. You should only use this
// if you need a binary which is backwards-compatible and if you use
// interfaces carefully across multiple versions.
// NS_IMPL_MOZILLA192_NSGETMODULE(&kSampleModule)

Здесь стоит обратить внимание на функцию myF - она возвращает объект созданного ранее класса echoImpl. В примере с MDN её не было (точнее имя-то функции было, а вот тела - нет).

Всё, проект можно компилировать. Если всё пройдет ок, то на выходе получим dll-файл, и представляющий собой компонент (к совокупности с полученным ранее xpt-файлом, описывающим его интерфейс).

Теперь дополнение необходимо добавить в FF. Для этого понадобиться xpi-файл со следующей структурой:
  • components/ - директория, куда нужно положить колченные xpt и dll файлы
  • chrome.manifest - файл реестра FF, описывающий компонент, со следующим содержимым:
binary-component components/echo.dll
interfaces components/echo.xpt
component {99152f77-3f74-5d38-e942-63e21106357} components/echo.dll
Здесь в фигурных скобках указан GUID, установленный в коде компонента с помощью константы ECHO_CID.

  •  install.rdf - файл, описывающий компонент и необходимый для его установки. Минимальное содержимое файла следующее:
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>echo@demo.com</em:id>
    <em:type>2</em:type>
    <em:name>Echo echo</em:name>
    <em:version>0.2.1</em:version>
    <em:creator>barbitoff</em:creator>
    <em:contributor/>
    <em:description>Blablabla echo</em:description>
    <em:aboutURL/>
<em:unpack>true</em:unpack>
    <em:targetApplication>
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--FF-->
        <em:minVersion>3.0</em:minVersion>
        <em:maxVersion>7.*</em:maxVersion>
      </Description>
    </em:targetApplication>
  </Description>
</RDF>
Такое содержимое  install.rdf позволит установить xpi на версии FF от 3.0 до 7.* (т.е. любой 7ой версии).
Всё, теперь пакуем созданную структуру в zip-файл, меняем расширение на xpi и перетаскиваем его на окно FF. Компонент должен успешно установиться, перезапускаем FF. Вызвать наш echo-метод из JS можно:
var a = Components.classes["@some.domain.com/echosample;1"].createInstance();
alert(a.echo("HELLO WORLD!"));
Примечание - на эту операцию требуется разрешение. Читал, что оно устанавливается вызовом netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect") (естественно, использовать его можно только в тестовых целях, а не на боевых веб-страницах), но у меня почему-то и с ним не работает. Приведенный выше вызов работает из js-консоли Firebug, и должен также работать при вызове из js-кода расширения.

пятница, 15 апреля 2011 г.

undefined reference to 'timer_settime'

Проблема:
При компиляции программы, работающей с таймером, линковщик выдает ошибки типа:
undefined reference to 'timer_create'
undefined reference to 'timer_settime'
Решение: при компиляции использовать ключ -lrt

Вот примерчик их использования для периодической генерации SIGALRM:

#include <signal.h>
#include <sys/time.h>
#include <time.h>
...
timer_t mytimer;
struct sigevent timer_event;
/*
* - use signals
* - send SIGALRM
*/
timer_event.sigev_notify=SIGEV_SIGNAL;
timer_event.sigev_signo= SIGALRM;
if (timer_create(CLOCK_REALTIME, &timer_event, &mytimer) < 0)
{
printf("timer create error");
exit(0);
}
if(timer_settime(mytimer,CLOCK_REALTIME,&timerSettings,NULL)!=0)
{
printf("timer setting error");
exit(0);
}
while(1)
pause();