Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Access Violation unter Win 10 (https://www.delphipraxis.net/196519-access-violation-unter-win-10-a.html)

tumo 28. Mai 2018 16:38

Access Violation unter Win 10
 
Hallo zusammen,

ich habe mir ein kleines Programmchen geschrieben, was Morsecode "abspielt". Dazu nutze ich die BASS.dll. Funktioniert alles super. Nun wollte ich mein Programm meinen Freunden geben, die Windows 10 haben, ich selber arbeite auf Windows 8.1 Delphi XE3. Das Programm wirft bei ihnen beim drücken des Play-Buttons eine Access Violation aus. (Exception der Klasse $C00000005 mit der Meldung Read of adress 0x00000019). Unter Win 7 und 8.1 kommt dieser Fehler nicht. Ebenfalls kommt er nur in der Release-Version vor. Nun dachte ich, ich bin klug und debugge oldschool, mithilfe von ShowMessage oder ner Memo, nur durfte ich festestellen, dass es dann keine Exception mehr auswirft.

Klar, könnte ich jetzt einfach ein unsichtbare Memo machen und immer eine Zeile schreiben, aber komisch ist die ganze sache schon, nur die Frage: Warum?

Mit freundlichen Grüßen

Hier die PlayButton Routine:

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
begin
  Label11.Caption := 'Playing...';
// ShowMessage('1');    <-- Wenn aktiv, kein Fehler mehr
//Memo1.Lines.Add('1'); <-- Wenn aktiv, kein Fehler mehr

  Button4.Enabled := true;
  Button2.Enabled := false;
  EnablePlaying(true); //schaltet manche Komponenten aus, die nicht gedrückt werden dürfen

// ShowMessage('2');    <-- Wenn aktiv, kein Fehler mehr
//Memo1.Lines.Add('2'); <-- Wenn aktiv, kein Fehler mehr

  Play(Richedit1.Lines,DitSettings,DahSettings,DitPool,DahPool);

// ShowMessage('3');   <-- Wenn aktiv, kein Fehler mehr
//Memo1.Lines.Add('3'); <-- Wenn aktiv, kein Fehler mehr

  for i := 0 to Length(DitPool) - 1 do
  BASS_ChannelStop(DitPool[i].SoundStream);
  for i := 0 to Length(DahPool) - 1 do
  BASS_ChannelStop(DahPool[i].SoundStream);

  Richedit1.SelectAll;
  Richedit1.SelAttributes.Color := clBlack;
  RichEdit1.SelLength := 0;

  EnablePlaying(false);

  Button2.Enabled := true;
  Button4.Enabled := false;

  Label11.Caption := 'Finished or Stopped';
end;
Und hier noch die Play Routine, falls das Problem dort ist (Achtung, etwas länger):

Delphi-Quellcode:
procedure TForm1.Play(Text: TStrings; DitRule, DahRule: TOutputSettings; DitSound,DahSound: TAudioFileArray);
var
  i,i2: Integer;
  CurrAudio,BeforeAudio: TAudioFile;
  DitBefore,DahBefore,Dur: integer;
begin
  DitBefore := 0;
  DahBefore := 0;

 { for i := 0 to Length(DitSound) - 1 do
  BASS_ChannelSetAttribute(DitSound[i].SoundStream,BASS_ATTRIB_VOL,Volume);
  for i := 0 to Length(DahSound) - 1 do
  BASS_ChannelSetAttribute(DahSound[i].SoundStream,BASS_ATTRIB_VOL,Volume); }

  for i := 0 to Text.Count - 1 do
  begin
    for i2 := 1 to Length(Text[i]) do
    begin
      RichEdit1.SelStart := i2 - 1;
      RichEdit1.SelLength := 1;
      RichEdit1.SelAttributes.Color := clRed;
  //    Richedit1.ClearSelection;

      case Text[i][i2] of
        '.':
          begin
            case DitRule.Rules of
              prRandom:
                begin
                  CurrAudio := DitSound[Random(Length(DitSound) - 1)];
                end;
              prOrder:
                begin
                  if DitBefore = Length(DitSound) - 1 then
                  DitBefore := -1;
                  CurrAudio := DitSound[DitBefore + 1];
                  Inc(DitBefore);
                end;
              prSingleOnly:
                begin
                  CurrAudio := DitSound[0];
                end;
            end;

            case BeforeAudio.PlayHandling of
              phWait:
                begin
                  while BASS_ChannelIsActive(BeforeAudio.SoundStream) = BASS_ACTIVE_PLAYING do
                  begin
                    Idle(10);
                  end;
                end;
              phContinue:
                begin

                end;
              phPause:
                begin
                  BASS_ChannelPause(BeforeAudio.SoundStream);
                end;
              phStop:
                begin
                  BASS_ChannelStop(BeforeAudio.SoundStream);
                end;
            end;

            if CurrAudio.PlayHandling = phPause then
            BASS_ChannelPlay(CurrAudio.SoundStream,false)
            else
            BASS_ChannelPlay(CurrAudio.SoundStream,true);

            BeforeAudio := CurrAudio;

            if CurrAudio.PlayHandling <> phWait then
            begin
              if DitRule.Variance then
              Idle(DitRule.Duration + Random(DitRule.VarianceValue))
              else
              Idle(DitRule.Duration);
            end;
          end;
        '-':
          begin
            case DahRule.Rules of
              prRandom:
                begin
                  CurrAudio := DahSound[Random(Length(DahSound) - 1)];
                end;
              prOrder:
                begin
                  if DahBefore = Length(DahSound) - 1 then
                  DahBefore := -1;
                  CurrAudio := DahSound[DahBefore + 1];
                  Inc(DahBefore);
                end;
              prSingleOnly:
                begin
                  CurrAudio := DahSound[0];
                end;
            end;

            case BeforeAudio.PlayHandling of
              phWait:
                begin
                  while BASS_ChannelIsActive(BeforeAudio.SoundStream) = BASS_ACTIVE_PLAYING do
                  begin
                    Idle(10);
                  end;
                end;
              phContinue:
                begin
                end;
              phPause:
                begin
                  BASS_ChannelPause(BeforeAudio.SoundStream);
                end;
              phStop:
                begin
                  BASS_ChannelStop(BeforeAudio.SoundStream);
                end;
            end;

            if CurrAudio.PlayHandling = phPause then
            BASS_ChannelPlay(CurrAudio.SoundStream,false)
            else
            BASS_ChannelPlay(CurrAudio.SoundStream,true);

            BeforeAudio := CurrAudio;

            if CurrAudio.PlayHandling <> phWait then
            begin
              if DahRule.Variance then
              Idle(DahRule.Duration + Random(DahRule.VarianceValue))
              else
              Idle(DahRule.Duration);
            end;
          end;
        ' ':
          begin
            case BeforeAudio.PlayHandling of
              phWait:
                begin
                  while BASS_ChannelIsActive(BeforeAudio.SoundStream) = BASS_ACTIVE_PLAYING do
                  begin
                    Idle(10);
                  end;
                end;
              phContinue:
                begin
                end;
              phPause:
                begin
                  BASS_ChannelPause(BeforeAudio.SoundStream);
                end;
              phStop:
                begin
                  BASS_ChannelStop(BeforeAudio.SoundStream);
                end;
            end;

            Idle(BetterSpinedit5.Value);
          end;
        '/':
          begin
            case BeforeAudio.PlayHandling of
              phWait:
                begin
                  while BASS_ChannelIsActive(BeforeAudio.SoundStream) = BASS_ACTIVE_PLAYING do
                  begin
                    Idle(10);
                  end;
                end;
              phContinue:
                begin
                end;
              phPause:
                begin
                  BASS_ChannelPause(BeforeAudio.SoundStream);
                end;
              phStop:
                begin
                  BASS_ChannelStop(BeforeAudio.SoundStream);
                end;
            end;

            Idle(BetterSpinedit6.Value);
          end;
        '#':
          begin

          end;
      end;
      if Stop then
      begin
        Stop := false;
        exit;
      end;

      RichEdit1.SelStart := i2 - 1;
      RichEdit1.SelLength := 1;
      RichEdit1.SelAttributes.Color := clLime;
//      Richedit1.ClearSelection;
    end;
    if Stop then
    begin
      Stop := false;
      exit;
    end;
  end;
end;

tumo 28. Mai 2018 17:01

AW: Access Violation unter Win 10
 
Ok, hätte vielleicht warten können mit dem Thread.

Hat sich geklärt, nach weiterem Debuggen habe ich herausgefunden, dass
Delphi-Quellcode:
case BeforeAudio.PlayHandling of
              phWait:
                begin
                  while BASS_ChannelIsActive(BeforeAudio.SoundStream) = BASS_ACTIVE_PLAYING do
                  begin
                    Idle(10);
                  end;
                end;
              phContinue:
                begin

                end;
              phPause:
                begin
                  BASS_ChannelPause(BeforeAudio.SoundStream);
                end;
              phStop:
                begin
                  BASS_ChannelStop(BeforeAudio.SoundStream);
                end;
            end;
scheinbar die Exception beim ersten Durchlauf wirft, sprich, wenn
Delphi-Quellcode:
BeforeAudio
undefiniert, aber nicht NIL ist. Im Debug springt Delphi scheinbar in case-Schleifen bei ungültigen Werten einfach weiter und unter Win 7 und 8.1 auch. Warum ein ShowMessage vor dem Aufruf der Play-Routine (aber nicht innerhalb der Play Routine) das verhindert, ist mir schleierhaft, aber okay.

Mit freundlichen Grüßen

hoika 28. Mai 2018 17:07

AW: Access Violation unter Win 10
 
Hallo,
da hättest Du doch eine Compilerwarnung bekommen müssen?

Win10 prüft die Exe'n viel stärker auf Speicherüberschreibung und Zugriffe auf nicht erlaubte Bereiche,
also wie bei dir mit dem Zugriff auf eine nicht initialisiertes Objekt.

Ich empfehle dann immmer "Nimm MadExcept".

freimatz 28. Mai 2018 17:32

AW: Access Violation unter Win 10
 
Ne nicht okay

Vielleicht fängst Du mal an, Warnungen und Hinweise des Compilers nicht zu ignorieren. ;-)

