Блогът на Ладжър

септември 19, 2008

IE Binary Behaviors - Част 1 или “това к’во е?”

Публикувано в: Elements, Общи — Етикети:, , , — admin @ 5:53 pm

Владислав Косев,
Development Manager

Ще започна първо с това, какво е това и какво може да се прави с него.

Какво е това?

Казано накратко, това е технология, която позволява да правите неща, които иначе правите с JavaScript, само че повече и по-бързи.

Вариант 1

Можете да си направите клас, който да “закачите” към някакъв таг, да речем някакъв <div>, който имате, и да му добавите допълнителни свойства и методи. Освен това можете да рисувате върху него с Windows GDI и GDI+.

Вариант 2

Можете директно да си направите собствен таг, да речем <MyBetterDiv/>, който може всичките гореизборени неща, можете да го направите да участва в POST-а на форма, можете да направите супер комплексно съдържание, без това да се вижда в главния документ като елементи (ViewLink, обяснено е тук), можете целия да го нарисувате (вместо да използвате HTML елементи за тази цел), можете да му бъзикате фокуса, layout-а, парсването, можете да изпълнявате значително повече команди, отколкото при JS изпълнението на WYSIWYG редактор и още тон други неща.

Как работи?

Въпросният клас е всъщност COM клиент, а браузъра е COM сървър. Браузъра комуникира с нашия клас чрез интерфейси. Всичките интерфейси, необходими за комуникацията, се намират в mshtml.dll. За да се използва това в .NET обаче, трябва да се направи PIA (Primary Interop Assembly) - това е assembly, което представлява посредник, между .NET и COM сървъра. Такъв има направен от Microsoft и идва обикновено с Visual Studio (или някое от SDK-тата). Можете и сами да си направите, само че имайте предвид, че файла е колосален - 7 мегабайтов DLL, който някъде четох, че му трябват 4G (или сходна умопомрачителна цифра) RAM, за да се компилира.

Та. Как точно браузъра открива нашия файл, че да го load-не, че да намери кой отговаря за тага, че да ги върже двамката да си приказват?

IElementBehaviorFactory и IElementNamespaceFactory2

Има един подход на програмиране, който се казва Factory. При него, не се конструира директно класът, който ти трябва, а ми има втори клас, който играе точно ролята на фабрика, и има обикновено някакъв метод от сорта на .CreateЕдиЩоСи. Та, в нашия случай, в HEAD на HTML-а се паркира един <object> таг, който има един атрибут classid, в който пише един GUID. Тези GUID-ове са общо взето ID-та, по които се индентифицират класове и интерфейси в Windows и се записват в регистрите - един кой си GUID е един кой си клас/интефейс и се намира в едй-кой си файл, който се намира еди-къде си.

Та, браузъра вижда тоз таг, взима GUID-а и почва да рови в регистрите. Там намира, че това е еди-кой си файл (което е нашето assembly) и търси вътре клас с даденото име (това го пише в регистрите). След като докопа класа, пробва дали не имплементира IElementBehaviorFactory интерфейса (който общо взето казва - ако намериш таг, който не знаеш какво да го правиш, питаш мене). Тук има и един финт с namespace, слага се един <?import namespace=”my” implementation=”(id-то на object тага)”?> таг, който казва, че за всички тагове, които започват с my, трябва да пита factory-то, чийто object таг има даденто ID.

Например:

<object id="CustomControls" classid="CLSID:3CF8817B-58DF-4C4A-96BB-21C0A8D822D7" ></object>
<?import namespace="Ladger" implementation="#CustomControls" />

Нашият клас пък, започва така:

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), Guid("3CF8817B-58DF-4C4A-96BB-21C0A8D822D7"), ProgId("Elements.IE.ElementFactory")]
public class ElementFactory : IObjectSafety, IElementBehaviorFactory, IElementNamespaceFactory2

Този клас прави 2 неща: добавя тагове в namespace-а (тага <?import ?>) и създава класове срещу тагове.

Тагове се добавят в метода: 

 

public void CreateWithImplementation(IElementNamespace pNamespace, string bstrImplementation) {
     pNamespace.AddTag("line", 0);
}

Така казваме, че тагът line е наш, т.е. ние отговаряме за него. След това, когато браузъра намери такъв таг, извиква друг метод:

[return: MarshalAs(UnmanagedType.Interface)]
IElementBehavior FindBehavior(string behav, string behavUrl, COM.IElementBehaviorSite behavSite) {
   if(behav == "line"){
      return new LineTagClass();
   }
}

С този метод браузъра общо взето казва: “Глей ся, тука намерих един таг, твърди се, че е твой, така че ми дай, ако обичаш, клас, който имплементира IElementBehavior, който да вържа за този таг”. Ние, съответно, връщаме нашия клас и оттам нататък браузъра и този клас си говорят, фабриката повече не я занимават с проблемите си.

Идеята зад цялото нещо е следната: браузъра няма идея как да намира и конструира класовете, който отговарят за всеки таг, затова има един клас, с който си говори на тези теми.

Има и един интерфейс, който трябва да се имплементира и той е IObjectSafety, който казва дали компонента е Safe For Scripting (има таква настройка в security зоните). Той има два метода:

 

public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions) {
    pdwSupportedOptions = 0x00000001 | 0x00000002; // INTERFACESAFE_FOR_UNTRUSTED_CALLER and _DATA (Nathan, p. 677).
    pdwEnabledOptions = 0x00000001 | 0x00000002;
    return 0; // S_OK.
}
public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionsSetMaks, int dwEnabledOptions) {
    return 0; // S_OK;
}

 

Това са трите интерфейса, който тряебва да имплементира един клас, за да може оттам нататък само да си правите HTML елементи. Във FindBehavior например, си паркирате един switch и според тага, връщате съответния клас.

Интерфейси и COM

Интерфейсите са основния компонент на COM (Component Object Model) архитектурата. Те са основния начин даден обект да предостави функционалности на друг. Интерфейсите са езико-независими, т.е., могат да се дефинират на C++, на C#  или общо взето на каквото си искате. В нашия случай, те са дефинирани на C++ (в mshtml.dll). Ние само използваме дефинициите (те са преведени на C# в PIA-то, но е съвсем лесно човек да си ги преведе сам, само трябва да знае кой COM тип в какъв .NET-ски се обръща). Има разни грешки в PIA-то на Microsoft (сбъркани типове на аргументи или връщан тип), хората из интернет са публикували общо взето тяхни корекции и не е трудно човек да си намери дефиници на коригираните интерфейси и да си ги ползва тях.

2 Коментара »

  1. Hi,

    I’m trying to implement exactly similar thing, but I have a problem - FindBehaviour didn’t called after I added my BehaviourFactory ActiveX object on the page and activated it in such way:

    canvas {
    behavior: url(#canvasbehaviourfactory);
    }

    Could you help me with that?

    Thanks,
    Paul.

    Коментар от Paul — март 24, 2009 @ 12:37 pm

  2. Can you paste the tag here and the declaration of the C# class. The CSS seems allright, maybe you have an error in the C# class or the Object Tag. Note the interfaces you should implement (IObjectSafety, IElementBehaviorFactory, IElementNamespaceFactory2).

    Коментар от blademf — март 24, 2009 @ 6:27 pm

RSS хранилка за коментарите по тази публикация. Адрес за TrackBack

Вашият коментар

Ladger