Страницы: 1
Есть необходимость использовать 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.
Отсутствует
Первое, что бросается в глаза - это неправильно определенный на 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.
Или можно поменять директиву вызова на safecall. Тогда обработка ошибок в Delphi будет комовской и все что вы получите - EOleException с кодом исключения.
А вообще интересно было бы посмотреть на SDK и xpidl-компилятор, которые вы используете, а также полный код программы.
Отсутствует
Благодарю за Ваши пояснения и исправления в определении интерфеса!
Программа теперь отрабатывает нормально, НО, к сожалению, желаемого результата от нее все равно пока получить не могу. Вот полный текст программы:
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 (включая этот компилятор) но вроде в систему его не ставил.
Отсутствует
Какие 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 в паскалевский или сишный хедер. У вас там была ошибка, очевидно вы все делали вручную.
Отсутствует
Еще раз спасибо за быстрый ответ!
Вообщем-то я и сам сомневался, что включенный пример работает. Похоже, что этот SDK писался просто под другой GRE...
Мне нужно читать и записывать cookies из/в базу браузера, установленного в системе, из внешнего приложения. FireFox3 использует SQLite для хранения кук. С этим я уже разобрался. Все бы хорошо, но этот механизм не позволяет редактировать куки "на лету": т.е. пока браузер запущен, он не видит изменений, сделанных извне (хотя и видит их после перезапуска).
Т.е. в моем случае, на сколько я понял, может помочь только написание расширения? Что ж, буду рад если подскажете, как это лучше сделать. На каком языке его лучше писать, так чтобы можно было потом обеспечить нормальное взаимодействие с приложением на дельфях?
P.S. Спасибо за пояснение по поводу xpidl-компилятора! Действительно все делал вручную.
Отсутствует
Расширения пишутся на 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).
Отсутствует
Спасибо 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 имеет значение, как ее устанавливать сессионной или нет? (пока разницы не заметил)
Отсутствует
Но недавно столкнулся с необходимостью записывать сессионные куки, и возникла проблема. Вот вырезка из кода:
Выделить кодКод:
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 почему-то не появляется. Что может быть не так в коде?
Я не проверял, но проблема может быть в формате передаваемых данных. Возможно, для домена обязательно должна первой идти точка, а для пути обязательный закрывающий слеш. Поэкспериментируйте с этим. И, по-моему, результат успешного завершения - это 0, а не 1.
Отсутствует
А вообще нет. Пардон за беспокойство - код работает Правда он работает, когда фф окончательно загрузился, а вот с моментом загрузки - проблема: мне нужно перебить сессионную куку новой, когда первая грузится из sessionstore.js. А вот как отловить момент "догрузки" фф - не ясно. Если слишко рано пытаться выставлять куку (а ставлю я ее на событии DLL_PROCESS_ATTACH) - фф периодически валится или вообще ее не выставляет, а подгружает старую, из сохраненной сессии. А вот если ее ставить с задержкой в потоке - то уже чаще ставится (но тоже не всегда).
Что касается макроса NS_SUCCEEDED, он возвращает 1 в случае успешного завершения (проверено). А точку перед доменом - да нужно ставить (но этим я озаботился еще раньше).
Отсутствует
А вот как отловить момент "догрузки" фф - не ясно. Если слишко рано пытаться выставлять куку (а ставлю я ее на событии DLL_PROCESS_ATTACH) - фф периодически валится или вообще ее не выставляет, а подгружает старую, из сохраненной сессии. А вот если ее ставить с задержкой в потоке - то уже чаще ставится (но тоже не всегда).
Все менеджеры mozilla будут, по идее, уже инициализированы в CreateInstance интерфейса nsIFactory. Это то место, где должен создаваться экземпляр вашего объекта. Так что, правильно бы было добавлять куки в его кострукторе, а не на загрузку библиотеки в адресное пространство процесса.
Отсутствует
Вообще, экземпляр моего объекта похоже даже не создается (по крайней мере в конструктор объекта я под дебеггером не попал), ведь я никогда явно не запрашиваю свой интерфейс. Поэтому куда сунуть вызов своей функции даже не заню, может переписать NS_GENERIC_FACTORY_CONSTRUCTOR под себя, добавив в конец вызов этой функции, или это не поможет? (надо будет проверить на досуге)
Отсутствует
Страницы: 1