tumo 31. Mai 2018 20:30

AW: Access Violation unter Win 10
 
Hallo,

@hoika Ja, habe ich, nur achte ich nicht auf Compilerhinweise, solange das Programm in seiner Grundstruktur nicht läuft. Warnungen schau ich schon öfter an, nur waren die auch bisher unbedeutend für mich, da das meistens Warnungen waren, wie "xyz ist möglicherise nicht initialiesiert worden" wo ich daneben sitze und sage "Jein. Ich weise einen anderen zu, garantiert. Ergo Warnung unbedeutend". Schon Karma, dass es dieses Mal der Fehler war.

@freimatz Doch ist okay, hab ja den Fehler gefunden. Die Hinweise und insbesondere die Warnungen sind für mich nie ein Problem gewesen. Da da auch so Sachen sind wie "xyz ist möglicherise nicht initialiesiert worden", was ich vorher aber in jedem Fall zuweise, ignorier ich die öfter mal. Wenn ich dann sehr viele Warnungen und Hinweise habe, habe ich auch keine Lust, die durchzugehen und zu gucken ob möglicherweise einer davon mein Problem verursacht. Da geht es schneller, wenn ich das schnell durchdebugge.

hoika 31. Mai 2018 20:36

AW: Access Violation unter Win 10
 
