Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Unerklärbare Zugriffsverletzung (https://www.delphipraxis.net/179588-unerklaerbare-zugriffsverletzung.html)

ByTheTime 18. Mär 2014 15:11

Unerklärbare Zugriffsverletzung
 
Hallo,
leider ist der Titel nicht ganz passend gewählt und mein Problem etwas undurchsichtig, aber ich versuche es mal bestmöglich darzustellen:

Ich habe einen Service, in welchem man Jobs anlegen kann, die verschiedene Events abwarten und bei deren eintreten eine Mail verschicken. Das geht auch mit mehreren Jobs. Sprich: Wenn Event A eintritt, dann schicke eine Mail an Person, welche im Auftrag (Job) A angegeben ist; Wenn Event B eintritt, dann schicke eine Mail an Person, welche im Auftrag B angegeben ist. Zu bestimmten Zeiten, welche ebenfalls im Auftrag festgelegt sind, wird dann ein Report bzw. eine Mail erstellt, welche die Events und Informationen enthält. Jetzt habe ich 2 Jobs angelegt, welche zur selben Zeit ihren Report abschicken sollen. In einer Schleife funktioniert das so:

Delphi-Quellcode:
  if EventCount > 0 then // wenn mehr als 0 Events existieren
  begin
    Log(False, 'EventCount for "' + Jobs[i].AName+ '": ' +
      IntToStr(EventCount)); // Im Log festhalten, wieviele Events für den Job eingetreten sind
    Log(False, 'All Events: ' + IntToStr(Length(Events))); // Die Anzahl aller Events im Log festhalten

    SendMail(i); // Mail schicken; i = Schleifen-Variable
  end;
Die Funktion Log habe ich selbst geschrieben, sie kapselt einfach nur den Zugriff auf eine Textdatei. In der Funktion SendMail wird einfach nur eine TStringList mit Text und Infos gefüllt und später an den MailBody übergeben. Anschließend wird eine DLL von mir aufgerufen, welche die Mail verschickt. Schlussendlich werden am Ende der SendMail-Prozedur noch die bereits geschickten Events aus der Liste der ausstehenden Events entfernt.

Die Ausgabe in meinem Log sieht übrigens so aus:
EventCount for "Auftrag 1": 3
All Events: 11
[18.03.2014 14:50:05]: INFORMATION: Message to "empfänger_1@googlemail.com" was successfully transmitted
EventCount for "Auftrag 2": 8
All Events: 8
[18.03.2014 14:50:09]: INFORMATION: Message to "empfänger_2@isforb.com" was successfully transmitted


Es funktioniert alles wie beschrieben: Es existieren zu Beginn 11 Events in der Liste, 3 für Job A. Nach dem verschicken der ersten Mail existeren noch 8, alle für Job B, das ist ja logisch.

Bis hierhin klappt auch alles, allerdings war die Ausgabe im Log nur zu Testzwecken, die interessiert mich nicht wirklich. Ich möchte nur wissen, ob die Mail erfolgreich geschickt wurde. Also sieht die Routine jetzt wie folgt aus:

Delphi-Quellcode:
  if EventCount > 0 then // wenn mehr als 0 Events existieren
    SendMail(i); // Mail schicken; i = Schleifen-Variable
Allerdings erhalte ich jetzt eine Zugriffsverletzung. Das Log dazu sieht so aus:
[18.03.2014 14:58:05]: INFORMATION: Message to "empfänger_1@googlemail.com" was successfully transmitted
[18.03.2014 14:58:05]: CRITICAL ERROR: [EAccessViolation] "Zugriffsverletzung bei Adresse 0040479F in Modul 'RPU_SRV.exe'. Lesen von Adresse 0324DD9C"


Jetzt verstehe ich aber nicht warum ich diese Zugriffsverletzung erhalte. Das Log hat ja rein garnichts mit dem Rest zu tun. In der der Prozedur wird lediglich in eine Textdatei geschrieben. Warum erhalte ich nun beim senden der zweiten Mail einen Fehler?

Ich sage vllt. noch dazu, das der Service aus mehreren Threads besteht, aber das war auch das einzige was noch wichtig sein könnte.

Ich hoffe ihr könnt mir helfen, natürlich könnte ich auch einfach die Zeilen mit dem Log stehen lassen, das würde aber meine Ehre kränken 8-)

