Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   [AV] Ende einer Prozedur verursacht eine AV... (https://www.delphipraxis.net/73065-%5Bav%5D-ende-einer-prozedur-verursacht-eine-av.html)

Daniel G 11. Jul 2006 22:53


[AV] Ende einer Prozedur verursacht eine AV...
 
Liste der Anhänge anzeigen (Anzahl: 1)
N'Abend,

Ich hab' mal wieder ein kleines Problem mit einem C - Code, den ich nach Delphi portiert habe. (Ja, ich weiß, ich sollte diese verdammte Sprache endlich lernen :wall: ). Da in dem Original die AV nicht auftritt, denke ich, dass das mit meiner Portierung zu tun hat...

Nevertheless, mein Problem liegt hier:

Delphi-Quellcode:
//----------------------------------------------------------------------------
// ATA-Befehle unter Windows 2000 oder XP versenden
Function TAAMClass.SendCommand_Win2000(pATARegs: PATARegs; var pBuf;
BufSize: Cardinal; pResultSize: Cardinal): Boolean;
var
  Size: Cardinal;
  VirtualAddress: Pointer;
  pAPT: ATA_PASS_THROUGH;
  BytesReturned: LongWord;
  Status: Boolean;
  ReturnedSize: Cardinal;
begin
  Result := FALSE;
  BytesReturned := 0;

  Size := sizeof(ATA_PASS_THROUGH) + BufSize;
  // Datenpuffer mit VirtualAlloc anfordern, damit der Gerätetreiber den Speicher ansprechen kann
  VirtualAddress := VirtualAlloc(NIL, Size, MEM_COMMIT, PAGE_READWRITE);
  pAPT := ATA_PASS_THROUGH(VirtualAddress^);

  if Assigned(@pAPT) then
  begin
      //ZeroMemory(@pAPT, Size);
      // Größe des Datenpuffers setzen
      pAPT.DataBufferSize         := BufSize;
      // ATA-Register setzen
      pAPT.IdeReg.bFeaturesReg    := pATARegs^.Reg[0];
      pAPT.IdeReg.bSectorCountReg := pATARegs^.Reg[1];
      pAPT.IdeReg.bSectorNumberReg := pATARegs^.Reg[2];
      pAPT.IdeReg.bCylLowReg      := pATARegs^.Reg[3];
      pAPT.IdeReg.bCylHighReg     := pATARegs^.Reg[4];
      pAPT.IdeReg.bDriveHeadReg   := pATARegs^.Reg[5];
      pAPT.IdeReg.bCommandReg     := pATARegs^.Reg[6];

      if (pAPT.IdeReg.bCommandReg = $EC) then // ist es ATA IDENTIFY?
      begin
           // Windows XP merkt sich den IDE-Konfigurationssektor
          // daher vorher mit IOCTL_SCSI_RESCAN_BUS für ungültig erklären

          DeviceIoControl(fDriveHandle, IOCTL_SCSI_RESCAN_BUS, NIL, 0, NIL, 0, BytesReturned, 0);
          // eine halbe Sekunde warten
          Sleep(500);
      end;

      BytesReturned := 0;
     
      Status := DeviceIoControl(fDriveHandle, IOCTL_IDE_PASS_THROUGH,
                                    @pAPT, Size, @pAPT, Size, BytesReturned, 0);
      if not Status then
        fError := aec_CommandFailed
      else
      begin
          if Assigned(@pBuf) then
          begin
              if (BufSize <> 0) then
                ZeroMemory(@pBuf, BufSize);
              if (BytesReturned > sizeof(ATA_PASS_THROUGH)) then
              begin
                  ReturnedSize := BytesReturned - sizeof(ATA_PASS_THROUGH);
                  if (pResultSize <> 0) then
                    pResultSize := ReturnedSize;
                  if (ReturnedSize > BufSize) then
                    ReturnedSize := BufSize;
                  Move(pAPT.DataBuffer, pBuf, ReturnedSize);
              end;
          end;
          Result := TRUE;
      end;
      VirtualFree(@pAPT, Size, MEM_RELEASE);
  end
  else
    fError := aec_OutOfMemory;
end;
Nachdem die Funktion durchlaufen wurde, werde ich mit einer AV bombadiert:

Zitat:

---------------------------
Anwendungsfehler
---------------------------
Exception EAccessViolation in Modul bquiet.exe bei 57201020.

Zugriffsverletzung bei Adresse 57202020. Lesen von Adresse 57202020.


---------------------------
OK
---------------------------
Ich tippe mal, ich mache dort irgendwie Mist mit den Pointern. Leider kann ich mit dieser Meldung absolut nichts anfangen, da im Vergleich zu sonst dort ausnahmsweise ne Adress steht und nicht, wie gewohnt, 00000000 (:mrgreen:)

Der äquivalente C - Code wäre:

Code:
//----------------------------------------------------------------------------
// ATA-Befehle unter Windows 2000 oder XP versenden

BOOL CWinAta::SendCommand_Win2000(PATARegs pATARegs, void *pBuf, unsigned int BufSize, unsigned int *pResultSize)
{
 
  //---------------------------------------------------
  // Definitionen und Datenstruktur für Windows 2000 und XP
  // aus dem DDK-Headerfile ntddscsi.h übernommen
 
  #define IOCTL_IDE_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  #define FILE_ANY_ACCESS        0
  #define IOCTL_SCSI_RESCAN_BUS  CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS)

  typedef struct
  {
      IDEREGS IdeReg;
      ULONG  DataBufferSize;
      UCHAR  DataBuffer[1];
  } ATA_PASS_THROUGH;
  //---------------------------------------------------
 
 
  BOOL Result = FALSE;
 
  unsigned int Size = sizeof(ATA_PASS_THROUGH) + BufSize;
  // Datenpuffer mit VirtualAlloc anfordern, damit der Gerätetreiber den Speicher ansprechen kann
  ATA_PASS_THROUGH *pAPT = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, Size, MEM_COMMIT, PAGE_READWRITE);
 
  if (pAPT)
  {
      memset(pAPT, 0, Size);
      // Größe des Datenpuffers setzen
      pAPT->DataBufferSize         = BufSize;
      // ATA-Register setzen
      pAPT->IdeReg.bFeaturesReg    = pATARegs->Reg[0];
      pAPT->IdeReg.bSectorCountReg = pATARegs->Reg[1];
      pAPT->IdeReg.bSectorNumberReg = pATARegs->Reg[2];
      pAPT->IdeReg.bCylLowReg      = pATARegs->Reg[3];
      pAPT->IdeReg.bCylHighReg     = pATARegs->Reg[4];
      pAPT->IdeReg.bDriveHeadReg   = pATARegs->Reg[5];
      pAPT->IdeReg.bCommandReg     = pATARegs->Reg[6];

      if (pAPT->IdeReg.bCommandReg == 0xEC) // ist es ATA IDENTIFY?
      {
     // Windows XP merkt sich den IDE-Konfigurationssektor
          // daher vorher mit IOCTL_SCSI_RESCAN_BUS für ungültig erklären
          DWORD BytesReturned = 0;
          (void)DeviceIoControl(Device, IOCTL_SCSI_RESCAN_BUS, NULL, 0, NULL, 0, &BytesReturned, FALSE);
          // eine halbe Sekunde warten
          Sleep(500);  
      }

      DWORD BytesReturned = 0;
     
      BOOL Status = DeviceIoControl(Device, IOCTL_IDE_PASS_THROUGH,
                                    pAPT, Size, pAPT, Size, &BytesReturned, FALSE);
      if (!Status)
        WinAtaErrorCode = aec_CommandFailed;
      else
      {
          if (pBuf)
          {
              if (BufSize)
                memset(pBuf, 0, BufSize);
              if (BytesReturned > sizeof(ATA_PASS_THROUGH))
              {
                  unsigned int ReturnedSize = BytesReturned - sizeof(ATA_PASS_THROUGH);
                  if (pResultSize)
                    *pResultSize = ReturnedSize;
                  if (ReturnedSize > BufSize)
                    ReturnedSize = BufSize;
                   
                  memcpy(pBuf, pAPT->DataBuffer, ReturnedSize);
              }
          }
          Result = TRUE;
      }
      VirtualFree(pAPT, Size, MEM_RELEASE);
  }
  else
    WinAtaErrorCode = aec_OutOfMemory;
 
  return Result;
}
Hat irgendwer eine Idee? Im Anhang ist mal das komplette Delphi - Projekt. Auf das C - Original verlinke ich lieber. Ist von der c't... es handelt sich um die "WinATA.cpp".