Hallo,
Zitat:

xyz ist möglicherweise nicht initialisiert worden
Dann bist Du selber Schuld, weil genau diese Warnung darfst Du nicht ignorieren,
gerade bei Objekten.

KodeZwerg 31. Mai 2018 20:37

AW: Access Violation unter Win 10
 
Wenn Dateigröße egal ist würde ich anders vorgehen um OS Probleme zu vermeiden:

Wandel dein Ton in WAV/PCM und steig auf PlaySound() um, binde die Waves als Resource ein, spiele mit PlaySound die Resource, schmeiss Bass.dll raus, fertig.


edit
Ps: Falls Du nicht Wissen solltest wie, ich kann gerne ein Beispiel teilen das Dir alle Grundlagen Erklärt da ich erst Gestern mir selbst so etwas geschrieben habe, allerdings als eigener Thread, damit bei mir etwas solange wie der Ton spielt Unterdrückt bzw nach Ton erst freigegeben wird, und das App nicht hängt während Thread aufs Ton-Ende wartet.

tumo 1. Jun 2018 16:44

AW: Access Violation unter Win 10
 
Hallo,

@hoika Naja, selbstverständlich bin ich selber schuld, ist ja mein Code. Hier kurz zum Verständnis: Ich habe zwei Arrays, ein Dit und ein Dah Array of TAudioFile. Da stehen alle möglichen Sachen zu Audiodateien drinne. Nun brauche ich für meine Routine den Sound, der zuletzt gespielt wurde, ergo ich übergebe das aktuelle Arrayglied an eine Variable und lese die später aus. Das hat ein Problem verursacht, was mir nicht bekannt war (ich habe zum ersten Mal mit eigenen Objekten gearbeitet und musste erst rausfinden, dass man in einer Variable des Typen nur einen Pointer auf den Speicher schreibt und nicht den Typ mit all seinen propertys selber, stört ja aber für meinen Anwendungszweck garnicht), was ja jetzt auch gelöst ist. Nun habe ich einen Record, aus dem ich Spielanweisungen auslese. Dieser wird im OnCreate beschrieben und später nur noch mit (garantiert gültigen) Werten überschrieben. Dann lese ich aus diesem Record aus und verwende Daten darin. Jetzt sagt mir Delphi, dass diese Variable, die den Inhalt eines (in jedem Fall beschriebenen) Records beinhaltet, möglicherweise nicht initialisiert wird. Was auch immer das in diesem Zusammenhang bedeutet.