P.S: Sorry für den vielen Text... Vielleicht sollte ich Autor werden :lol:

Gruß,
Lukas

Aviator 18. Mär 2014 15:41

AW: Unerklärbare Zugriffsverletzung
 
Leider hast du hier nur die Hälfte des eigentlich interessanten Codes gepostet. Was ist mit der SendMail Procedure? Du befindest dich ja anscheinend in einer For/While/Repeat Schleife. Dort wirst du wohl auf ein Element zugreifen, dass es nicht (mehr) gibt á la Listenindexfehler. Wenn der nicht kommt, kann der Fehler in der Sendmail Procedure auftreten. Schau doch dort noch einmal nach.

Blup 18. Mär 2014 16:10

AW: Unerklärbare Zugriffsverletzung
 
Die 'Log()'- Aufrufe erzeugen einen zeitlichen Abstand zwischen den 'SendMail()'-Aufrufen.
In deinem Beispiel 4 Sekunden sind eine kleine Ewigkeit.
Zitat:

Ich sage vllt. noch dazu, das der Service aus mehreren Threads besteht, aber das war auch das einzige was noch wichtig sein könnte.
In welchem Zusammenhang stehen die Jobs, Events und die Threads?
Wenn z.B. die Threads parallel neue Events der Liste hinzufügen und im Hauptthread Events ausgelesen und entfernt werden, ist eine saubere Synchronisation notwendig.

himitsu 18. Mär 2014 16:48

AW: Unerklärbare Zugriffsverletzung
 
Zitat:

Zitat von Blup (Beitrag 1252395)
Die 'Log()'- Aufrufe erzeugen einen zeitlichen Abstand zwischen den 'SendMail()'-Aufrufen.
In deinem Beispiel 4 Sekunden sind eine kleine Ewigkeit.

Ich hätte jetzt, daran gedacht, daß er im Debugmodus war und nicht schnell genug auf F7/F8 geklickt hat, wodurch diese Differenz entstand.

Sonst erstmal nix schreib, da ja bereits Alles/Vieles gefragt wurde.

ByTheTime 18. Mär 2014 19:15

AW: Unerklärbare Zugriffsverletzung
 
Erstmal danke für die Antworten :)

Also es gibt 3 Threads, den ServiceThread, welcher ja immer aktiv ist, jedoch bei mir keine relevanten Aufgaben hat. Dann gibt es noch einen Überwachungs-Thread, welcher auf Events wartet und diese bearbeitet, und den Mail-Thread. Dieser bekommt von dem Überwachungs-Thread immer die Events zugeschoben. Allerdings ist hier alles schön synchronisiert und ich hatte hier noch nie Probleme. Jobs und Events sind je 2 Arrays. Allerdings nicht ineinander verschachtelt. Sie dienen eigentlich nur zum speichern von den Aufträgen und eben den Ergenissen/Events

Die 4 Sekunden enstehen nicht durch das Loggen. Ich habe die besagt Mail-DLL mal aus dem stehgreif runtergetippt. Für jeden Sendevorgang werden die INDY-Komponenten neu initialisiert und einen Verbindung aufgebaut, auch wenn es der gleiche SMTP-Server ist. Klar könnte man das optimieren, aber für meine Zwecke, bei max. 5 Mails in einer Stunde nicht mein primäres Ziel.


