Announcement

Collapse
No announcement yet.

Event in einem eigenen COM-Object setzen

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

  • Event in einem eigenen COM-Object setzen

    Es ist ja einfach, über ein Interface einem COM-Object Daten zu übermitteln. Solange es sich um "gewöhnliche" Datentypen wie Integer, Widestring u.s.w. handelt, ist das kein Problem. Wie übergebe ich aber z.B. ein tNotifyEvent oder ein anderes Event?
    Konkret handelt es sich um ein hypertextfähige RTF-Komponente als COM-Object, wo ich je nach Anwendung auf einen Link unterschiedlich reagieren muß. Also muß ich von außen den betreffenden Event (vom Typ tHyperLinkEvent) übergeben.
    tHyperlinkEvent hat folgende Deklaration:

    procedure (Sender: TObject; text, stamp: String; LineNumber: Longint) of Object;

    Ich muß vermutlich diesen Typ registrieren, aber wie sieht die Implementation aus?

  • #2
    Hallo,

    es ist hier nur von einem COM-Objekt als In-Process Server (DLL) die Rede, richtig? Wenn ja, wird auf dieses Objekt nur innerhalb des gleichen Apartments (Thread) zugegriffen? Wenn ja, ist <b>kein</b> Marshaler beteiligt, so dass fast alle COM-Regeln ausser Kraft gesetzt sind. Da beide Stellen im gleichen Adressraum liegen, kann man sogar Zeiger als Parameter der Interface-Methode übergeben. Die Einschränkung "Automation-kompatible Datentypen" gilt nur für Dual Interfaces (d.h. für IDispatch) und nur dann, wenn ein Marshaler eine Apartment-Grenze überbrücken muss.

    Generell würde ich jedoch mein Design überdenken, wenn <b>Methoden-Zeiger</b> übergeben werden sollen. Anstelle eine Implementierung der Ereignisbehandlungsmethode als Zeiger zu übergeben, ist es normalerweise üblich, das der Client (Aufrufer) ein eigenes Sink-Objekt erzeugt, und den Interface-Zeiger darauf an den COM-Server übergibt. Immer dann, wenn der Server dann ein Event auslöst, ruft er das Sink-Objekt über diese Interface-Zeiger direkt auf. Im Fall des Rückrufs (Callbacks) wird das COM-Objekt zum "Client" und das Programm zum "Server". Damit das Programm ungestört weiterlaufen kann, ist es sinnvoll, dieses Sink-Objekt in einem eigenen Thread auszuführen. Dann allerdings ist eine Apartmentgrenze da, so dass die Einschränkungen zu den Parametern gelten. Es sei denn, man greift auf <b>IStream</b> und <b>IRecordInfo</b> zurück - hier kann man fast alles (von komplexen Strukturen und Records bis zum kompletten Speicherblöcken) transportieren.
    &#10

    Comment


    • #3
      Danke für die schnelle Antwort. Es ist in der Tat ein in-Process-Server, allerdings handelt es sich um ein Dual-Interface. Ich könnte aber auch ein zusätzliches iUnknown-Interface implementieren, dann fallen die Beschränkungen ja zum größten Teil weg.

      Mit den Sink-Objekten habe ich bisher noch keine Erfahrungen gemacht, scheint sich aber zu lohnen.

      Bei der Gelegenheit gerade noch eine Frage. Eines meiner COM-Objekte (ein Fenster, in dem sich u.a. ein TreeView und ein DBGrid befindet) erzeugt beim Beenden des Programms, das die COM-Objekte benutzt, einen Run-Time-Error 216 (sichtbar beim Debuggen unter Delphi, ansonsten Fehler beim Lesen auf Adresse xxxx). Dieser Fehler erscheint aber nur, wenn das Fenster auch gezeigt wird. Ein Kreieren des COM-Objektes ohne Anzeige hat keine negativen Folgen. Folglich müßte der Fehler beim Aufbau des Fensters passieren (FormCreate, Formactivate, TimerClick). Wird das besagte Fenster in einer gewöhnlichen EXE benutzt, gibts keine Probleme.
      Das merkwürdige ist, daß andere COM-Objekte, die ähnlich aufgebaut sind überhaupt keine Probleme bereiten. Es scheint was beim Release schief zu gehen, bloß wie kann man das feststellen (debuggen geht ja schlecht)

      Comment


      • #4
        Hallo,

        hinter dem Laufzeitfehler 216 verbirgt sich eine EAccessViolation-Exception, die auftritt, nachdem Delphi sein internes Exception-Handling bereits abgeräumt hat. Wenn das Projekt Runtime-Packages verwendet, würde ich darauf verzichten. Falls nicht, so würde ich mir die folgenden Punkte genauer anschauen: <br>
        - Wird <b>CoInitialize/CoUninitialize</b> vom aufrufenen Programm korrekt gesetzt (entweder implizit durch Delphi-Units oder explizit von Hand)? <br>
        - Wenn der Fehler nur dann auftritt, wenn ein Fenster sichtbar wird, wie wird dieses Fenster zerstört? Wird auch wirklich <b>Release</b> verwendet (d.h. das Fenster sollte die Chance erhalten, alle anstehenden Botschaften vorher abzuarbeiten)?<br>
        - Wird auf eines der Objekt intern direkt (also ohne Interface) zugegriffen?<br>
        - Verwendet das COM-Objekt auch wirklich ein STA (Single Threaded Apartment)?

        Die Anzeige eines Formulars aus einem COM-Objekt könnte zum Beispiel so aussehen:
        <pre>
        uses
        ComObj, ActiveX, FrmInCOMSrv_TLB;

        type
        TFrmSrv = class(TAutoObject, IFrmSrv)
        protected
        procedure ShowForm(hWndParent: Integer); safecall;
        procedure ShowFormResult(const MsgText: WideString;
        out UserText: WideString); safecall;
        { Protected-Deklarationen }
        end;

        implementation

        uses ComServ, FrmInCOMSrv_Frm, FrmInCOMSrv_Frm2, Controls, Windows, Forms;

        procedure TFrmSrv.ShowForm(hWndParent: Integer);
        begin
        Application.Handle := hWndParent;
        FormSrvDlg := TFormSrvDlg.Create(nil);
        try
        FormSrvDlg.ShowModal;
        finally
        FormSrvDlg.Release
        end;
        end;
        procedure TFrmSrv.ShowFormResult(const MsgText: WideString;
        out UserText: WideString);
        begin
        UserText := '(kein Kommentar)';
        FormSrvDlg2 := TFormSrvDlg2.Create(nil);
        try
        if FormSrvDlg2.ShowModal = mrOK
        then UserText := FormSrvDlg2.EditUserText.Text;
        finally
        FormSrvDlg2.Release
        end;
        end;

        initialization
        TAutoObjectFactory.Create(ComServer, TFrmSrv, Class_FrmSrv,
        ciMultiInstance, tmApartment);
        end.
        </pre&gt

        Comment


        • #5
          Das Problem konnte ich heute nach mühevollem Ausprobieren erschlagen. Ursache war eine Memory-Table, die in einer variablen Konstante abgelegt war. Diese wurde beim Beenden nicht geschlossen und "gefreed", was unter der EXE-Version offenbar problemlos ist. Ich hab dann in der Formdestroy dies nachgeholt und seitdem keine Problem mehr.

          Nochmals vielen Dank für Ihre Hilfe

          Comment

          Working...
          X