Announcement

Collapse
No announcement yet.

COM/DCOM mit Delphi

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

  • COM/DCOM mit Delphi

    Hallo Herr Kosch,<br>
    ich arbeite z.Z. Ihr Buch COM/DCOM mit Delphi durch.<br>
    Dabei ist mir etwas (für mich) merkwürdiges aufgefallen.<br>
    Auf den Seiten 165 und 166 beschreiben Sie den COM-Contest2 mit Threads.<br>
    Dem Thread-constructor übergeben Sie das Object TStaticText mit SText. Dieses Object<br>
    ist ein VCL-Object des Hauptprozesses.<br>
    <p>
    So jetzt die Frage:<br>
    Geht das in Ordnung, wenn Sie auf dieses Object ohne Synchronize in der Execute<br>
    des Threads zugreiffen ?</p>

    <p>
    procedure TCOMThread.Execute;<br>
    var<br>
    &nbsp;&nbsp;iCnt,<br>
    &nbsp;&nbsp;iStart,<br>
    &nbsp;&nbsp;iStop : Integer;<br>
    &nbsp;&nbsp;cBrutto : Currency;<br>
    begin<br>
    &nbsp;&nbsp;iStart := GetTickCount;<br>
    &nbsp;&nbsp;for iCnt := 1 to FCount do begin<br>
    &nbsp;&nbsp;&nbsp;&nbsp;if FDual then begin<br>
    &nbsp;&nbsp;&nbsp;&nbsp;FEarly.SetPercent(16);< br>
    &nbsp;&nbsp;&nbsp;&nbsp;cBrutto := FEarly.GetBrutto(100.75);<br>
    &nbsp;&nbsp;end else begin<br>
    &nbsp;&nbsp;&nbsp;&nbsp;FLate.SetPercent(16);<b r>
    &nbsp;&nbsp;&nbsp;&nbsp;cBrutto := FLate.GetBrutto(100.75);<br>
    &nbsp;&nbsp;end;<br>
    &nbsp;&nbsp;end;<br>
    &nbsp;&nbsp;iStop := GetTickCount;<br>
    &nbsp;&nbsp;FValue := iStop - iStart;<br>
    &nbsp;&nbsp;if FDual<br>
    &nbsp;&nbsp;&nbsp;&nbsp;then FText.Caption := 'Early: ' + IntToStr(FValue)<br>
    &nbsp;&nbsp;&nbsp;&nbsp;else FText.Caption := 'Late: ' + IntToStr(FValue);<br>
    end;<br>
    </p>

  • #2
    Hallo Jens,

    diese Frage beantwortet das Programm selbst - sowohl das Programm als auch Win32 hat zur Laufzeit nichts daran auszusetzen ;-)

    Der Aufruf von <b>Synchronize</b> bewirkt nichts anderes, als das die dort aufgerufenen Funktionen erst nach einem Threadwechsel direkt im primären Thread der Anwendung ausgeführt werden. Somit sorgt Synchronize <b>garantiert</b> dafür, das keine parallelen Zugriffe auf die VCL entstehen können. Dieser Abschottungsgrad ist im o.g. Beispielprogramm nicht notwendig - da nur ein einziger Thread auf eine konkrete TLabel-Instanz im Hauptformular zugreift. Außerdem soll das Beispielprogramm Zeiten <i>messen</i> - so daß alle störenden Beeinflussungen (wie zum Beispiel ein erzwungener Threadwechsel) minimiert werden müssen. Außerdem ist die VCL im Laufe der beiden letzten Delphi-Versionen (4 bzw. 5) in wichtigen Teilen threadsicher geworden - was nicht bedeutet, daß Synchronize überhaupt keine Daseinsberechtigung hat

    Comment


    • #3
      Hi

      Wenn ich das richtig sehe nutz das Beispiel vom Andreas eine TStaticText Componente und keine TLabel Komponente, und genau da liegt der Unterschied. Eine TStaticText Componente hat ein reguläres Window handle. Somit wird mit StaticText.Caption := 'XYZ' eine message der Form SendMessage(StaticText.handle, wm_setText, ...) gesendet. Windows synchronisiert eine gesendete Message mit dem zugehörigen Thread des Fensterhandles. Genau die selbe Vorgehenweise nutz auch Synchronize. Mit einer TLabel Componente sieht das ein bißchen anders aus, da TLabel.Caption := '..' nicht unbedingt mit Messages über Fensterhandles agiert. Der häufigste Crash Punkt ist dabei die methode Paint, also der Zugriff auf das Canvas Object vom TLabel. Aber auch da hat delphi ab version 4 vorgesorgt, indem es den Canvas sperrt. Meiner Meinung nach ist das Beispiel zwar nicht absolut Threadsicher, aber mindestens genauso sicher wie mit Synchronize.

      Gruß hage

      Comment


      • #4
        Hallo,
        ich habe ein Problem mit dem AgentIntro-Programm von der Konferenz99 unter Delphi 5.0.

        procedure TForm1.GetAnimationList(aSL: TStrings; aAgent: IAgentCtlCharacterEx);
        const
        IID_IEnumVariant: TGUID = (
        D1:$00020404;D2:$0000;D3:$0000;D4$C0,$00,$00,$00,$00,$00,$00,$46));
        var
        pEnum :IEnumVARIANT ;
        vAnimName :VARIANT ;
        dwRetrieved WORD ;
        hRes : HResult;
        begin
        aSL.Clear;
        pEnum := aAgent.AnimationNames.Enum as IEnumVARIANT;
        while (TRUE) do
        begin
        !!! Jetzt kommt es
        hRes := pEnum.Next(1, vAnimName, @dwRetrieved); <-- an den Variablen vAnimName und dwRetrieved meckert der Compiler herum.
        !!! -----
        if hRes <> S_OK then
        Break;
        aSL.add(vAnimName);
        end;
        end;

        Außerdem bekomme ich den Office Einstein nicht auf den Schirm, obwohl dieser in Office zu sehen ist. Die Datei des Einstein heißt bei mir auch Genius.act und nicht Genius.acs.

        Wie geht's

        Comment


        • #5
          Hallo Andreas,

          bei den Konferenz-Demos habe ich <b>Delphi 4</b> verwendet (um nicht
          Delphi 5 zwingend vorauszusetzen), so daß mit Delphi 5 an der
          einen oder anderen Stelle Änderungen notwendig sind. Eine dieser
          Änderungen betrifft das Interface <b>IEnumVARIANT.</b>

          <pre>Delphi 4: function Next(celt: Longint; out elt;
          pceltFetched: PLongint): HResult; stdcall;

          Delphi 5: function Next(celt: LongWord; var rgvar : OleVariant;
          out pceltFetched:LongWord): HResult; stdcall;</pre>

          <p>Wenn ich mit richtig erinnere, habe ich das Beispiel einmal
          mit D5 compiliert (ohne auf eine Fehlermeldung zu stoßen) - ich
          schaue jedoch zur Sicherheit am Wochenende noch einmal nach. Bis
          dahin kann ich nur <b>IEnumVariant_D4</b> als Workaround anbieten,
          dies sollte unter Delphi 5 den Zustand von Delphi 4
          wiederherstellen.</p>

          <p>Die <b>Einstein</b>-Figur habe ich über das Setup-Programm von <em>Microsoft
          Office 2000</em> installiert - somit habe ich keinen Einfluß auf
          den Dateinamen. Kommt bei Dir eventuell eine andere Office-Version
          zum Einsatz? Wenn ja - muß nur der Dateiname angepaßt werden.</p&gt

          Comment


          • #6
            Hallo Andreas,

            ich habe mir die Sache mit Delphi 5 und <b>IEnumVariant</b> nochmals im Detail angeschaut. Zur Vorbereitung der Konferenz-Unterlagen habe ich am 19.09.99 das Beispielprojekt mit der Vorabversion von Delphi 5(FT4) ohne Problem compilieren können. Hier stimmte IEnumVariant noch der D4-Version überein. Mit der "offiziellen" deutschen Version von D5 erhalte ich allerdings auch das Veto des Compilers, da zwei Parametertypen verändert wurden. Damit ist zwar binär noch alles beim alten, aber die strenge Typprüfung von Object Pascal schreitet ein.

            Das für <b>Delphi 5 angepasste Beispielprogramm</b> sieht also nun wie folgt aus:

            <pre>procedure TForm1.GetAnimationList(aSL: TStrings; aAgent: IAgentCtlCharacterEx);
            var
            pEnum : IEnumVARIANT_D4;
            vAnimName : VARIANT ;
            dwRetrieved : DWORD ;
            hRes : HResult;
            begin
            aSL.Clear;
            pEnum := aAgent.AnimationNames.Enum as IEnumVARIANT_D4;
            while (TRUE) do
            begin
            hRes := pEnum.Next(1, vAnimName, @dwRetrieved);
            if hRes <> S_OK then
            Break;
            aSL.add(vAnimName);
            end;
            end;</pre&gt

            Comment


            • #7
              Hallo Andreas!

              Bin gerade dabei, Dein COM-DCOM-Buch durchzuarbeiten,
              für Compound Documents habe ich bereits den ersten "Ernstfall"...

              Hatte ein Problem bezüglich des Dateizugriffes, wenn die Datei schreibgeschützt ist,
              kriege ich mit Deiner Art von Dateizugriff Probleme (STGM_READWRITE),
              die sich beheben lassen, wenn man STGM_READ setzt (siehe Listing unten).
              Spricht das was dagegen, es so zu machen, wenn man nur lesend zugreifen will?

              Jetzt das Problem:
              Wenn Dateien geöffnet sind (bei mir in PowerPoint), versagt auch "meine" Zugriffsart (STGM_READ).

              Was kann man tun?

              Vorab besten Dank,

              Gruß

              Christoph

              //Kapitel 14, StructuredStorage - Compound Document Properties
              unit OSDocSum;

              function TOSDocSummary.GetSummaryInfo(sFileName: String): Boolean;
              ...

              begin

              ...

              // ORIGINALAndreas Kosch, ergibt Probleme bei schreibgeschützten Dateien
              { OleCheck(StgOpenStorage(PWideChar(swFileName), nil, STGM_READWRITE or
              STGM_DIRECT or STGM_SHARE_EXCLUSIVE, nil, 0, aRoot)); }

              // Nur mit STGM_READ als erstem Parameterteil für Parameter "grfMode" läßt sich
              // das Problem umschiffen
              OleCheck(StgOpenStorage(PWideChar(swFileName), nil, STGM_READ or
              STGM_DIRECT or STGM_SHARE_EXCLUSIVE, nil, 0, aRoot));
              ..

              Comment


              • #8
                Hallo,

                zugegeben, das Beispielprogramm fordert vom Betriebssystem mehr Dinge an, als in der Tat benötigt werden. Als "Entschuldigung" kann ich nur vorbringen, das in der Anwendung, aus der das Beispiel herausgelöst wurde, auch die <i>Compound Document Properties</i> neue geschrieben bzw. aktualisiert werden.

                In der Hilfe zum <b>Microsoft Platform SDK</b> sind zur API-Funktion <b>StgOpenStorage</b> sehr umfangreiche Bemerkungen zu finden, in welcher Situation der <i>grfMode</i>-Parameter wie eingesetzt werden sollte (oder darf). Für das Beispielprogramm (nutzt den <b>Direct Mode</b>)sind die folgenden Alternativen zulässig:

                - STGM_DIRECT or STGM_READWRITE or STGM_SHARE_EXCLUSIVE <br>
                - STGM_DIRECT or STGM_READ or STGM_SHARE_DENY_WRITE <br>
                - STGM_DIRECT or STGM_READ or STGM_SHARE_EXCLUSIVE <br>

                Somit könnte "STGM_DIRECT or STGM_READ or STGM_SHARE_DENY_WRITE" das Problem lösen, allerdings mit Nebenwirkungen, die von Microsoft wie folgt beschrieben werden: <br>
                <i> Note: Opening a storage object in read and/or write mode without denying writer permission to others (the grfMode parameter specifies STGM_SHARE_DENY_WRITE) can be a time-consuming operation since the StgOpenStorage call must make a snapshot of the entire storage object. </i&gt

                Comment


                • #9
                  Hallo Andreas!

                  Besten Dank für Deine prompte Antwort auf meine Frage, klappt prima so.
                  Wenn ich allerdings einen ganzen Verzeichnisbaum rekursiv durchsuche,
                  wird's wohl Probleme geben, aber ich kann ja die schnelle Variante
                  (STGM_READWRITE or STGM_DIRECT or STGM_SHARE_EXCLUSIVE)
                  über eine Exception abfangen und bei Bedarf (Exception) über den Parameter
                  STGM_DIRECT or STGM_READ or STGM_SHARE_DENY_WRITE "nachhaken".

                  Gruß

                  Christop

                  Comment


                  • #10
                    Hallo Andreas!
                    Nochmals eine Rückfrage bezüglich der CompoundDocuments:
                    Ich würde gern die Properties schreiben. In DeinemBuch steht zwar,
                    dass man das mit IPropertyStorage.WriteMultiple realisieren kann,
                    ich bin allerdings nicht weitergekommen.

                    Da Du in Deiner Antwort auf meine letzte Frage geschrieben hast, daß
                    der Code im Buch aus einem programm stammt, in dem Properties
                    geschrieben wurden, gehe ich davon aus, daß Du das Problem schon
                    gelöst hast.

                    Gruß Christop

                    Comment


                    • #11
                      Hallo Andreas!

                      Ich haette da eine Frage bezueglich des verschickens (streamings) von Komponenten ueber com/dcom.
                      Folgendes: Ich habe eine Komponente (abgeleitet von TComponent) die als Container arbeitet. Dann habe ich weitere Komponenten z.b. TGeschaeft, TGeschaeftdetail usw. diese sind von einer (zum Teil abstrakten) Komponente: TBusiness=class(TComponent) abgeleitet. Somit ergibt sich folgende Struktur:
                      TContainer=class(TComponent);
                      TBusiness=class(TComponent);
                      TGeschaeft=class(TBusiness);
                      TGeschaeftdetail=class(TBusiness);
                      Es wird immer ein TContainer Objekt verschickt. In diesem TContainer Objekt sind mehrere TGeschaefte als Komponenten enthalten.
                      In einem TGeschaeft Objekt sind mehrere TGeschaeftDetails enthalten (wieder als Komponenten). Die Owner Verhaeltnisse werden durch das z.b. TGeschaeftDetail.Create(Geschaeft) erzeugt. Bei all diesen Komponenten habe ich die Funktion GetChildren(..) ueberschrieben.
                      Nun zum Problem: Wenn ich mit Ihren Funktionen aus dem Buch ComponentToVariant u. VariantToComponent einen solchen Container umwandle wird immer nur die erste Ebene (also die TGeschaefte rueckgewandelt) nicht aber die TGeschaeftDetails als Children von TGeschaeft. Inzwischen habe ich auch probiert die GetChildOwner funktionen zu ueberschreiben, ohne Erfolg.
                      Vielleicht koenne Sie mir einen Tip geben wo das Problem liegt - ich waere Ihnen sehr dankbar.

                      mfg
                      Werner Stotte

                      Comment


                      • #12
                        zuerst moechte ich mich fuer meine formatierung entschuldigen.<br>
                        ich konnte aber das problem inzwischen loesen. es war eine unguenstige kombination aus getchildren und getchildowner.<br>
                        trozdem danke<br>
                        mfg<br>
                        werner stotte

                        Comment


                        • #13
                          Sehr geehrter Herr Kosch,

                          Ihr Buch hat mein Verständnis für COM ein gutes Stück weitergebracht. Allerdings kann ich noch nicht behaupten, daß ich alles verstanden habe. Deshalb auch meine Frage: In ihrem Buch in Kapitel 12.4/"Ein einfaches Beispiel für Delphi 4" wird ein Event nur ausgelöst, wenn der Client die Methode "GetCounter" aufruft. Aber was muß ich anstellen damit der Server zu irgendeinem Zeitpunkt, ohne das der Client es weiß, ein Event auslöst und der Client es mitbekommt. Versuche mit dem Beispiel haben ergeben, daß es nicht ausreicht, einfach die Methode "GetCounter" innerhalb des Servers aufzurufen.

                          Danke im Voraus
                          Andreas Deiman

                          Comment


                          • #14
                            Hallo,

                            im Beispiel aus Kapitel 12.4 wird eine <b>Connection Point</b>-Verbindung verwendet, um vom Server-Objekt heraus den Client zu benachrichtigen. Allerdings wird nicht der Client direkt, sondern das vom Client implementierte <b>Sink-Objekt</b> vom Server aufgerufen. Der Server ist in diesem Fall der "Client" vom "Server" Sink-Objekt (siehe Grafik auf Seite 235).

                            Um die Benachrichtigung auszulösen, muss der Server nur den Aufruf <b>if FEvents <> nil then FEvents.OnGetNumber</b> auslösen, wobei <i>OnGetNumber</i> die Interface-Methode des Sink-Objekts (Client) ist. Wenn bei Ihnen der Client nicht benachrichtigt wird, würde ich zuerst auf der Server-Seite nachprüfen, ob <i>FEvents</i> tatsächlich einen Interface-Zeiger auf das Sink-Objekt enhält. Wenn nicht, hat der Client keine Connection Point-Verbindung zum Server aufgebaut - er wird in diesem Fall auch niemals informiert. Aus diesem Grund stellte das Kapitel 12 die beiden Alternativen <b>Callback</b> vs. <b>Connection Point</b> auch kritisch gegenüber.

                            Im Beispiel aus dem Buch ist der folgende Aufruf für den Verbindungsaufbau des Connection Points zuständig:
                            <pre>
                            procedure TFormClient.EventsOn;
                            begin
                            FCP3EventSink := TEventSink.Create(OnGetNumber);
                            InterfaceConnect(FCOMSvr, IConPointSrv3Events, FCP3EventSink, FCookie);
                            end;
                            </pre>

                            Wenn der Server in jedem Fall (auch wenn der Client noch nicht aktiv ist) den Client informieren soll, stehen ab Windows 2000 die neuen <b>COM+ Events</b> zur Verfügung. Da beim neuen Verfahren keine Connection Points beteiligt sind, entfallen dort alle diese Beschränkungen

                            Comment


                            • #15
                              Sehr geehrter H.Kosch,

                              vielen Dank für die Ihre Antwort. Wahrscheinlich habe ich mich nicht richtig ausgedrückt. Aber Ihre Antwort hat mich nocheinmal zum Nachdenken gebracht und ich habe dann eine funtionierende Lösung gefunden.

                              Hier nochmal vereinfacht mein Problem:

                              Ich starte mit dem Client einen "Out of Process"-Server. Dieser Server hat ein Formular mit einer Timer -Komponente. Dieser Timer soll nun alle 10sek. ein Event auslösen.

                              Ich habe nun, damit in meinem Formular die Variable "FEvents" bekannt ist, einen globalen Zeiger vom gleichen Typ wie "FEvents" deklariert. Diesem Zeiger habe ich "FEvents" zugewiesen. Nun kann ich in meiner "OnTimer-Funktion" die Zeilen

                              if xy <> nil then
                              xy.onXyEvent;

                              benutzen.

                              In meinem Client benutze ich die Komponente aus ihrem Buch, die mit dem Sink-Generator erzeugt wurde. Die Methode "connect" dieser Komponente führe ich in der "onCreate-Funktion" des Client aus. Die Methode "disconnect" führe ich in "onCloseQuery" des Client aus.

                              Meine Unsicherheit dabei ist, habe ich trotz korrekter Funktion etwas übersehen ?

                              mfg.
                              Andreas Deiman

                              Comment

                              Working...
                              X