@KodeZwerg Ja, nee, soll eher so ablaufen, dass der Benutzer selber Sounds reinladen kann. Da .mp3 und .wav da recht verbreitet ist und ich bei "Delphi MP3 abspielen" so gut wie immer nur auf BASS gestoßen bin, hab ich mich da (in einer abgeschwächten Version) "reingefuchst". Insofern man mit deiner Methode zur Runtime Sounds individuell laden, abspielen und Sachen wie Lautstärke ändern kann, würde ich mir die gerne auch mal anschauen.

KodeZwerg 1. Jun 2018 17:03

AW: Access Violation unter Win 10
 
Nein, mein Beispiel würde sich auf das Wave Dateiformat beschränken, lese mal PlaySound function. Das ist simple Api für simple Nutzung.
Laut Leise Repeat Shuffle Record Convert Upload Download Tags WasAuchImmer, all das muss wenn gewünscht selber nachgerüstet werden, in wie fern da was Möglich ist entnehme bitte MSDN.
Die einzige Postive Nachricht zu Deinen Fragen, man muss nicht Resource verwenden, Du kannst auch während Laufzeit aus Resource einen Dateinamen übergeben. So wie "schau in Ordner X ob es Datei Y gibt, wenn nicht dann benutze Resource." Erweiterbar indem Du User eine x-beliebige Datei aussuchen lässt. Irgendwie so etwas.
Ich will damit Bass nicht schlecht machen aber um ein "pieps"-Geräusch abzuspielen bedarf es halt keiner großen Bibliothek, darauf wollte ich hinaus, mit Bass, korrekte Handhabung Vorausgesetzt, ist natürlich alles viel flexibler und universeller Nutzbar und vor allem Erweiterbarer aber im gleichen Augenblick bist Du auch an Bass gebunden, falls ein OS Update dieses kaputt macht hast Du ein Problem.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:13 Uhr.

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