@himitsu: Debugmodus :lol: Ja hab ich garnicht dran gedacht, aber wie geht das den bei einem Service :gruebel: Meine Vermutung war ja bis jetzt, das die Log Funktion irgendetwas verzögert, allerdings konnte ich das mit Sleep nicht ausgleichen. Wäre schön das nochmal mit dem Debugger durchzugehen.

@Aviator: Jop, der Code wird von einer for-Schleife umgeben. Allerdings hat sich mir kein Sinn ergeben warum hier ein Fehler auftreten könnte. Ich zeige gleich nochmal etwas mehr Code.

ByTheTime 18. Mär 2014 19:36

AW: Unerklärbare Zugriffsverletzung
 
Das wäre erstmal die vollständige Schleife von oben:
Delphi-Quellcode:
            for i := 0 to High(Jobs) do // eben für jeden Job durchgehen
            begin
              EventCount := 0; // Event-Zähler zurücksetzen

              if Jobs[i].SendWithNextWave = True then { Eine Vorher gesetzte Variable, welche angibt, ob der Job gesendet wird }
                for j := 0 to High(Events) do
                  if Events[j].AName = Jobs[i].AName then { prüft sozusagen die Zugehörigkeit aller Events zu dem im Moment in Bearbeitung befindlichen Job; Nicht elegant, aber ausreichend }
                    Inc(EventCount);

              if EventCount > 0 then
              begin
                { Log(False, 'EventCount for "' + Jobs[i].Mail + '": ' +
                  IntToStr(EventCount));
                  Log(False, 'All Events: ' + IntToStr(Length(Events))); }

                SendMail(i);
              end;
            end;
Hier wäre dann SendMail:
Delphi-Quellcode:
procedure TMailThread.SendMail(JobID: Integer);
var
  i: Integer;
  MMail: TStringList;
  ID: String;
  // EventCount = (Thread-)Global
begin
  MMail := TStringList.Create;

  MMail.Add('Es wurden ' + IntToStr(EventCount) + ' Meldungen für den Auftrag "'
    + Jobs[JobID].AName + '" gefunden:');
  MMail.Add('');

  { ... }
  { Andere Informationen hinzufügen, die hier aber nicht relevant sind (Keine Schleifen, nur stumpfe Zuweisungen von Text }

  for i := 0 to High(Events) do
    if Events[i].AName = Jobs[JobID].AName then
    begin
      MMail.Add('---');
      MMail.Add(Events[i].Info + ' | ' + Events[i].Time + ' | ' +
        Events[i].Status + ' | ' + Events[i].Date + ' | ' + Events[i].Note);
      MMail.Add('---');
      MMail.Add('');
    end
    else
    begin
      MMail.Add('---');
      MMail.Add(Events[i].Info + ' | ' + Events[i].Time + ' | ' +
        Events[i].Status + ' | ' + Events[i].Date + ' | ' + Events[i].Note);
      MMail.Add('---');
      MMail.Add('');
    end;

  { Der Code hierrüber fügt die Events welche dem passenden Job angehören einfach in den MailText ein. Allerdings frage ich mich gerade selbst, was das mit dem 'else' soll. Habe den Code aus einem anderen Projekt von mir übernommen und etwas umgestellt. Hier ergibt es keinen Sinn, ist mir eben erst aufgefallen, als ich es gepostet habe :D Ich lasse es trotzdem hier mal stehen. }

  MMail.Add('------------------------------------------------------------');
  MMail.Add('');
  MMail.Add('Dieser Bericht wurde um ' + DateTimeToStr(Now) + ' erstellt.');

  MailInfo.AHost := PChar(RPUService.Config.Host);
  MailInfo.APort := StrToInt(RPUService.Config.Port);
  { ... }

  { SMTP- und Mail-Einstellungen }

  ID := SMTPMail.SendMail(MailInfo); // Meine DLL: Zugriff und abschicken

  if ID = 'Success' then // Rückgabewert prüfen und bearbeiten
  begin
    Jobs[JobID].Sended := True;
    Jobs[JobID].SendWithNextWave := False;
    Jobs[JobID].LastTime := TimeOf(Now);
    Log(True, 'INFORMATION: Message to ' + Jobs[JobID].AMail +
      ' was successfully transmitted';
  end
  else
  begin
    Log(True, 'ERROR [' + Jobs[JobID].AMail + ']: ' + ID);
    Jobs[JobID].Sended := False;
    Jobs[JobID].SendWithNextWave := True;
  end;

  MMail.Free;

  if (Length(Events) > 0) and (Jobs[JobID].Sended = True) then
    for i := High(Events) downto 0 do
      if Events[i].AName= Jobs[JobID].AName then
        DeleteEventArrayElement(Events, i);

  { In den 4 Zeilen hierrüber werden die bearbeiteten Events aus der Liste entfernt }
So... Ich hoffe das ist etwas genauer. Ich gehe auch nochmal den Quellcode umgraben.

MfG

Ralf Kaiser 18. Mär 2014 19:47

AW: Unerklärbare Zugriffsverletzung
 
Zitat:

Zitat von ByTheTime (Beitrag 1252421)
@himitsu: Debugmodus :lol: Ja hab ich garnicht dran gedacht, aber wie geht das den bei einem Service :gruebel: Meine Vermutung war ja bis jetzt, das die Log Funktion irgendetwas verzögert, allerdings konnte ich das mit Sleep nicht ausgleichen. Wäre schön das nochmal mit dem Debugger durchzugehen.

Hi,

du könntest ein zweites "Debug"-Projekt erstellen, das alle Units aus dem Service enthält und eventuell Logausgaben direkt anzeigt. Dieses Programm läuft dann nicht als Service sondern als normale Applikation die dann einfach mit dem Debugger zu verfolgen ist.

Ciao,
Ralf

himitsu 18. Mär 2014 21:24

AW: Unerklärbare Zugriffsverletzung
 
Man muß nur den Debugger/IDE mit den nötigen Rechten starten und schon hat man Zugriff.

Zitat:

du könntest ein zweites "Debug"-Projekt erstellen
Ja, es ist recht "einfach" den Service auch als normale Anwendung zu starten.

Meine letzte gewachsene Variante reagiert darauf wie/wo der Service gestartet wurde und verbiegt den Service dann so, daß er als "normale" Anwendung läuft.
Delphi-Quellcode:
uses
  ..., Forms, SvcMgr, ...; // Forms (vor der SvcMgr), damit man Zugriff auf die Messagebehandlung und auf die normale TForm hat.

procedure TMyService.ServiceCreate(Sender: TObject);
var
  Started: Boolean;
  Temp:   TForm;
begin
  LogMessage('Service wird erzeugt', EVENTLOG_INFORMATION_TYPE, 0, 1);

  ...

  if IsDebuggerPresent or FindCmdLineSwitch('DEBUG', ['-', '/'], True) then begin
    MyService := Self; // wird sonst von Application.CreateForm gesetzt, aber durch die Messageloop kommt es dort nicht vorbei

    Forms.Application.MainFormOnTaskBar := False; // geht leider doch nicht ohne Form
    Forms.Application.CreateForm(TForm, Temp);   // Form zum Beenden und für Eintrag in Taskbar
    Temp.Caption     := 'Debug-Mode: ' + SvcMgr.Application.Title;
    Temp.OnCloseQuery := DebugServiceClose;
    Temp.Visible     := True;

    // ab hier der Ersatz für SvcMgr.Application.Run, welches sonst sofort Beenden würde, da es kein echter Service ist
    if FindCmdLineSwitch('INSTALL', ['-', '/'], True) then
      TServiceApplicationHack(Application).RegisterServices(True, FindCmdLineSwitch('SILENT', ['-', '/'], True))
    else if FindCmdLineSwitch('UNINSTALL', ['-', '/'], True) then
      TServiceApplicationHack(Application).RegisterServices(False, FindCmdLineSwitch('SILENT', ['-', '/'], True))
    else begin
      Started := True;
      ServiceStart(Self, Started);
      if Started then begin
        ServiceExecute(nil);
        while not Terminated do
          try
            Forms.Application.ProcessMessages;
            Sleep(10);
          except
            on E: Exception do begin
              LogMessage('Error: ' + E.ClassName + ' = ' + E.Message, EVENTLOG_WARNING_TYPE, 0, 22);
              Sleep(1000);
            end;
          end;
      end;
      Forms.Application.Terminate;
    end;
  end;
end;

procedure TMyService.ServiceExecute(Sender: TService);
begin
  LogMessage('Service wird ausgeführt', EVENTLOG_INFORMATION_TYPE, 0, 3);

  ...

  while Assigned(Sender) and not Terminated do//warten auf beenden
    try
      ServiceThread.ProcessRequests(False);
      Sleep(10);
    except
      on E: Exception do begin
        LogMessage('Error: ' + E.ClassName + ' = ' + E.Message, EVENTLOG_WARNING_TYPE, 0, 22);
        Sleep(1000);
      end;
    end;
  LogMessage('Service wurde beendet', EVENTLOG_INFORMATION_TYPE, 0, 5);
end;

procedure TMyService.DebugServiceClose(Sender: TObject; var CanClose: Boolean);
begin
  if Assigned(MyService) and Assigned(MyService.ServiceThread) then
    MyService.ServiceThread.Terminate;
end;
Aber so sind die Rechte natürlich etwas Anderes und auch der "Benutzerkontext", womit da eventuell nicht alle Resourcen gleich verfügbar sind.
Etwas was im Service läuft, könnte hier nicht laufen. Und andersrum.

Zitat:

Delphi-Quellcode:
if Jobs[i].SendWithNextWave = True then

Delphi-Quellcode:
= True
?


Man kann natürlich aud die IDE und den Debugger mit den nötigen Rechten starten.
z.B. als Admin

Oder man lässt den Service in einem anderem Benutzerkontext laufen.
Wenn man den Service im normalen Benutzerkontext laufen lässt, dann hätte er es eventuell auch einfacher auf den Desktop dieses Benutzers zuzugreifen.

ByTheTime 18. Mär 2014 21:36

AW: Unerklärbare Zugriffsverletzung
 
Ich glaube ich mache ein Debug Projekt, das ist nicht so viel Code, da mach ich mir die "Mühe" :)

@himitsu:
Zitat:

Zitat von himitsu (Beitrag 1252427)
Zitat:

Delphi-Quellcode:
if Jobs[i].SendWithNextWave = True then

Delphi-Quellcode:
= True
?

Hö? :stupid:

Was wills du mir damit sagen :gruebel:

Sir Rufo 18. Mär 2014 22:13

AW: Unerklärbare Zugriffsverletzung
 
Das Vergleichen mit
Delphi-Quellcode:
true
ist doppelgemoppel, denn
Delphi-Quellcode:
if ... then
prüft schon auf
Delphi-Quellcode:
not false
und dadurch wird das zu
Delphi-Quellcode:
if ( Bedingung = true ) <> not false then
.

Delphi-Quellcode:
false
ist definiert als
Delphi-Quellcode:
0
und
Delphi-Quellcode:
true
ist das Gegenteil (alles ausser 0). Der Einfachheit halber wurde
Delphi-Quellcode:
true
(in Delphi) als Konstante mit dem Wert
Delphi-Quellcode:
1
festgelegt. Andere Sprachen verwenden hier andere Werte (z.B.
Delphi-Quellcode:
-1
), was egal ist, wenn man eben nicht mit
Delphi-Quellcode:
true
vergleicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:38 Uhr.
Seite 1 von 3  1 23      

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 by Thomas Breitkreuz