Announcement

Collapse
No announcement yet.

CriticalSection-Problem

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

  • CriticalSection-Problem

    Hallo,

    ich fange in meinem Programm einige vom COM-Server implementierte Ereignisse auf. Die Ereignisse werden richtig ausgeführt, sobald ich aber auf VCL zugreifen will, bleibt das ganze Programm stehen. TCriticalSection habe ich definiert und in jedem Event (Beim OnStrart funktioniert noch), wird FCriticalSection.Enter und Leave ausgeführt. Trozdem komme ich nicht weiter.

    Danke

  • #2
    Hallo,

    ich gehe einmal davon aus, das Delphi 5 verwendet wird und der eigene COM-Server als Komponente im Formular plaziert wurde. Für das Server-Ereignis wurde dann im Objektinspektor eine Ereignisbehandlungsmethode angelegt. Wie sieht die Implementierung in dieser Methode aus? Werden separate Threads verwendet? Werden über SendMessage Botschaften verschickt? Wird ein <b>modaler Dialog</b> oder eine MessageBox etc. aufgerufen?

    Falls ja, muss die Anwendung geändert werden. Wird ein modaler Dialog in einem ungünstigen Moment aufgerufen (d.h. bei einem aktiven Sink-Objekt-Aufruf), bleibt der Server-Thread, der gerade im Callback-Aufruf ist, solange hängen, bis der Dialog wieder geschlossen wird.
    Um dieses Problem zu umgehen, muß das Sink-Objekt völlig vom primären Thread (STA0) der Anwendung getrennt werden, indem auch das Sink-Objekt in einem eigenem Thread (und damit in einem eigenem STA) ausgeführt wird.

    P.S. Was passiert, wenn alle Critical Section-Aufrufe auskommentiert werden?
    &#10

    Comment


    • #3
      Erstens danke für die Antwort!

      Es handelt sich um DTS Automatisierung von SQL Server 7.0. Die Package wird richtig gestartet, am Anfang beim OnStart kann ich noch sogar ein ShowMessage anzeigen. Beim OnProgress kann ich nicths mehr machen, das Programm bleibt nicht nur bei ShowMessage stehen, sondern auch bei Memo.Lines.Add, ProgressBar usw. Wenn ich eben die VCL benutzen will. Aus dem Grund habe ich das CriticalSection eingebunden, hilft leider nichts. Wenn ich alles auskommentiere (Memo.Lines.Add,ProgressBar) funktioniert alles richtig. Es ist bestimmt ein Thread-Problem.

      Dank

      Comment


      • #4
        Hallo,

        wie sieht ein Minimal-Beispiel aus, mit dem das Problem reproduziert werden kann

        Comment


        • #5
          Hallo,

          ich habe inzwischen eine halbe Lösung gefunden, die mir überhaupt nicht gefällt. Man kann bei dem DTS-Package einstellen, dass die Steps in der Mainthread ausgeführt werden sollen. Diese Variante ist aber wirklich nur eine VB Lösung.
          <p>Die DTS-Package von Microsoft ist free-threaded</p>
          Kurz die DTS.TLB
          <p>IDTSStdObject = interface(IDispatch)</p>
          <p>_Package = interface(IDTSStdObject)</p>
          Delphi Wrapper
          TPackage = class(TOleServer)

          <p>
          Wie könnte ich dieses Package als Multiinstance implementieren? Delphi 5 erstellt immer die Wrapper-Klasse wo ich TPackage = class(TOleServer) habe, kann ich von dem etwas ableiten, oder muß ich alles mit TAutoObjectFactory.Create(...ciMultiInstance) machen
          und TDTSPack = class(TAutoObject,_Package) und alle Prozeduren neu implementieren. Nachdem in einem Package die Transaktionen in Threads ausgeführt werden, so werden mehrere (eingstellt 4) Transaktionen auf einmal gestartet. Muß ich dann an meinen Event-Interface auch was ändern?
          Ich hoffe, daß ich halbwegs verständlich mein Problem geschildert habe. Ich würde mich für eine kleine Hilfe sehr bedanken.
          </p>

          Danke
          Balazs
          <p>
          ps. Was ist das unterschied zwischen TComObjectFactory und TAutoObjectFactory?
          </p&gt

          Comment


          • #6
            Hallo,

            das <b>DTSPackage object</b> (Data Transformation Services Package Object) wird mit dem Threading-Modell <b>Both</b> installiert. Somit kann dieses Objekt entweder im STA oder im MTA ausgeführt werden. Für die Zustellung der Events ruft dieses Objekt das <b>Sink</b>-Objekt im Client (also der Delphi-Anwendung) auf. Je nach dem aktuellen Apartment des DTSPackage-Objekts muss COM spezielle Anpassungen vornehmen, um die beiden beteiligten Apartments anzupassen.

            Wenn die von Delphi generierte VCL-Wrapperkomponente im Formular plaziert wird, wird das Sink-Objekt bei der Standardkonfiguration im STA0 (<i>Single Threaded Apartment</i> des primären Thread)
            ausgeführt. Wenn nun dieses Sink-Objekt von einem DTSPackage-Objekt aufgerufen wird, das in einem MTA (Multi Threaded Apartment) "lebt", muss COM über das Proxy-Objekt die Kompatibilität herstellen. Dies
            führt dazu, das der Aufruf als Windows-Botschaft einem versteckten Hilfsfenster zugeschickt wird. Und somit besteht die Gefahr von blockierenden Anwendungen, wenn die beteiligten Botschaftswarteschlangen nicht mehr regelmässig aufgerufen werden können. Daher war meine erste Frage (siehe 01.03.) auch nach einem modalen Dialog. Um nun zu prüfen, ob es an der Botschaftswarteschlange liegt, würde ich in den
            Ereignisbehandlungsmethoden für das Event nur eine private Botschaft über <b>PostMessage</b> an das eigene Hauptformular schicken. Da PostMessage nur eine Botschaft in die Warteschlange ablegt, kann die Event-Methode sofort zurückkehren, ohne das VCL-Aufrufe im primären Thread stattfinden.

            Wenn das erfolgreich ist, gibt es 2 Möglichkeiten: <br>

            1. Das eigene Sink-Objekt muss in einem MTA ausgeführt werden (vorher aber das Kleingedruckte genau durchlesen):

            <pre>
            begin
            {$IFDEF MultiThreadedServer}
            IsMultiThread:= True;
            CoInitFlags := COINIT_MULTITHREADED;
            {$ENDIF}
            Application.Initialize;
            Application.CreateForm(TFormMain2, FormMain2);
            Application.Run;
            end.
            </pre>

            2. Das eigene Sink-Objekt darf nicht im STA0 des primären Threads (VCL) laufen, sondern muss in eigenem eigenen STA ausgeführt werden. Das folgende Beispiel spaltet zu Beispiel einen separaten Thread ab und führt dort eine Instanz von TWordApplication (dem VCL-Wrapper für das Word-Application-Objekt) aus:

            <pre>
            type
            TForm1 = class(TForm)
            ListBox1: TListBox;
            Button1: TButton;
            procedure Button1Click(Sender: TObject);
            end;

            TWrdObjThread = class(TThread)
            private
            FText : String;
            procedure UpdateFrm;
            procedure WrdAppQuit(Sender: TObject);
            protected
            procedure Execute; override;
            public
            constructor Create(Text: String);
            end;

            var
            Form1: TForm1;

            implementation

            {$R *.DFM}

            uses ComObj, ActiveX;

            constructor TWrdObjThread.Create(Text: String);
            begin
            inherited Create(False);
            FreeOnTerminate := True;
            FText := Text;
            end;

            procedure TWrdObjThread.Execute;
            var
            aThrdWordApp : TWordApplication;
            vSaveChanges : OleVariant;
            begin
            OleCheck(CoInitialize(nil));
            try
            aThrdWordApp := TWordApplication.Create(nil);
            try
            aThrdWordApp.OnQuit := WrdAppQuit;
            aThrdWordApp.Connect;
            FText := aThrdWordApp.Version;
            Synchronize(UpdateFrm);
            aThrdWordApp.Quit(vSaveChanges);
            aThrdWordApp.Disconnect;
            finally
            aThrdWordApp.Free;
            end;
            finally
            CoUninitialize;
            end;
            end;

            procedure TWrdObjThread.WrdAppQuit(Sender: TObject);
            begin
            Form1.ListBox1.Items.Add('WordApplication: Quit');
            end;

            procedure TWrdObjThread.UpdateFrm;
            begin
            Form1.ListBox1.Items.Add('Testeintrag aus dem Thread: ' + FText);
            end;

            procedure TForm1.Button1Click(Sender: TObject);
            begin
            TWrdObjThread.Create('Test');
            end;
            </pre&gt

            Comment


            • #7
              Hallo Herr Kosch,
              <p>
              danke für die ausführliche Beschreibung.
              Ich habe wahrscheinlich vergessen zu schreiben, dass die Delphi erstellte DTS-Komponent nicht richtig funktioniert. (Bei mir!)
              Wenn ich das Package ausführen (aPack.execute) will, dann erhalte ich immer eine Exception-Access-Violation,
              deshalb muß ich mit CoPackage.create arbeiten. Ich weiß leider nicht, wieso immer diese Fehlermeldung
              kommt. Jetzt habe ich es auch in dem Thread ausprobiert, die Fehlermeldung erhalte ich weiterhin.
              </p>
              Bisher habe ich mit CoPackage und DTS-Event Komponent gearbeitet.
              <p>
              ...<BR>
              procedure TDTSThread.Execute;<BR>
              var aPack:_Package;<BR>
              &nbsp; aPunk:OleVariant;<BR>
              &nbsp; aPackEvent:TPackageEvents;<BR>
              begin<BR>
              &nbsp; OleCheck(CoInitialize(nil));<BR>
              &nbsp; try<BR>
              &nbsp; aPack:=CoPackage.Create;<BR>
              &nbsp; //aPack:=TPackage.Create(nil);<BR>
              &nbsp; aPackEvent:=TPackageEvents.Create(nil);<BR>
              &nbsp; try<BR>
              &nbsp; //aPack.OnFinish:=DtsPackFinish;<BR>
              &nbsp; aPack.LoadFromSQLServer(cServername,cServerPasswor d,'',DTSSQLStgFlag_Default,'','','',cPackageName,a punk);<BR>
              &nbsp; aPackEvent.Connect(aPack as IUnknown);<BR>
              &nbsp; aPackEvent.OnFinish:=DtsPackFinish;<BR>

              &nbsp; aPack.Execute;<BR>
              &nbsp; Synchronize(WorkDone);<BR>
              &nbsp; finally<BR>
              &nbsp; end;<BR>
              &nbsp; finally<BR>
              &nbsp; CoUninitialize;<BR>
              &nbsp; end;<BR>
              end;<BR>
              </p>
              <p>
              procedure TDTSThread.WorkDone;<BR>
              begin<BR>
              &nbsp; Form1.ListBox1.Items.Add(FText);<BR>
              end;<BR>
              </p>
              <p>
              function TDTSThread.DtsPackFinish(Sender: TObject;<BR>
              const EventSource: WideString):HRESULT;<BR>
              begin<BR>
              &nbsp; Form1.ListBox1.Items.Add('FERTIG');<BR>
              &nbsp; MakeResult(0, FACILITY_NULL, S_OK);<BR>
              end<BR>
              </p>

              <p>
              Diese Variante läuft schon besser, nachdem der Package aber eine neue STEP ausführen will bricht das Programm mit der folgende Fehlermeldung ab
              OleException: Ausführung wurde von Benutzer abgrebrochen.
              Dieselbe Fehlermeldung erhalte ich auch in Visula Basic.

              Ohne Thread habe ich auch die PostMessage in einer Ereginisbehandlung ausprobiert, das Programm ist trotzdem angehalten.
              </p>

              mfg und danke<BR>
              Balaz

              Comment


              • #8
                Wenn in der Ereignisbehandlungsmethode nur eine private PostMessage-Befehl steht, erhalte ich genauso die Fehlermeldung:
                <BR>
                OleException: Ausführung wurde von Benutzer abgrebroche

                Comment


                • #9
                  Hallo,

                  nicht ohne Grund habe ich in meinem Beispiel die TWordApplication-Komponente verwendet ;-) <br>
                  Wenn dieser Fehler auch in Visual Basic auftritt, scheint die Macke im DTSPackage-Objekt zu liegen bzw. man hält sich nicht an die von MS vorgeschriebenen Aufruf-Umgebungsbedingungen (sollte in der Dokumentation zu DTSPackage zu finden sein. Die Umfangreichen Abhandlungen zum <i>Package Objects (DTS)</i> aus dem <i>SQL Server Online-Handbuch</i> habe ich noch nicht gelesen - da ich dieses Objekt bisher noch nicht eingesetzt habe.

                  Solange diese Fehler auftreten, macht es keinen Sinn, weiter mit Threads und Apartments zu experimentieren.

                  Ich würde zuerst versuchen, das Event <b>OnError</b> auszuwerten, um genauere Angaben über die Gründe des DTSPackage-Vetos zu erhalten. Ausserdem kann man dort festlegen, ob die Ausführung abgebrochen werden soll oder nicht:

                  <i>Use the Cancel argument to direct package execution on error. When Cancel is True (default), execution of the package is terminated on return from the application error handler. When Cancel is False, DTS attempts to execute the next task indicated.</i&gt

                  Comment

                  Working...
                  X