Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Programm hält beim Start einfach an (https://www.delphipraxis.net/137151-programm-haelt-beim-start-einfach.html)

BAMatze 15. Jul 2009 08:20


Programm hält beim Start einfach an
 
Hallo und guten Tag an alle DP´ler,

Also ich hab mal wieder ein Problem und zwar eins wo ich nicht ansatzweise weiß, wo ich ansetzen soll. Leider gibt es deswegen auch nur eine so schlechte Problembeschreibung, wie sie im Threattietel steht.
Also ich beschreibe einfach mal, was mein Programm macht: Nach dem Start wird ein Splash-Screen gezeigt, welches Meldungen über den Ladestatus des Programms zeigt. Im Hintergrund werden Perepherie-Geräte geladen (DLL + ComPort-Kommunikationsaufbau + Initialisierung). Wenn alle Geräte Initialisiert sind, verschwindet der Splash-Screen und das Hauptfenster zur Steuerung des Programmes erscheint.
Jetzt zum Fehler. Es passiert (unregelmäßig), dass das Programm irgendwo wärend der Splashscreen gezeigt wird, einfach stehen bleibt und nichts mehr ausführt. Allerdings wenn ich es nach Abbruch nochmal starte, läuft er einfach durch, obwohl nichts am Code oder sonstwas geändert wurde.
Jetzt zu meiner direkten Frage: Wieso kann es sein, dass ein Fehler sporadisch auftritt? Habe ich vieleicht ein Speicherproblem (Könnte das eventuell daran liegen, dass ich irgendwo vergessen hab etwas wieder frei zu geben?? (Man kann ja auch mal was übersehen)) Und wenn das der Fall ist, kann man das irgendwie testen oder sonstwie herausfinden?

Falls jemand ein solches Verhalten kennt, würde ich mich sehr über eure Hinweise freuen

Vielen Dank
BAMatze

Uwe Raabe 15. Jul 2009 08:24

Re: Programm hält beim Start einfach an
 
Ich vermute (No Source - No Clue) das Problem in der Formulierung "Im Hintergrund". Breite das hier doch mal etwas mehr aus.

GHorn 15. Jul 2009 09:06

Re: Programm hält beim Start einfach an
 
...(DLL + ComPort-Kommunikationsaufbau + Initialisierung)...
Wartest Du dabei auf eine Antwort, z.B. des COM-Ports, die aber nicht kommt?
Bau doch mal ein TimeOut mit Fehlermeldung ein.

Gruß,
Gerald

BAMatze 15. Jul 2009 09:19

Re: Programm hält beim Start einfach an
 
@Uwe
Also zu deiner Frage, ich habe das wie folgt programmiert(die meisten Quellcodeteile stammen aus der DP-Suche und aus eigenen Threats hier).

Delphi-Quellcode:
var
  BedienForm: TBedienForm;
  Protokoll: TProtokoll;
  Fehler: TFehler;
  V_Tische: TV_Tische;
  P_Tisch: TP_Tisch;
  ControlerBoard: TControlerBoard;
  bP_Tisch, bV_Tische, bDatenbank_verfügbar: boolean;
 

implementation
uses LoaderUnit;


procedure TBedienForm.FormCreate(Sender: TObject);
begin
  BedienForm.Enabled := false; // Die Sichtbarkeit der Bedienform ist in der Projektdatei ausgeschalten
  // Splashscreen öffnen
  LoaderForm := TLoaderForm.Create(Application);
  LoaderForm.Show;
  LoaderForm.Refresh;
end;

procedure TBedienForm.FormShow(Sender: TObject);
begin
  Protokoll := TProtokoll.create;
  Fehler := TFehler.create;
  // ControlerBoard K8055
  ControlerBoard := TControlerBoard.create;
  ControlerBoard.Fehlerevent_ausloesen := Fehler_verifizieren;
  ControlerBoard.Initialising;
  // Es wird eine Pause eingelegt, damit auch wirklich Spannung an den Ausgängen
  // des ControlerBoards und somit an den Controlern für die Tische und so weiter
  // anliegt. Es dauert zwar theoretisch nur Bruchteile von Sekunden, aber da
  // noch einiges an Schaltunfen da hinter liegt, soll der Spannung ausreichend Zeit
  // gegeben werden.
  sleep(1000);
  // Verschiebetisch 200mm
  V_Tische := TV_Tische.create;
  V_Tische.Fehlerevent_ausloesen := Fehler_verifizieren;
  bV_Tische := V_Tische.Initialising;
  // Piezoverschiebetisch 100µm
  P_Tisch := TP_Tisch.create;
  P_Tisch.Fehlerevent_ausloesen := Fehler_verifizieren;
  bP_Tisch := P_Tisch.Initialising;

  sleep(1000);
  // Splashscreen schließen
  LoaderForm.Close;
  LoaderForm.Free;
  BedienForm.Enabled := true;
end;

procedure TBedienForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  V_Tische.Free;
  P_Tisch.Free;
  ControlerBoard.Free;
  Fehler.Free;
  Protokoll.Free;
end;
So lasse ich das in der Mainform (TBedienForm) erstmal initialisieren. Im folgenden zeige ich mal die Konstruktoren und Initalisierungen Beispielhaft an einem Perepherie-Gerät (die anderen sind in gleicher Art und Weise aufgebaut):

Delphi-Quellcode:
constructor TControlerBoard.create;
var iIndex: integer;
begin
  //Protokoll.Protokolleingang('ControlerBoard', 'Create');
  digEingaenge_pruefen := TTimer.Create(Nil);
  digEingaenge_pruefen.OnTimer := fdigEingaenge_pruefen;
  digEingaenge_pruefen.Enabled := true;
  digEingaenge_pruefen.Interval := 30;
  for iIndex := 1 to 2 do Diodenhelligkeit[iIndex] := 150;
  bKamerabeleuchtung_angeschaltet := false;
end;

function TControlerBoard.DLLHandle_zuweisen: boolean;
begin
  //Protokoll.Protokolleingang('ControlerBoard', 'DLLHandle-Fkt');
  Fehlermeldung(300);
  try
    ControlerDLL := TDLL_Datei.create('K8055D.DLL');
    if ControlerDLL.Vorhanden then
      begin
        DLL_Handle := ControlerDLL.Handle;
        result := true;
      end
    else
      begin
        Fehlermeldung(301);
        DLL_Handle := 0;
        result := false;
      end
  except
    Fehlermeldung(302);
    result := false;
  end;
  //Protokoll.Protokollausgang('ControlerBoard', 'DLLHandle-Fkt');
end;

function TControlerBoard.DLLFunktionen_laden: boolean;
begin
  //Protokoll.Protokolleingang('ControlerBoard', 'DLL-Fkt laden');
  try
    if DLL_Handle <> 0 then
      begin
        // Es wurde festgestellt, dass nicht alle Funktionen, die in der DLL vorhanden
        // sind wirklich für die Umsetzung des Programmes benötigt werden. Deswegen
        // werden hier nur die wirklich verwendeten Funktionen eingebunden, um auch die
        // Resourcen für das Programm zu beschränken
        @OpenDevice := GetProcAddress(DLL_Handle, 'OpenDevice');
        @CloseDevice := GetProcAddress(DLL_Handle, 'CloseDevice');
        @ClearAllAnalog := GetProcAddress(DLL_Handle, 'ClearAllAnalog');
        @ClearAllDigital := GetProcAddress(DLL_Handle, 'ClearAllDigital');
        @ClearAnalogChannel := GetProcAddress(DLL_Handle, 'ClearAnalogChannel');
        @ClearDigitalChannel := GetProcAddress(DLL_Handle, 'ClearDigitalChannel');
        @OutputAnalogChannel := GetProcAddress(DLL_Handle, 'OutputAnalogChannel');
        @SetDigitalChannel := GetProcAddress(DLL_Handle, 'SetDigitalChannel');
        @ReadDigitalChannel := GetProcAddress(DLL_Handle, 'ReadDigitalChannel');
        result := true;
      end
    else
      begin
        Fehlermeldung(303);
        result := false;
      end
  except
    Fehlermeldung(304);
    result := false;
  end;
  //Protokoll.Protokollausgang('ControlerBoard', 'DLL-Fkt laden');
end;

function TControlerBoard.Initialising:boolean;
begin
  try
    if (DLLHandle_zuweisen = true) and (DLLFunktionen_laden = true) then
      begin
        OpenDevice(0);
        ClearAllDigital;
        // Das ControlerBoard schaltet die Stromversorgung für die Tische, das
        // Spleißgerät und die Kamera ein. Deswegen muss es in der BedienForm als
        // erstes gestartet werden. Wenn es initialisiert wurde, müssen alle
        // digitalen und analogen Ausgänge eingeschaltet werden, damit eine
        // erfolgreiche Initialisierung der Tische überhaupt möglich ist.
        // Ich werde hier aufgrund der einfacheren Wartung für mögliche Nachfolger
        // direkte Funktionen zur Verfügung stellen, die genau die richtigen dig.
        // und analogen Ausgänge einschaltet. Damit muss sich später keiner mehr
        // gedanken machen, welcher Ausgang wohin gehört (Anhalt ist auch die Liste
        // im Kopf dieser Unit) und brauch gegebenenfalls nur die Funktionen an der
        // gewünschten Stelle aufrufen.

        {Liste der einzuschaltenen Geräte per Funktion hier einfügen}
        Stromversorgung_Verschiebetische_einschalten;
        Spleissgeraet_einschalten;
        Kamerabeleuchtung_einschalten;
        // Hier wird ein neues Konzept benötigt, welches die Gerätebeleuchtung rot
        // belässt und nur grün anzeigt, wenn das Gerät einsatzbereit ist.
        Geraetebeleuchtung_schalten(green);
        result := true;
      end
    else
      begin
        Fehlermeldung(305);
        result := false;
      end
  except
    Fehlermeldung(306);
    result := false;
  end;
end;

Destructor TControlerboard.Destroy;
begin
  {Liste der auszuschaltenen Geräte per Funktion hier einfügen}
  alle_anaAusgeange_ausschalten;
  alle_digAusgang_ausschalten;
  digEingaenge_pruefen.Free;
  //Protokoll.Protokollausgang('ControlerBoard', 'Destroy');
end;
Ich hoffe das hilft euch, mir zu helfen. Ähm ich weiß die Verwendung der try/except-Blöcke ist wohl nicht ganz konform den Regeln und sind so eventuell auch nicht wirklich nötig, wurde ich schonmal drauf hingewiesen, konnte das nur noch nicht umsetzen. Weiterhin der Hinweiß, dass ich kein Fehlerhaftes Hochfahren bisher registriert habe, wenn ich ohne die Perepheriegeräte das Programm lade. Allerdings wird dann auch nur Teile der einzelnen Klassen ausgeführt. Ich weiß, ich muss deswegen sicherlich eine genaue Prüfung aller Klassen nochmal durchführen, hoffe ihr könnt mir aber eventuell helfen und schauen, ob irgendwo ein systematischer Fehler vorliegt.

@GHorn
Also bei den ComPorts bestimme ich über folgender Funktion hier aus der DP welche belegt sind:
Delphi-Quellcode:
function TComport.ComPort(ComPortNummer: byte): longbool;
var TestHandle : integer;
begin TestHandle :=CreateFile(PChar('\\.\COM'+IntToStr(ComPortNummer)),GENERIC_READ or GENERIC_WRITE,0,
                              nil,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,LongInt(0));
  if (TestHandle <= 0) then Result := false
  else begin Result := true; CloseHandle(TestHandle); end;
end;
// Funktion, die alle ComPorts bis zu einer vom Programmierer festgelegten maximal
// ComPortnummer anspricht.
function TComport.ComportScan: boolean;
var i: integer;
    aBuffer: array of Integer;
begin
  SetLength(aComport,0);
  for i := 3 to 100 do
    begin
      if ComPort(i) = true then
        begin
          if Length(aComport) = 0 then
            begin
              SetLength(aComport,1);
              aComport[0]:= i;
            end
          else
            begin
              SetLength(aBuffer,Length(aComport));
              ArrayInhaltinNeuesArray(aComport,aBuffer);
              SetLength(aComport,Length(aBuffer)+1);
              ArrayInhaltinNeuesArray(aBuffer,aComport);
              aComport[Length(aComport)-1] := i;
            end;
        end;
    end;
  if Length(aComport) = 0 then result := false
  else result := true;
end;
Mit dieser Liste teste ich dann, indem ich einen Befehl an das angeschlossene Gerät schicke und prüfe ob es eine Reaktion gibt (ist ein spezifischer Befehl für jedes einzelne der Perepheriegeräte). Wenn ich eine Reaktion bekomme, passte der Befehl zu dem Gerät und der Comport wird diesem Gerät zugeordnet. Wenn nicht, werden die restlichen Comports getestet. Dies funktioniert eigentlich sehr gut, hab in mehreren Tests immer gut detektieren können, welches Gerät angeschlossen ist und welches nicht.

Vielen Dank
BAMatze

Ps.: Wie schon erwähnt, der Fehler tritt irgendwie nur sporadisch auf.

GHorn 15. Jul 2009 11:49

Re: Programm hält beim Start einfach an
 
Auf Anhieb fällt mir da nichts verdächtiges auf.
Nehme an, dass das im Debugger nicht nachzustellen ist.
Um die Ursache einzukreisen würde ich einfach Text
in eine Datei schreiben, während die Aktionen
ausgeführt werden. Z.B. Zahlen hochzählen. Wenn das
Prog dann stehen bleibt, erkennst Du an Hand der eindeutigen
Zahl, welcher Schritt zuletzt ausgeführt wurde.
Ist das Problem beseitigt, das Protokollieren wieder
rausschmeissen. Nicht schön - aber manchmal hilfreich als
erster Ansatz.

Blup 15. Jul 2009 12:03

Re: Programm hält beim Start einfach an
 
Da schon eine Protokollierung vorgesehen ist, würde ich diese nutzen, um den Fehler weiter einzugrenzen.
Die Protokollierung würde ich auch nicht auskommentieren, sondern abhängig von einem Programmparameter oder einer Einstellung die Protokollklasse steuern.

BAMatze 15. Jul 2009 12:31

Re: Programm hält beim Start einfach an
 
Ja hab ich mir auch schon gedacht mit der Protokollklasse. Bin derzeit mit dem Umbau beschäfftigt, würde mich aber weiterhin über anregungen freuen.

BAMatze 15. Jul 2009 14:42

Re: Programm hält beim Start einfach an
 
Hallo habe das jetzt mit Debugger und Protokoll getestet und er bleibt immer in der FromClose-Prozedure des Splash hängen. Allerdings wird dort, wie man anhand des Quellcodes sieht nicht viel gemacht.

Delphi-Quellcode:
procedure TLoaderForm.FormClose(Sender: TObject; var Action: TCloseAction);
var i: Integer;
   
begin
  // Wenn Form geschlossen wird, ausblenden und "Self.Free" machen
  for i := 230 downto 0 do
  begin
    LoaderForm.AlphaBlendValue := i;
    Application.ProcessMessages;
  end;
  Action := caFree;
end;
Und wie gesagt, in dem Meisten Fällen läuft das ganze ja durch.

nahpets 15. Jul 2009 15:01

Re: Programm hält beim Start einfach an
 
Hallo,
Zitat:

Zitat von BAMatze
Hallo habe das jetzt mit Debugger und Protokoll getestet und er bleibt immer in der FromClose-Prozedure des Splash hängen. Allerdings wird dort, wie man anhand des Quellcodes sieht nicht viel gemacht.

Delphi-Quellcode:
procedure TLoaderForm.FormClose(Sender: TObject; var Action: TCloseAction);
var i: Integer;
   
begin
  // Wenn Form geschlossen wird, ausblenden und "Self.Free" machen
  for i := 230 downto 0 do
  begin
    LoaderForm.AlphaBlendValue := i;
    Application.ProcessMessages;
  end;
  Action := caFree;
end;
Und wie gesagt, in dem Meisten Fällen läuft das ganze ja durch.

ändere die Routine bitte dahingehend, dass sie am Anfang und am Ende was loggt, damit Du sehen kannst, ob der Fehler im FormClose passiert oder schon vorher. Beim Debuggen bin ich im Debugger schon ein paarmal direkt hinter dem Fehler gelandet, es könnte also sein, dass einer der letzten Befehle, die vor dem FormClose ausgeführt werden, scheitert.

GHorn 15. Jul 2009 15:10

Re: Programm hält beim Start einfach an
 
Das "Application.ProcessMessages" in der for-Schleife solltest Du raus nehmen.
Wird für Schleifen grundsätzlich nicht empfohlen. Besser ist direkt NACH der
Schleife.

Was machst Du denn in Deinem Splash? Irgenwelche Animationen, Timer o.ä.?
Wenn ja, werden die auch korrekt abgeräumt?

Uwe Raabe 15. Jul 2009 15:30

Re: Programm hält beim Start einfach an
 
Zitat:

Zitat von BAMatze
Hallo habe das jetzt mit Debugger und Protokoll getestet und er bleibt immer in der FromClose-Prozedure des Splash hängen. Allerdings wird dort, wie man anhand des Quellcodes sieht nicht viel gemacht.

Delphi-Quellcode:
procedure TLoaderForm.FormClose(Sender: TObject; var Action: TCloseAction);
var i: Integer;
   
begin
  // Wenn Form geschlossen wird, ausblenden und "Self.Free" machen
  for i := 230 downto 0 do
  begin
    LoaderForm.AlphaBlendValue := i;
    Application.ProcessMessages;
  end;
  Action := caFree;
end;
Und wie gesagt, in dem Meisten Fällen läuft das ganze ja durch.

Ich denke auch, das Application.ProcessMessages ist das Problem. Dabei werden alle anstehenden Messages abgearbeitet bis die Message-Queue leer ist. Da dein Timer im ControlerBoard aber alle 30ms auslöst, kann es sein, daß deine "Ausblenden"-Aktion ziemlich lange dauern wird.

Vorschlag: Nimm im Hauptform einen neuen Timer, der das Splashscreen ausblendet und am Ende freigibt. Statt LoaderForm.Close/Free enablest du nur den Timer. Etwa so:

Delphi-Quellcode:
 
  // Splashscreen schließen
  LoaderFormCloseTimer.Interval := 100; // 100 ms;
  LoaderFormCloseTimer.Tag := 50; // 50 * 100ms = 5 Sekunden
  LoaderFormCloseTimer.Enabled := true;
  BedienForm.Enabled := true;
Und im Timer-Event:

Delphi-Quellcode:
procedure TBedienForm.LoaderFormCloseTimerTimer(Sender: TObject);
begin
  LoaderFormCloseTimer.Tag := LoaderFormCloseTimer.Tag - 1;
  if LoaderFormCloseTimer.Tag > 0 then begin
    LoaderForm.AlphaBlendValue := Round(230*LoaderFormCloseTimer.Tag/50);
  end
  else begin
    LoaderForm.Close;
    LoaderForm.Free;
  end;

end;
Die Schleife im TLoaderForm.FormClose kann dann weg.

Luckie 15. Jul 2009 21:18

Re: Programm hält beim Start einfach an
 
Man kommt auch ohne Timer und Schleife aus, wenn man die API-Funktion MSDN-Library durchsuchenAnimateWindow benutzt.

rollstuhlfahrer 16. Jul 2009 09:00

Re: Programm hält beim Start einfach an
 
Zitat:

Zitat von BAMatze
Delphi-Quellcode:
procedure TLoaderForm.FormClose(Sender: TObject; var Action: TCloseAction);
var i: Integer;
   
begin
  // Wenn Form geschlossen wird, ausblenden und "Self.Free" machen
  for i := 230 downto 0 do
  begin
    LoaderForm.AlphaBlendValue := i;
    Application.ProcessMessages;
  end;
  Action := caFree;
end;

Mich würde da was ganz großes stören, nämlich die Verwendung von exakten Variablennamen (LoaderForm.). Der andere Punkt ist, dass du hier schreibst, dass das Formular durch den aufruf von Self.Free terminiert wird.

An anderer Stelle schreibst du:
Zitat:

Zitat von BAMatze
[delphi]
// Splashscreen schließen
LoaderForm.Close;
LoaderForm.Free;

So, jetzt ist das Objekt doch 2x aus dem Speicher entfernt worden, oder? (hab die Delphi-VCL-Quelltexte noch nicht konsultiert) Aber dennoch müsste hier ein Fehler vorliegen.

Bernhard

BAMatze 16. Jul 2009 16:34

Re: Programm hält beim Start einfach an
 
Hallo an euch alle und vielen Dank für die ganzen Anregungen. Konnte heute leider noch nicht weiter machen mit dem Problem, ist aber morgen früh gleich 2. Tagespunkt. Musste heute mal um Abschlussarbeit und so kümmern und deswegen leider nur unterwegs.
Aber danke euch für die vielen Verbesserungsvorschläge und die Hilfe. Werde morgen posten, ob das Problem gelöst ist.

Vielen Dank an Euch :dp:
BAMatze

BAMatze 17. Jul 2009 12:43

Re: Programm hält beim Start einfach an
 
Also habe jetzt mal die Close-Procedure wie ihr mir geraten habt geändert und das Application.ProcessMessages; aus der Schleife genommen. Funktioniert alles super. Hätte eigentlich nur noch die Frage, warum eigentlich, wenn ich diesen Befehl weglasse das Bild in meiner Form nicht sichtbar ist, sondern erst nach der Schleife eingebunden wird.

Vielen Dank
BAMatze

Uwe Raabe 17. Jul 2009 13:15

Re: Programm hält beim Start einfach an
 
Zitat:

Zitat von BAMatze
Hätte eigentlich nur noch die Frage, warum eigentlich, wenn ich diesen Befehl weglasse das Bild in meiner Form nicht sichtbar ist, sondern erst nach der Schleife eingebunden wird.

Weil die Paint- bzw. Refresh-Message solange noch in der Message-Queue festsitzt?

GHorn 17. Jul 2009 13:33

Re: Programm hält beim Start einfach an
 
Zitat:

Hätte eigentlich nur noch die Frage, warum eigentlich, wenn ich diesen Befehl weglasse das Bild in meiner Form nicht sichtbar ist, sondern erst nach der Schleife eingebunden wird.
Was meinst Du mit "Bild nicht sichtbar"? Wird das Ausblenden nicht angezeigt?
Oder stehe ich jetzt auf dem Schlauch?

BAMatze 17. Jul 2009 13:45

Re: Programm hält beim Start einfach an
 
Zitat:

Zitat von GHorn
Zitat:

Hätte eigentlich nur noch die Frage, warum eigentlich, wenn ich diesen Befehl weglasse das Bild in meiner Form nicht sichtbar ist, sondern erst nach der Schleife eingebunden wird.
Was meinst Du mit "Bild nicht sichtbar"? Wird das Ausblenden nicht angezeigt?
Oder stehe ich jetzt auf dem Schlauch?

Das aufblenden wird dargestellt mit schwarzem Bildschirm, ist das Fenster da, wird das Bild eingefügt. Problem lässt sich beheben, indem ich den angesprochenen Befehlt bei close nach der Schleife und beim Start vor die Schleife schreibe. Was ich mich gerade nur frage, ist warum dies so ist, dass das Bild ohne den Befehl nicht oder erst später angezeigt wird, als ich eigentlich möchte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:15 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz