>Форум Mozilla Россия http://forum.mozilla-russia.org/index.php >Разработка http://forum.mozilla-russia.org/viewforum.php?id=18 >Применение Gecko SDK в Delphi http://forum.mozilla-russia.org/viewtopic.php?id=33322 |
Delivron > 01-04-2009 18:17:39 |
Есть необходимость использовать XPCOM в Delphi. Конкретно - для управления Cookies из своего приложения. Скачал и установил Gecko SDK под Delphi. Скомпилировал прилагаемый пример - запускается (хотя правильно ли он работает, я так и не понял, но под дебаггером вроде все идет нормально). Далее, поскольку требуемых мне интерфейсов в текущей версии Gecko SDK под Delphi не оказалось, описал их самостоятельно. Теперь сама проблема: подгрузить интерфейсы удается (указатель получаю), но вызов их функций в лучшем случае просто не приводит к ожидаемым результатам, в худшем - инициирует аварийный выход. Если кто знает, подскажите, пожалуйста, что не так делаю. Вот доопределенные мною интерфейсы: Выделить код Код:const NS_COOKIEMANAGER_CONTRACTID = '@mozilla.org/cookiemanager;1'; type nsICookie2 = interface(nsICookie) ['{736619fe-8d09-4e59-8223-32f176c22977}'] end; nsICookieManager2 = interface(nsICookieManager) ['{5047cab4-9cb2-4927-a4ab-77422bc3bc67}'] procedure add ( domain: nsACString; path: nsACString; name: nsACString; value: nsACString; isSecure: PRBool; isHttpOnly: PRBool; isSession: PRBool; expiry: PRInt64 ); stdcall; function cookieExists ( cookie: nsICookie2 ): PRBool; stdcall; function countCookiesFromHost ( host: nsACString ): PRUint32; stdcall; procedure importCookies ( cookieFile: nsIFile ); stdcall; end; А вот попытка их применить: Выделить код Код:var cookie_man : nsICookieManager2; n: Cardinal; icsDomain: IInterfacedCString; begin GRE_Startup; NS_CreateInstance(NS_COOKIEMANAGER_CONTRACTID, nsICookieManager2, cookie_man); n := cookie_man.RemoveAll; // просто ничего не происходит: ожидаю - очищение кук из FireFox icsDomain := NewCString; icsDomain.Assign('somehost.ru'); n := cookie_man.countCookiesFromHost(icsDomain.ACString); // возвращает 0, хотя куки приутствуют, кроме того, вызов этой функции в итоге инициализирует аварийное завершение программы GRE_Shutdown; end. |
Elexander > 02-04-2009 10:51:03 |
Первое, что бросается в глаза - это неправильно определенный на Delphi интерфейс nsICookieManager2. Он должен выглядеть так: Выделить код Код:nsICookieManager2 = interface(nsICookieManager) ['{5047CAB4-9CB2-4927-A4AB-77422BC3BC67}'] function Add(const aDomain: nsACString; const aPath: nsACString; const aName: nsACString; const aValue: nsACString; aIsSecure: LongBool; aIsHttpOnly: LongBool; aIsSession: LongBool; aExpiry: PRInt64): HRESULT; stdcall; function CookieExists(aCookie: nsICookie2; var _retval: LongBool): HRESULT; stdcall; function CountCookiesFromHost(const aHost: nsACString; var _retval: Cardinal): HRESULT; stdcall; function ImportCookies(aCookieFile: nsIFile): HRESULT; stdcall; end; То есть, при директиве вызова stdcall должен возвращаться HRESULT для всех методов, а результат помещаться в _retval. А вообще интересно было бы посмотреть на SDK и xpidl-компилятор, которые вы используете, а также полный код программы. |
Delivron > 02-04-2009 12:08:25 |
Благодарю за Ваши пояснения и исправления в определении интерфеса! Программа теперь отрабатывает нормально, НО, к сожалению, желаемого результата от нее все равно пока получить не могу. Вот полный текст программы: Выделить код Код:program Project1; {$APPTYPE CONSOLE} uses nsXPCOM, nsXPCOMGlue, nsTypes, nsError, nsGeckoStrings, nsNetUtil, SysUtils; const NS_COOKIEMANAGER_CONTRACTID = '@mozilla.org/cookiemanager;1'; type nsICookie2 = interface(nsICookie) ['{736619fe-8d09-4e59-8223-32f176c22977}'] end; nsICookieManager2 = interface(nsICookieManager) ['{5047cab4-9cb2-4927-a4ab-77422bc3bc67}'] function add ( domain: nsACString; path: nsACString; name: nsACString; value: nsACString; isSecure: PRBool; isHttpOnly: PRBool; isSession: PRBool; expiry: PRInt64 ): nsresult; stdcall; function cookieExists ( cookie: nsICookie2; out _retval: PRBool ): nsresult; stdcall; function countCookiesFromHost ( host: nsACString; out _retval: PRUint32 ): nsresult; stdcall; function importCookies ( cookieFile: nsIFile ): nsresult; stdcall; end; var cookie_man : nsICookieManager2; res: nsresult; n: Cardinal; icsDomain: IInterfacedCString; begin GRE_Startup; icsDomain := NewCString; NS_CreateInstance(NS_COOKIEMANAGER_CONTRACTID, nsICookieManager2, cookie_man); res := cookie_man.RemoveAll; icsDomain.Assign('somehost.ru'); res := cookie_man.countCookiesFromHost(icsDomain.ACString, n); GRE_Shutdown; end. Gecko SDK для Delphi можно скачать здесь: http://ftp.newbielabs.com/Delphi%20Geck … Readme.htm. Что касается xpidl-компилятора... должен признаться, что не совсем понимаю, что это. Однако, как мне кажется, отдельно его не ставил. Правда, несколько раньше я хотел скомпилировать сам FireFox под VS2005 и качал C-шное SDK (включая этот компилятор) но вроде в систему его не ставил. |
Elexander > 02-04-2009 16:19:53 |
Какие Cookies вы хотите получить? Броузера встроенного в ваше приложение или установленного в системе? У меня пример из этого SDK не заработал. По-идеи, должен показаться броузер, вместо этого - пустая панель. Или неправильный SDK, или неправильный GRE. В nsXPCOMGlue есть nsGREDirServiceProvider.GetFile, так вот он в момент инициализации броузера постоянно возвращает NS_ERROR_FAILURE. Думаю ошибка в инициализации GRE. Похоже, что это какой-то недоделанный проект. Если вам нужен в приложении встроенный броузер, то можно посмотреть на Mozilla ActiveX control (http://www.iol.ie/~locka/mozilla/contro … singDelphi). А если нужны cookies установленного в системе броузера, то нужно разбираться с форматом файла в котором они хранятся, или писать экстеншен и вычитывать при запущеном броузере. Опишите задачу, может тогда еще чем-то смогу помочь. P.S. xpidl-компилятор переводит интерфейс idl в паскалевский или сишный хедер. У вас там была ошибка, очевидно вы все делали вручную. |
Delivron > 02-04-2009 17:34:16 |
Еще раз спасибо за быстрый ответ! Вообщем-то я и сам сомневался, что включенный пример работает. Похоже, что этот SDK писался просто под другой GRE... Мне нужно читать и записывать cookies из/в базу браузера, установленного в системе, из внешнего приложения. FireFox3 использует SQLite для хранения кук. С этим я уже разобрался. Все бы хорошо, но этот механизм не позволяет редактировать куки "на лету": т.е. пока браузер запущен, он не видит изменений, сделанных извне (хотя и видит их после перезапуска). Т.е. в моем случае, на сколько я понял, может помочь только написание расширения? Что ж, буду рад если подскажете, как это лучше сделать. На каком языке его лучше писать, так чтобы можно было потом обеспечить нормальное взаимодействие с приложением на дельфях? P.S. Спасибо за пояснение по поводу xpidl-компилятора! Действительно все делал вручную. |
Elexander > 03-04-2009 10:33:14 |
Расширения пишутся на XUL и Java Script. Здесь есть достаточно материала для старта: http://forum.mozilla-russia.org/viewtopic.php?id=4393 Для взаимодействия с внешней программой можна использовать XPCOM-компонент. Например, броузер при старте загружает экстеншен, тот просто поднимает компонент и дальше вся работе ведется в нем. Компонент можно написать на Delphi или С++, а для взаимодействия использовать любую межпроцесcную технологию (http://msdn.microsoft.com/en-us/library … S.85).aspx). |
Delivron > 17-08-2009 15:32:26 |
Спасибо Elexander'у за ссылки! Написал на С++ компонент, связался с ним через pipe, вроде все заработало. Но недавно столкнулся с необходимостью записывать сессионные куки, и возникла проблема. Вот вырезка из кода: Выделить код Код:if (t) bRes = NS_SUCCEEDED(cookieMgr2->Add(aDomain, aPath, aName, aValue, PR_FALSE, PR_FALSE, PR_FALSE, t)); else bRes = NS_SUCCEEDED(cookieMgr2->Add(aDomain, aPath, aName, aValue, PR_FALSE, PR_FALSE, PR_TRUE, t)); // сессионная В обоих ветвях результат возвращаяется положительный (1). Однако сессионная кука в FireFox почему-то не появляется. Что может быть не так в коде? И еще вопрос: для установки куки вида <name>=deleted имеет значение, как ее устанавливать сессионной или нет? (пока разницы не заметил) |
Elexander > 18-08-2009 10:34:55 |
Delivron пишет
Я не проверял, но проблема может быть в формате передаваемых данных. Возможно, для домена обязательно должна первой идти точка, а для пути обязательный закрывающий слеш. Поэкспериментируйте с этим. И, по-моему, результат успешного завершения - это 0, а не 1. |
Delivron > 18-08-2009 16:47:02 |
А вообще нет. Пардон за беспокойство - код работает Правда он работает, когда фф окончательно загрузился, а вот с моментом загрузки - проблема: мне нужно перебить сессионную куку новой, когда первая грузится из sessionstore.js. А вот как отловить момент "догрузки" фф - не ясно. Если слишко рано пытаться выставлять куку (а ставлю я ее на событии DLL_PROCESS_ATTACH) - фф периодически валится или вообще ее не выставляет, а подгружает старую, из сохраненной сессии. А вот если ее ставить с задержкой в потоке - то уже чаще ставится (но тоже не всегда). Что касается макроса NS_SUCCEEDED, он возвращает 1 в случае успешного завершения (проверено). А точку перед доменом - да нужно ставить (но этим я озаботился еще раньше). |
Elexander > 19-08-2009 10:19:13 |
Delivron пишет
Все менеджеры mozilla будут, по идее, уже инициализированы в CreateInstance интерфейса nsIFactory. Это то место, где должен создаваться экземпляр вашего объекта. Так что, правильно бы было добавлять куки в его кострукторе, а не на загрузку библиотеки в адресное пространство процесса. |
Delivron > 21-08-2009 12:10:20 |
Вообще, экземпляр моего объекта похоже даже не создается (по крайней мере в конструктор объекта я под дебеггером не попал), ведь я никогда явно не запрашиваю свой интерфейс. Поэтому куда сунуть вызов своей функции даже не заню, может переписать NS_GENERIC_FACTORY_CONSTRUCTOR под себя, добавив в конец вызов этой функции, или это не поможет? (надо будет проверить на досуге) |
Elexander > 21-08-2009 12:26:25 |
А что мешает явно запросить интерфейс в инициализации расширения и сохранить в глобальной переменной? |