Перейти к содержимому





- - - - -

XPCOM

Опубликовал: topcraze, 12 Ноябрь 2013 · 5 547 Просмотров

Alert: Пишется с позиции системного программиста, с веб-разработкой плохо знакомого.

Итак, XPCOM - это технология, позволяющая получить доступ к каким-либо низкоуровневым вещам и нативным библиотекам через JavaScript в Mozilla Firefox. Теоретически может работать и в Chrome, но я не проверяла и думаю, что там есть какие-то ограничения и свои грабли. Грубо говоря, XPCOM - это ActiveX для Mozill`ы. Плюсы его в кроссплатформенности, в возможности использовать многое в обычной жизни для веб-разработчика недоступное. Минусы.. Эмм.. Вот этого добра тут валом. Начнем с того, что Mozilla нам ничего не обещает. В процессе изучения и поиска мне встретилась просто потрясающая фраза в MDN(о бинарных с++ компонентах) - "Вы не захотите этим заниматься, пока вам это не станет совершенно необходимо" :D К сожалению, ссылку утеряна, если найду - добавлю..После того, как расширение написано, его придется пересобирать каждый раз, когда выходит новая версия Mozilla. И самое главное - постоянно меняющееся SDK точно не даст заскучать.

Но, если вдруг у вас, также как у меня в один прекрасный день партия сказала - "Надо!" и отступать некуда, этот текст для вас.
Что понадобится: xulrunner-sdk 24 версии, python 27, visual studio 2010. Xulrunner-sdk скачиваем, распаковываем в папку c:\xulrunner-sdk. Python устанливаем, добавляем в переменные окружения PATHPYTHON с путем к нему, перезагружаемся.
Первым делом следует создать idl-файл, в котором будут описаны методы, реализуемые нашим компонентом. Я не буду мудрить и возьму ту idl-ку, которая много лет уже кочует из одного мануала в другой, добавив туда строковой метод
#include "nsISupports.idl"
[scriptable, uuid(1DD004E8-DD8A-4DD6-9C08-956BA6A0FF91)]
interface ITestComponent : nsISupports
{
  long Test(in long a, in long b);
  AString RetString(in long index);
};
Из этой idl нужно сгенерировать два файла - h и xpt. Вот тут-то нам и пригодится python, потому как утилиты предоставлены в виде файлов py. Когда-то, в древности пару лет и версий Мозиллы так 15 назад в SDK были и экзешники, но, видимо, кто-то решил, что не стоит упрощать жизнь разработчикам. Ну и ладно.
Для удобства я советую создать батник с приблизительно таким содержимым:
c:\xulrunner-sdk\sdk\bin\header.py -I c:\xulrunner-sdk\idl -o itestcomponent.h testcomponent.idl
c:\xulrunner-sdk\sdk\bin\typelib.py -I c:\xulrunner-sdk\idl -o testcomponent.xpt testcomponent.idl

Замечу, что хидер и xpt подлежат перегенерации при добавлении новых методов в idl.
Переходим в студию, создаем проект типа Empty project и добавляем в него след файлы:
1) сгенерированный h
2) MyComponent.h с следующим содержимым
#include "itestcomponent.h"
// These macros are used in ndnNrtModule.cpp
#define TEST_CLASSNAME  "test component"
#define TEST_CONTRACTID "@any.kz/testcomponent;1"
// "CD232E0F-A777-41A3-BB19-CF415B98088E"
#define TEST_CID \
{ 0x1dd004e8, 0xdd8a, 0x4dd6,\
{0x9c, 0x8, 0x95, 0x6b, 0xa6, 0xa0, 0xff, 0x91}}  
/** 
 * Class description goes here
 */  
class TestComponent: public ITestComponent
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_ITESTCOMPONENT
  TestComponent();
private:
  ~TestComponent();
protected:
  /* additional members */
};
здесь
CID - класс ID нашего компонента, сгенерированный любым удобным способом (например, в Visual Studio)
3) MyComponent.cpp, содержащий в себе реализацию метода нашего класса
#include "TestComponent.h"
#include <string>
#include "nsStringAPI.h"

NS_IMPL_ISUPPORTS1(TestComponent, ITestComponent)
TestComponent::TestComponent()
{
  /* member initializers and constructor code */
}
TestComponent::~TestComponent()
{
  /* destructor code */
}
/* long Add (in long a, in long b); */
NS_IMETHODIMP TestComponent::Test(int32_t a, int32_t b, int32_t *_retval)
{
   *_retval = a+b;
   return NS_OK;
}
NS_IMETHODIMP TestComponent::RetString(int32_t index, nsAString & _retval)
{
    std::string temp_str = "12345";
    std::string temp_str2 = "54321";
    if  (index==1)
    {
        nsString str;
        str.AppendLiteral((char*)temp_str.c_str());
       _retval.Append(str);
    }
    else
    {
        nsString str;
        str.AppendLiteral((char*)temp_str2.c_str());
        _retval.Append(str);
    }
    return NS_OK;
}
4) MyComponentModule.cpp, который содержит в себе необходимые для Mozzila макросы
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"
#include "TestComponent.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(TestComponent)
NS_DEFINE_NAMED_CID(TESTCOMPONENT_CID);
static const mozilla::Module::CIDEntry kNrtcCIDs[] = {
    { &kTESTCOMPONENT_CID, false, NULL, TestComponentConstructor},
    { NULL }
};
static const mozilla::Module::ContractIDEntry kNrtcContracts[] = {
    { TESTCOMPONENT_CONTRACTID, &kTESTCOMPONENT_CID },
    { NULL }
};
static const mozilla::Module::CategoryEntry kNrtcCategories[] = {
    { NULL }
};
static const mozilla::Module kNrtcModule = {
    mozilla::Module::kVersion,
    kNrtcCIDs,
    kNrtcContracts,
    kNrtcCategories
};
NSMODULE_DEFN(ndNrtcModule) = &kNrtcModule;
В свойствах проекта указываем пути к дополнительным инклюдам и либам - соотвественно.
В зависимостях указываем - xul.lib;xpcomglue_s_nomozalloc.lib;nss3.lib
Директивы препроцессора - XPCOM_GLUE;_WINDLL;XP_WIN;XP_WIN32;_MBCS;
Не забываем изменить тип проекта на dll и Code Generation на MT. Можно собирать ))
Поздравляю, полпути пройдено.
Для того, чтобы добавить наше расширение в Mozilla нужно его упаковать. Упаковывается оно в банальный зип, только с расширением xpi. Структура простейшего xpi такая:
  • chrome/
    • content/
  • components/
    • TestComponent.dll
    • TestComponent.xpt
  • install.rdf
  • chrome.manifest
Здесь:
директория components - тут должны находиться файлы dll и xpt. Строго говоря, директория может называться как угодно, но только не забудьте потом исправить ссылки и в chrome.manifest
chrome.manifest - в этом файле описана структура пакета. В нашем случае будет выглядеть так:
interfaces components/TestComponent.xpt
binary-component components/TestComponent.dll
install.rdf - тут в первую очередь следует обратить внимание на параметр unpack. Для бинарных компонентов он должен быть равен true. Наш install.rdf
<?xml version="1.0" ?>
- <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>testcomponent@any.kz</em:id>
  <em:version>0.1</em:version>
  <em:unpack>true</em:unpack>
  <em:type>2</em:type>
- <!--
<pre>
 Target Application this extension can install into, 
         with minimum and maximum supported versions. </pre>
  -->
- <em:targetApplication>
- <Description>
  <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
  <em:minVersion>4.5</em:minVersion>
  <em:maxVersion>99.*</em:maxVersion>
  </Description>
  </em:targetApplication>
  <em:name>Test</em:name>
  <em:description>TestComponent</em:description>
  <em:creator>Any</em:creator>
  <em:homepageURL>http://any.kz</em:homepageURL>
  </Description>
  </RDF>
Как я и говорила ранее, все это упаковывается в zip-архив, после чего полученный xpi можно просто перетащить в браузер. Вас спросят на самом ли деле вы хотите добавить это сомнительное расширение и Firefox перезапустится.
Для того, чтобы убедиться в том, что компонент установился корректно, можно воспользоваться расширением XPCOM Viewer. Если все хорошо, ты вы увидите его в списке.
Компонент установлен, теперь следует проверить его работу. Раньше для вызова XPCOM из JavaScript использовались конструкции такого вида:
const cid = "@any.kz/TestComponent;1";   	
var obj = Components.classes[cid].getService();
var res = obj.Test();
Начиная с 22-ой версии Firefox обращение к Components запрещено. Я попробовала разобраться с тем способом, который предлагает MDN, но тут то ли лыжи, то я.. В общем, лично у меня ничего не получилось, у кого получится - большая человеческая просьба - напишите об этом! А пока я предлагаю использовать подход, который описалPeter Gusev (дай Бог ему здоровья!). В общих чертах этот способ выглядит так: бинарный компонент оборачивается в JavaScript-компонент, который в свою очередь регистрируется в объекте window.
Итак, как выглядит обертка:
let Cu = Components.utils;
let Ci = Components.interfaces;
let Cc = Components.classes;
let Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function TESTCOMPONENTFUNC() {}
TESTCOMPONENTFUNC.prototype = {   
classID: Components.ID("{1619E6AA-7266-440B-809F-B79D2E0D8806}"),   
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),    
init: function(aWindow)
{
  let self = this;
    let api = {
    Test: self.Test.bind(self),
    RetString: self.RetString.bind(self),
    __exposedProps__: {
	    Test: "r",
        RetString: "r"
	    }
    };
    return api;
}, 
Test: function(a, b)
{
    var testcomponent= testcomponent= Cc["@any.kz/testcomponent;1"].createInstance();    
    testcomponent= testcomponent.QueryInterface(Ci.ITestComponent);    
    return testcomponent.Test(a,b);
},
RetString: function(index)
{
   var testcomponent= testcomponent= Cc["@any.kz/testcomponent;1"].createInstance();   
    testcomponent= testcomponent.QueryInterface(Ci.ITestComponent);    
    return testcomponent.RetString(index);
}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([TESTCOMPONENTFUNC]);
Как говорит Петер Гусев, сия конструкция позволяет внедрить в DOM наш компонент. Я в Javascript несильна, поэтому просто верю :D Теперь следует упаковать обертку и компонент вместе, желательно так, что MF понял, что от него требуется. Для этого создадим файл wrapper.manifest
component {1619E6AA-7266-440B-809F-B79D2E0D8806} wrapper.js
contract @any.kz/testcomponent;1 {1619E6AA-7266-440B-809F-B79D2E0D8806}
category JavaScript-global-property testObject @any.kz/testcomponent;1
Этот файл, а также файл с оберткой перенесем в директорию components нашего пакета и внесем изменения в chrome.manifest:
manifest components/wrapper.manifest
interfaces components/testcomponent.xpt
binary-component components/testcomponent.dll
Снова пакуем зипом, переименовываем в xpi и тащим в Firefox. Проверяем наличие зарегистрированного компонента в XPCOMViewer, если все ок, то можно создавать страничку с вызовом. Приблизительно таким:
alert(window.testObject.Test(1,2));
alert(window.testObject.RetString(1));
Готово :)

  • 2


Больше месяца собиралась опубликовать.. Уфф

    • 0

)гы. я такая думаю - это про чо такое странное название)...

 не поняла ни слова - но восхищена)

    • 3

)гы. я такая думаю - это про чо такое странное название)...

:D

 

спасибо, Ри! :)

    • 0

)гы. я такая думаю - это про чо такое странное название)...
а после содержания, название уже не кажется странным  :D
    • 1

ты ни представляешь что я почувствовала когда листала  этот текст :)))))))))))))

    • 0

ты ни представляешь что я почувствовала когда листала  этот текст :)))))))))))))

что? :)

    • 0

 

ты ни представляешь что я почувствовала когда листала  этот текст :)))))))))))))

что? :)

 

"где я?!?! и кто эти люди?!?!" )))))))) какие то слова, слова, слова и ни одного знакомого)))))))))

    • 0
Это же макросы, которые в VBA пишут? Или нет? Только начала учить писать их, поэтому пока полный 0, но Топины труды очень похоже))))

Это же макросы, которые в VBA пишут? Или нет?

нет :)

это с++ и Javascript

    • 0
А, понятно :)

Суровый пост. :)

    • 0

Суровый пост. :)

это не я, это все mozilla :)


и, так как лень мне новый пост создавать, а записать куда-то надо (а всякого рода памятные txt я вечно теряю!), добавлю еще суровости:
чтобы настроить соединение vmware 10 (гест - win7, хост - win7) и windbg для kernel mode, надо
на виртуалке:
1) проверить какой ком-порт свободен, не всегда первый доступен
2) добавить его во вкладке hardware, настроив на пайп
3) other end - application
4) настроить загрузку ОС. Boot.ini нет, поэтому используем bcedit -
bcdedit /debug on
bcdedit /bootdebug on
bcdedit /dbgsettings SERIAL DEBUGPORT:2 (или какой там свободен) BAUDRATE:115200
5) выключить ВМ

на хосте нужно запустить windbg c параметрами windbg.exe -d -k com:pipe,port=\\.\pipe\com_2,reconnect
затем включить ВМ и все, можно отлаживаться

    • 0

И вот после таких постов я начинаю думать, что надо было в старших классах не деффкам хвосты крутить, а поступать в кинотехникум. Многие мои умники и умницы тогда так и сделали. А нынче хто хде в США, израИле, ФРГэ и т.п.

    • 0

гы.. Рене, можно ж было все успешно совмещать :D

 

а почему в кинотехникум?

 

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

 

мне в этом плане больше нравятся выпускники универститета им. С.Димиреля.. кстати, помню, как они нас на ACM порвали :D

    • 0

пользователей просматривает

0 пользователей, 2 неизвестных прохожих, 0 скрытых пользователей

Размещение рекламы на сайте     Предложения о сотрудничестве     Служба поддержки пользователей

© 2011-2022 vse.kz. При любом использовании материалов Форума ссылка на vse.kz обязательна.