Announcement

Collapse
No announcement yet.

Browser-PlugIn in Delphi programmieren

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Browser-PlugIn in Delphi programmieren

    Hallo Ihr Lieben,

    wie programmiert man ein Browser-Plugin ähnlich der Google-Toolbar? Ist dies mit Delphi möglich? Welche Web-Technologien benötigt man dafür?

    Wer kann mir Tipps geben, wie ich mehr zu diesem Thema rausfinde?

    Besten Dank
    Jörg

  • #2
    Hallo,

    über ein eigenes COM-Objekt kann man ein eigenes Delphi-Formular dem Internet Explorer als Toobar unterschieben. Das eigene COM-Objekt muss dazu das <b>IDeskBand</b>-Interface sowie einige weitere von Windows bzw. der Internet Explorer vordefinierte Interfaces implementieren. Um an die TLB-Datei für die Internet Explorer-Interfaces zu kommen, muss in Delphi einmalig die Typbibliothek der <i>Microsoft Internet Controls</i> importiert werden.

    Der interne Aufbau des COM-Objekts stellt prinzipiell keine Besonderheit dar. Um die vom Internet Explorer erwarteten Registry-Einträge zu setzen bzw. bei der De-Installation wieder zu entfernen, greift das COM-Objekt auf eine eigene Class Factory zurück. Da diese die wichtigen Sachen von <b>TComObjectFactory</b> erbt, müssen nur die IE-spezifischen Registry-Einträge gesetzt werden.

    Das folgende Beispiel greift auf ein Grundgerüst zurück, das ich aus dem Internet gezogen habe:

    <pre>

    <font color="#003399"><i>//************************************************** *********</i></font>
    <font color="#003399"><i>// Band Objects (May 24, 2000) *</i></font>
    <font color="#003399"><i>// ver. 1.1 *</i></font>
    <font color="#003399"><i>// For Delphi 5 *</i></font>
    <font color="#003399"><i>// by *</i></font>
    <font color="#003399"><i>// Per Linds&#248 Larsen *</i></font>
    <font color="#003399"><i>// [email protected] *</i></font>
    <font color="#003399"><i>// Updated versions: *</i></font>
    <font color="#003399"><i>// http://www.euromind.com/iedelphi *</i></font>
    <font color="#003399"><i>// http://www.intelligo.net/iedelphi *</i></font>
    <font color="#003399"><i>//************************************************** *********</i></font>

    <b>unit</b> OSIEBand_Impl;

    <b>interface</b>

    <b>uses</b>
    Windows, Classes, ActiveX, ShlObj, ComServ, ComObj,
    Controls, SysUtils, Messages, Forms,
    SHDocVw_TLB, <font color="#003399"><i>// Typbibliothek &quot;Microsoft Internet Controls 1.1&quot; importieren</i></font>
    OssiSoftForm; <font color="#003399"><i>// Formular, das im IE angezeigt werden soll</i></font>

    <b>const</b>
    DeskBand = <font color="#9933CC">'{00021492-0000-0000-C000-000000000046}'</font>;
    VerticalBand = <font color="#9933CC">'{00021493-0000-0000-C000-000000000046}'</font>;
    HorizontalBand = <font color="#9933CC">'{00021494-0000-0000-C000-000000000046}'</font>;

    <font color="#003399"><i>{ ----------------- Anpassen --------------------- }</i></font>

    <font color="#003399"><i>// Band-Beschriftung</i></font>
    Caption = <font color="#9933CC">'OssiSoft'</font>;
    <font color="#003399"><i>// Horizontales oder vertikales Band ?</i></font>
    BandType = HorizontalBand;
    <font color="#003399"><i>// Toolband soll erzeugt werden</i></font>
    ToolBand = TRUE;
    <font color="#003399"><i>// eigene CLSID erzeugen (Delphi-IDE : Ctrl-Shift-G)</i></font>
    CLSID_OSIEBAND: TGUID = <font color="#9933CC">'{3C3CB83A-231E-4548-BCB6-AE976CBAE0EF}'</font>;

    <b>type</b>
    TOSIEBandFactory = <b>class</b>(TComObjectFactory)
    <b>private</b>
    <b>procedure</b> AddKeys;
    <b>procedure</b> RemoveKeys;
    <b>public</b>
    <b>procedure</b> UpdateRegistry(Register: Boolean); override;
    <b>end</b>;

    TOSIEBand = <b>class</b>(TComObject, IDeskBand, IPersistStreamInit,
    IObjectWithSite, IContextMenu, IInputObject)
    <b>private</b>
    MenuItems : Integer;
    SavedWndProc: TWndMethod;
    HasFocus: Boolean;
    BandID: DWORD;
    ParentWnd: HWND;
    Site: IInputObjectSite;
    cmdTarget: IOleCommandTarget;
    BandForm: TBandform;
    <b>public</b>
    <font color="#003399"><i>// IDeskBand</i></font>
    <b>function</b> GetBandInfo(dwBandID, dwViewMode: DWORD; <b>var</b> pdbi: TDeskBandInfo):
    HResult; <b>stdcall</b>;
    <b>function</b> ShowDW(fShow: BOOL): HResult; <b>stdcall</b>;
    <b>function</b> CloseDW(dwReserved: DWORD): HResult; <b>stdcall</b>;
    <b>function</b> ResizeBorderDW(<b>var</b> prcBorder: TRect; punkToolbarSite: IUnknown;
    fReserved: BOOL): HResult; <b>stdcall</b>;
    <b>function</b> GetWindow(out wnd: HWnd): HResult; <b>stdcall</b>;
    <b>function</b> ContextSensitiveHelp(fEnterMode: BOOL): HResult; <b>stdcall</b>;
    <font color="#003399"><i>// IPersistStreamInit</i></font>
    <b>function</b> InitNew: HResult; <b>stdcall</b>;
    <b>function</b> GetClassID(out classID: TCLSID): HResult; <b>stdcall</b>;
    <b>function</b> IsDirty: HResult; <b>stdcall</b>;
    <b>function</b> Load(<b>const</b> stm: IStream): HResult; <b>stdcall</b>;
    <b>function</b> Save(<b>const</b> stm: IStream; fClearDirty: BOOL): HResult; <b>stdcall</b>;
    <b>function</b> GetSizeMax(out cbSize: Largeint): HResult; <b>stdcall</b>;
    <font color="#003399"><i>// IObjectWithSite</i></font>
    <b>function</b> SetSite(<b>const</b> pUnkSite: IUnknown): HResult; <b>stdcall</b>;
    <b>function</b> GetSite(<b>const</b> riid: TIID; out site: IUnknown): HResult; <b>stdcall</b>;
    <font color="#003399"><i>// IContextMenu</i></font>
    <b>function</b> QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; <b>stdcall</b>;
    <b>function</b> InvokeCommand(<b>var</b> lpici: TCMInvokeCommandInfo): HResult; <b>stdcall</b>;
    <b>function</b> GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; <b>stdcall</b>;
    <font color="#003399"><i>/// IInputObject </i></font>
    <b>function</b> UIActivateIO(fActivate: BOOL; <b>var</b> lpMsg: TMsg): HResult; <b>stdcall</b>;
    <b>function</b> HasFocusIO: HResult; <b>stdcall</b>;
    <b>function</b> TranslateAcceleratorIO(<b>var</b> lpMsg: TMsg): HResult; <b>stdcall</b>;
    <b>procedure</b> BandWndProc(<b>var</b> Message: TMessage);
    <b>procedure</b> FocusChange(bHasFocus: Boolean);
    <b>procedure</b> UpdateBandInfo;
    <b>end</b>;

    <b>var</b>
    IE: IWebbrowser2;

    <b>implementation</b>

    <b>uses</b> Dialogs, Registry;

    <font color="#003399"><i>{---------------------------------------------------------------- }</i></font>
    <font color="#003399"><i>{ Eigene Class Factory setzt Registry-Eintraege beim Registrieren }</i></font>
    <font color="#003399"><i>{ a) Delphi-ID: Start | ActiveX-Server eintragen }</i></font>
    <font color="#003399"><i>{ b) Registrierung über &quot;regsvr32 osieband.dll&quot; }</i></font>
    <font color="#003399"><i>{---------------------------------------------------------------- }</i></font>

    <b>procedure</b> TOSIEBandFactory.UpdateRegistry(Register: Boolean);
    <b>begin</b>
    <b>inherited</b> UpdateRegistry(Register);
    <b>if</b> Register <b>then</b>
    AddKeys
    <b>else</b>
    RemoveKeys;
    <b>end</b>;

    <b>procedure</b> TOSIEBandFactory.AddKeys;
    <b>var</b>
    S: <b>string</b>;
    <b>begin</b>
    S := GUIDToString(CLSID_OSIEBAND);
    <b>with</b> TRegistry.Create <b>do</b>
    <b>try</b>
    <font color="#003399"><i>// http://support.microsoft.com/support/kb/articles/Q247/7/05.ASP -&gt;</i></font>
    <b>if</b> BandType &lt;&gt; DeskBand <b>then</b>
    <b>begin</b>
    DeleteKey(<font color="#9933CC">'Software\Microsoft\Windows\Curren tVersion\Explorer\Discardable\PostSetup\Component Categories\'</font> + VerticalBand + <font color="#9933CC">'\Enum'</font>);
    DeleteKey(<font color="#9933CC">'Software\Microsoft\Windows\Curren tVersion\Explorer\Discardable\PostSetup\Component Categories\'</font> + HorizontalBand + <font color="#9933CC">'\Enum'</font>);
    <b>end</b>;
    RootKey := HKEY_CLASSES_ROOT;
    <b>if</b> OpenKey(<font color="#9933CC">'CLSID\'</font> + S, True) <b>then</b>
    <b>begin</b>
    WriteString(<font color="#9933CC">''</font>, <font color="#9933CC">'&amp;'</font> + Caption);
    CloseKey;
    <b>end</b>;
    <b>if</b> OpenKey(<font color="#9933CC">'CLSID\'</font> + S + <font color="#9933CC">'\InProcServer32'</font>, True) <b>then</b>
    <b>begin</b>
    WriteString(<font color="#9933CC">'ThreadingModel'</font>, <font color="#9933CC">'Apartment'</font>);
    CloseKey;
    <b>end</b>;
    <b>if</b> OpenKey(<font color="#9933CC">'CLSID\'</font> + S + <font color="#9933CC">'\Implemented Categories\'</font> + BandType, True) <b>then</b>
    CloseKey;
    <b>if</b> Toolband <b>then</b>
    <b>begin</b>
    RootKey := HKEY_LOCAL_MACHINE;
    <b>if</b> OpenKey(<font color="#9933CC">'SOFTWARE\Microsoft\Internet Explorer\Toolbar'</font>, True) <b>then</b>
    <b>begin</b>
    WriteString(S, <font color="#9933CC">''</font>);
    CloseKey;
    <b>end</b>;
    <b>end</b>;
    <b>finally</b>
    Free;
    <b>end</b>;
    <b>end</b>;

    <b>procedure</b> TOSIEBandFactory.RemoveKeys;
    <b>var</b> S: <b>string</b>;
    <b>begin</b>
    S := GUIDToString(CLSID_OSIEBAND);
    <b>with</b> TRegistry.Create <b>do</b>
    <b>try</b>
    RootKey := HKEY_CLASSES_ROOT;
    <font color="#003399"><i>// http://support.microsoft.com/support/kb/articles/Q214/8/42.ASP -&gt;</i></font>
    <b>if</b> BandType = DeskBand <b>then</b>
    DeleteKey(<font color="#9933CC">'Component Categories\'</font> + DeskBand + <font color="#9933CC">'\Enum'</font>);
    DeleteKey(<font color="#9933CC">'CLSID\'</font> + S + <font color="#9933CC">'\Implemented Categories\'</font> + BandType);
    DeleteKey(<font color="#9933CC">'CLSID\'</font> + S + <font color="#9933CC">'\InProcServer32'</font>);
    DeleteKey(<font color="#9933CC">'CLSID\'</font> + S);
    Closekey;
    <b>if</b> ToolBand <b>then</b>
    <b>begin</b>
    RootKey := HKEY_LOCAL_MACHINE;
    OpenKey(<font color="#9933CC">'Software\Microsoft\Internet Explorer\Toolbar'</font>, FALSE);
    DeleteValue(s);
    CloseKey;
    <b>end</b>;
    <b>finally</b>
    Free;
    <b>end</b>;
    <b>end</b>;

    <font color="#003399"><i>{----------------------------------------------------------- }</i></font>
    <font color="#003399"><i>{ Standard-Implementierung (keine Änderungen notwendig) }</i></font>
    <font color="#003399"><i>{----------------------------------------------------------- }</i></font>

    <b>function</b> TOSIEBand.QueryContextMenu(Menu: HMENU;
    indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
    <b>begin</b>
    Result := 0;
    <b>end</b>;

    <b>function</b> TOSIEBand.InvokeCommand(<b>var</b> lpici: TCMInvokeCommandInfo): HResult;
    <b>begin</b>
    <b>if</b> (HiWord(Integer(lpici.lpVerb)) &lt;&gt; 0) <b>or</b>
    (LoWord(lpici.lpVerb) &gt; MenuItems - 1) <b>then</b>
    <b>begin</b>
    Result := E_FAIL;
    Exit;
    <b>end</b>;
    Result := NO_ERROR;
    <b>end</b>;

    <b>procedure</b> TOSIEBand.UpdateBandInfo;
    <b>var</b>
    vain, vaOut: OleVariant;
    PtrGuid: PGUID;
    <b>begin</b>
    vaIn := Variant(BandID);
    New(PtrGUID);
    PtrGUID^ := IDESKBAND;
    cmdTarget.Exec(PtrGUID, DBID_BANDINFOCHANGED, OLECMDEXECOPT_DODEFAULT,
    vaIn, vaOut);
    Dispose(PtrGUID);
    <b>end</b>;

    <b>function</b> TOSIEBand.GetBandInfo(dwBandID, dwViewMode: DWORD;
    <b>var</b> pdbi: TDeskBandInfo): HResult;
    <b>begin</b>
    BandId := dwBandID;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_MINSIZE) &lt;&gt; 0
    <b>then</b> <b>begin</b>
    pdbi.ptMinSize.y := BandForm.height;
    pdbi.ptMinSize.x := 0;
    <b>end</b>;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_MAXSIZE) &lt;&gt; 0
    <b>then</b> <b>begin</b>
    pdbi.ptMaxSize.x := -1;
    pdbi.ptMaxSize.y := Bandform.Height;
    <b>end</b>;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_INTEGRAL) &lt;&gt; 0
    <b>then</b> <b>begin</b>
    pdbi.ptIntegral.x := 1;
    pdbi.ptIntegral.y := 1;
    <b>end</b>;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_ACTUAL) &lt;&gt; 0
    <b>then</b> <b>begin</b>
    pdbi.ptActual.x := Bandform.Height;
    pdbi.ptActual.y := bandform.Width;
    <b>end</b>;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_MODEFLAGS) &lt;&gt; 0 <b>then</b>
    <b>begin</b>
    pdbi.dwModeFlags := DBIMF_NORMAL;
    <b>end</b>;
    <b>if</b> (pdbi.dwMask <b>or</b> DBIM_BKCOLOR) &lt;&gt; 0 <b>then</b>
    <b>begin</b>
    pdbi.dwMask := pdbi.dwMask <b>and</b> (<b>not</b> DBIM_BKCOLOR);
    <b>end</b>;
    <b>if</b> (Pdbi.dwMask <b>and</b> DBIM_TITLE) = DBIM_TITLE
    <b>then</b> <b>begin</b>
    FillChar(pdbi.wszTitle, SizeOf(Caption) + 1, <font color="#9933CC">' '</font>);
    StringToWideChar(Caption, @pdbi.wszTitle, Length(Caption) + 1);
    <b>end</b>;
    Result := NOERROR;
    <b>end</b>;

    <b>procedure</b> TOSIEBand.BandWndProc(<b>var</b> Message: TMessage);
    <b>begin</b>
    <b>if</b> (Message.Msg = WM_PARENTNOTIFY) <b>then</b>
    <b>begin</b>
    HasFocus := True;
    FocusChange(True);
    <b>end</b>;
    SavedWndProc(Message);
    <b>end</b>;

    <b>function</b> TOSIEBand.GetWindow(out wnd: HWnd): HResult;
    <b>begin</b>
    <b>if</b> <b>not</b> Assigned(BandForm) <b>then</b>
    BandForm := TBandForm.CreateParented(ParentWnd);
    Wnd := Bandform.Handle;
    SavedWndProc := Bandform.WindowProc;
    Bandform.WindowProc := BandWndProc;
    Result := S_OK;
    <b>end</b>;

    <b>procedure</b> TOSIEBand.FocusChange(bHasFocus: Boolean);
    <b>begin</b>
    <b>if</b> (Site &lt;&gt; <b>nil</b>) <b>then</b>
    Site.OnFocusChangeIS(Self, bHasFocus);
    <b>end</b>;

    <b>function</b> TOSIEBand.TranslateAcceleratorIO(<b>var</b> lpMsg: TMsg): HResult;
    <b>begin</b>
    <b>if</b> (lpMsg.WParam &lt;&gt; VK_TAB) <b>then</b>
    <b>begin</b>
    TranslateMessage(lpMSg);
    DispatchMessage(lpMsg);
    Result := S_OK;
    <b>end</b>
    <b>else</b>
    Result := S_FALSE;
    <b>end</b>;

    <b>function</b> TOSIEBand.HasFocusIO: HResult;
    <b>begin</b>
    Result := Integer(<b>not</b> HasFocus);
    <b>end</b>;

    <b>function</b> TOSIEBand.UIActivateIO(fActivate: BOOL;
    <b>var</b> lpMsg: TMsg): HResult;
    <b>begin</b>
    Hasfocus := fActivate;
    <b>if</b> HasFocus <b>then</b>
    Bandform.SetFocus;
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.SetSite(<b>const</b> pUnkSite: IUnknown): HResult;
    <b>begin</b>
    <b>if</b> Assigned(pUnkSite) <b>then</b>
    <b>begin</b>
    Site := pUnkSite <b>as</b> IInputObjectSite;
    (pUnkSite <b>as</b> IOleWindow).GetWindow(ParentWnd);
    cmdTarget := pUnkSite <b>as</b> IOleCommandTarget;
    (CmdTarget <b>as</b> IServiceProvider).QueryService(IWebbrowserApp,
    IWebbrowser2, IE);
    <b>end</b>;
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.GetSite(<b>const</b> riid: TIID; out site: IUnknown): HResult;
    <b>begin</b>
    <b>if</b> Assigned(Site) <b>then</b>
    Result := Site.QueryInterface(riid, site)
    <b>else</b>
    Result := E_FAIL;
    <b>end</b>;

    <b>function</b> TOSIEBand.ShowDW(fShow: BOOL): HResult;
    <b>begin</b>
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.GetClassID(out classID: TCLSID): HResult;
    <b>begin</b>
    classID := CLSID_OSIEBAND;
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.CloseDW(dwReserved: DWORD): HResult;
    <b>begin</b>
    <b>if</b> BandForm &lt;&gt; <b>nil</b> <b>then</b> BandForm.Destroy;
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
    <b>begin</b>
    Result := NOERROR;
    <b>end</b>;

    <b>function</b> TOSIEBand.ContextSensitiveHelp(fEnterMode: BOOL): HResult;
    <b>begin</b>
    Result := E_NOTIMPL;
    <b>end</b>;

    <b>function</b> TOSIEBand.ResizeBorderDW(<b>var</b> prcBorder: TRect; punkToolbarSite: IUnknown;
    fReserved: BOOL): HResult;
    <b>begin</b>
    Result := E_NOTIMPL;
    <b>end</b>;

    <b>function</b> TOSIEBand.IsDirty: HResult;
    <b>begin</b>
    Result := S_FALSE;
    <b>end</b>;

    <b>function</b> TOSIEBand.Load(<b>const</b> stm: IStream): HResult;
    <b>begin</b>
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.Save(<b>const</b> stm: IStream; fClearDirty: BOOL): HResult;
    <b>begin</b>
    Result := S_OK;
    <b>end</b>;

    <b>function</b> TOSIEBand.GetSizeMax(out cbSize: Largeint): HResult;
    <b>begin</b>
    Result := E_NOTIMPL;
    <b>end</b>;

    <b>function</b> TOSIEBand.InitNew: HResult;
    <b>begin</b>
    Result := E_NOTIMPL;
    <b>end</b>;

    <b>initialization</b>
    TOSIEBandFactory.Create(ComServer, TOSIEBand, CLSID_OSIEBAND,
    <font color="#9933CC">''</font>, Caption, ciMultiInstance);
    <b>end</b>.

    </pre&gt

    Comment


    • #3
      guten tag,

      könntest Du mir bitte etwas genauer erklären, wie ich vorgehn muss, damit ich mit diesem unit und einer form eine toolbar für den IE machen kann?
      oder darf ich Dir meine source zukommen lassen?
      bzw willst Du mir Deine source zukommen lassen?
      (mailto: [email protected] )

      danke

      Comment


      • #4
        Hallo,

        das Beispielprojekt ist schon etwas angestaubt (Delphi 5 und Internet Explorer 5), ich füge es trotzdem als ZIP-Archiv bei.

        Wenn das COM-Objekt registriert wird, meldet es sich automatisch beim IE an. Die eigene Toolbar ist immer dann im Internet Explorer sichtbar, solange das COM-Objekt registriert bleibt. Sobald in Delphi über <i>Start | ActiveX-Server austragen</i> das eigene COM-Objekt wieder aus der Registry entfernt wird, sorgt die eigene Class Factory <i>TOSIEBandFactory dafür</i>, dass auch die IE-spezifischen Registry-Einträge wieder entfernt werden

        Comment


        • #5
          Wie gibt am denn so ein Browser-Plugin weiter??
          Sprich ist es egal wohin die Dll kopiert wird?
          Wie kann man die Installation so vornehmen, das das Plugin auch gleich angezeigt wird?
          Gruss

          Stefa

          Comment


          • #6
            Hallo,

            &gt;Sprich ist es egal wohin die Dll kopiert wird?

            Ja.

            &gt;Wie kann man die Installation so vornehmen, das das Plugin auch gleich angezeigt wird?

            Das COM-Objekt (DLL) muss registriert werden: <br>
            a) entweder über das Installationsprogramm, oder <br>
            b) über das Windows-Zubehört <b>regsvr32.exe</b> von der Kommandozeile aus.

            Bei der Registrierung (Weg a oder b) wird die Methode <b>UpdateRegistry</b> (siehe oben) aufgerufen, so dass das COM-Objekt die vom IE erwarteten Registry-Einträge setzen kann

            Comment


            • #7
              Hallo

              Ich hab erfolgreich eine Toolbar gebaut. Funzt prächtig

              Ich habe jedoch folgende Fehlfunktion:

              Ich lasse mir testweise die URL des IE anzeigen, wo die Toolbar angezeigt wird. Öffnet nun eine Webseite eine neue Seite, dann springt die URL auf diese Seite um, obwohl ich die Abfrage im "Parent"Fenster durchführe.

              Schliesse ich das neu geöffnete Fenster, ist die URL plötzlich leer (!!). Es wird also nicht zum alten Fenster zurückgekehrt.

              Wie kann ich das "Überspringen" des Focus verhindern? Ich möchte immer die URL haben, die in dem IE Fenster angezeigt wird, in dem auch die Abfrage erfolgt :

              Comment


              • #8
                Hallo !!

                Ich habe das gleiche Problem mit dem Focus.... gibt es inzwischen einen Lösungsansatz

                Comment

                Working...
                X