Download vpn Heise *klick*

Danke schonmal für's lesen. ;)

Daniel G 12. Jul 2006 10:56

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
So, ich bins wieder. Ich habe mir das CPU - Fenster mal genauer angesehen. Es hängt also wirklich mit irgendeinem falsch zeigenden Pointer zusammen. So sehen ich in dem Fenster, wo die ganzen Adressen stehen, lauter Fragezeichen.

Kann es eventuell an einer falschen Verwendung von VirtualAlloc liegen?

Muetze1 12. Jul 2006 13:02

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Oder an MemSet() und MemCopy()?. Wie wärs denn mit was debugbarem, wenn du es nicht hinbekommst die Adressen zu debuggen...

Daniel G 12. Jul 2006 13:04

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Muetze1
Wie wärs denn mit was debugbarem, wenn du es nicht hinbekommst die Adressen zu debuggen...

Ich weiß, du willst mir damit was sagen, nur was? :gruebel:

//Edit:
Ich bin gerade auf die großartige Idee gekommen, dass ich die MadExcept ja wieder installieren könnte. :mrgreen: Ich hol das mal eben nach...

//Edit2: So, hier mal der Bugreport. Das Projekt selbst befindet sich ja oben im Anhang

Daniel G 12. Jul 2006 13:43

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Zitat:

Zitat von Muetze1
Oder an MemSet() und MemCopy()?.

Ich hatte für MemSet ZeroMemory benutzt, da MemSet lauter nullen setzt. Also dachte ich, ZeroMemory würde dasselbe machen. Oder ist das ein Irrglaube? Ich hatte den Befehl ja auskommentiert. Wenn ich ihn wieder hinnein nehme, zeigt der Pointer ins Leere...

Flocke 12. Jul 2006 15:46

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Code:
ATA_PASS_THROUGH *pAPT
Das bedeutet, dass pAPT ein ZEIGER auf eine ATA_PASS_THROUGH-Struktur ist.

Nachdem du das in Delphi geändert hast, kannst du den Typecast und die @'s vor pAPT entfernen (Assigned(@x) ist 'ne ziemlich sinnlose Abfrage).

Daniel G 12. Jul 2006 17:09

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Hi Flocke,

das heißt also, wenn ich
Delphi-Quellcode:
Type
PATA_PASS_THROUGH = ^ATA_PASS_THROUGH
definieren würde, könnte ich das so schreiben:
Delphi-Quellcode:
var
pAPT: PATA_PASS_THROUGH;
begin
pAPT := VirtualAlloc(NIL, Size, MEM_COMMIT, PAGE_READWRITE);
if Assigned(pAPT) then
[...]
Damit wäre ich diese AV schonmal interessanterweise los. Vllt. is' das ja doch nicht so kompliziert, wie ich denke... Aber warum ist "Assigned(@pAPT)" mit der alten Definition denn sinnlos?

Christian Seehase 12. Jul 2006 18:18

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Moin Daniel,

Zitat:

Zitat von Daniel G
Aber warum ist "Assigned(@pAPT)" mit der alten Definition denn sinnlos?

das hat nichts mit der alten Definition zu tun.

Assigned prüft den Ausdruck auf nil. Nun wird die Adresse einer Variablen aber wohl nie gleich nil sein... ;-)

Daniel G 12. Jul 2006 19:06

Re: [AV] Ende einer Prozedur verursacht eine AV...
 
Zitat:

Zitat von Christian Seehase
Nun wird die Adresse einer Variablen aber wohl nie gleich nil sein... ;-)

:gruebel: Ähä.. Ok, das ergibt Sinn....


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:30 Uhr.

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