Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Zugriffsverletzung bei(Kommunikations)Timeout mit overlapped (https://www.delphipraxis.net/100600-zugriffsverletzung-bei-kommunikations-timeout-mit-overlapped.html)

Viktorii 1. Okt 2007 11:06


Zugriffsverletzung bei(Kommunikations)Timeout mit overlapped
 
Moin zusammen.

Ich möchte von einem USB-Geräte lesen und es beschreiben können. Dazu benutze ich DriverX von Tetradyne. Das funktioniert soweit auch alles wunderbar.
Wenn ich nun vom Gerät lesen möchte, dieses aber nicht antwortet bleibt meine Anwendung in der Read Funktion 'stecken'. Ich möchte also sowas wie ein Timeout haben. Der Treiber bietet keine Möglichkeit einen Timeout einzustellen. Die einzige Chance die ich sehe ist es mit 'overlapped' zu realisieren. 'Overlapped' war mit bis jetzt noch kein Begriff. Die Dokumentation gibt leider nicht viel her:

Zitat:

Reads from a USB device bulk endpoint.


BOOL DXUSBAPI UsbBulkRead(PDXUSB_DEVICE device, ULONG iIndex, ULONG endpoint, ULONG flags, PVOID buffer, ULONG cb, PULONG pcbRet, LPOVERLAPPED pol);

Parameters

...

pol

Pointer to initialized OVERLAPPED structure to use for request or NULL to block on completion.

...

Comments
If pol is NULL, UsbBulkRead will block on completion of the request. If pol is not NULL, then UsbBulkRead will return immediately. The caller can then block on completion of the request using the specified OVERLAPPED structure and the Win32 WaitForSingleObject or GetOverlappedResult APIs.

Nun habe ich ein wenig rumgegooglet und habe versucht, das was ich gefunden habe umzusetzten:


Code:
procedure InitOverlapped(var Overlapped : lpOverlapped);
begin
  Overlapped.Offset := 0;
  Overlapped.OffsetHigh := 0;
  Overlapped.Internal := 0;
  Overlapped.InternalHigh := 0;}
  Overlapped.hEvent := CreateEvent(nil,True,False,'');
end;


function ReceiveCommand(...):..
var
  ...
  Overlapped     : lpOverlapped;

begin

  InitOverlapped(Overlapped);

  Ret := UsbBulkRead(deviceHandle, 0, 0, 0, @USBcb, 8, BytesReceived, Overlapped);
Wenn ich nun aber das Programm ausführe und auf ein Element der Overlapped Struktur zugegriffen wird, kommt es zu einer Zugriffsverletzung:

Zitat:

Im Projekt xyz.exe ist eine Exception der Klasse EaccessViolation mit der Meldung 'Zugriffsverletzung bei Adresse 00464ED1 in Modul xyz.exe'. Schreiben von Adresse 00431a20' aufgetreten.
Kann dies nicht wirklich nachvollziehen und habe auch keine hilfreichen Informationen ergooglen können.

Wer kann bei mir für Aufklärung sorgen?

Vielen Dank für jegliche Hinweise und Hilfe!

Robert Marquardt 1. Okt 2007 11:26

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Das ist die uebliche Verwechslung von Pointer auf Struktur und Struktur.

Delphi-Quellcode:
procedure InitOverlapped(var Overlapped: TOverlapped);
begin
  Overlapped.Offset := 0;
  Overlapped.OffsetHigh := 0;
  Overlapped.Internal := 0;
  Overlapped.InternalHigh := 0;
  Overlapped.hEvent := CreateEvent(nil,True,False,'');
end;
Der Eventname ist verdaechtig ein anonymer Event verwendet nil statt ''.
Ist ein ManualReset-Event noetig?

Hier setzt sich der Fehler fort.
Delphi-Quellcode:
function ReceiveCommand(...):..
var
  ...
  Overlapped: TOverlapped;
begin
  InitOverlapped(Overlapped);
  Ret := UsbBulkRead(deviceHandle, 0, 0, 0, @USBcb, 8, BytesReceived, @Overlapped);
Wird denn nach UsbBulkRead auf den Event gewartet? Wenn nicht wird die Funktion verlassen und Overlapped wird zerstoert. Es muss sichergestellt werden das Overlapped solange existiert bis UsbBulkRead den Event nicht mehr triggert.

Viktorii 1. Okt 2007 13:00

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Erstmal vielen Dank für die schnelle Antwort.

Zitat:

Zitat von Robert Marquardt
Das ist die uebliche Verwechslung von Pointer auf Struktur und Struktur.

Verstehe das nicht genau. Was genau bedeute Verwechslung?

Zitat:

Zitat von Robert Marquardt
Delphi-Quellcode:
procedure InitOverlapped(var Overlapped: TOverlapped);
begin
  Overlapped.Offset := 0;
  Overlapped.OffsetHigh := 0;
  Overlapped.Internal := 0;
  Overlapped.InternalHigh := 0;
  Overlapped.hEvent := CreateEvent(nil,True,False,'');
end;
Der Eventname ist verdaechtig ein anonymer Event verwendet nil statt ''.
Ist ein ManualReset-Event noetig?

Ich muss zugeben, dass ich mich damit noch nicht auseinandergesetzt habe, weil die Zugriffsverletzung schon bei 'Overlapped.Offset := 0;' auftritt. Habe dieses Codesegment so im Netz gefunden. Hab es erstmal so übernommen. Wie schon angedeutet, bin ich mit sowas noch nicht in Berührung gekommen. Leider hilft die Delphi Hilfe da auch nicht weiter :(

Zitat:

Zitat von Robert Marquardt
Hier setzt sich der Fehler fort.
Delphi-Quellcode:
function ReceiveCommand(...):..
var
  ...
  Overlapped: TOverlapped;
begin
  InitOverlapped(Overlapped);
  Ret := UsbBulkRead(deviceHandle, 0, 0, 0, @USBcb, 8, BytesReceived, @Overlapped);
Wird denn nach UsbBulkRead auf den Event gewartet? Wenn nicht wird die Funktion verlassen und Overlapped wird zerstoert. Es muss sichergestellt werden das Overlapped solange existiert bis UsbBulkRead den Event nicht mehr triggert.

Also mein Ansatz sollte folgender sein: Soweit ich das verstanden habe, wird die Funktion (UsbBulkRead) sofort wieder verlassen, wenn ich das Overlapped verwende. Nun möchte ich eine gewisse (Timeout-) Zeit nach dem Lesen warten und dann schauen ob gelesen wurde oder nicht. Ist das so zu realisieren oder habe ich da was falsch verstanden?

Nochmals vielen Dank!

Robert Marquardt 1. Okt 2007 17:36

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
LPOVERLAPPED ist in C ein "OVERLAPPED *", also ein Zeiger auf ein OVERLAPPED-Record. In Delphi gibt es OVERLAPPED als TOverlapped bereits. LPOVERLAPPED waere dann "type LPOVERLAPPED = ^TOverlapped;". Damit hast du nie ein Record deklariert und die Variable Overlapped ist ein uninitialisierter Zeiger und zeigt in den Wald. Natuerlich bekommst du damit eine Schutzverletzung.

Das mit dem warten ob etwas gelesen wurde, sollte sich realisieren lassen. SleepEx sollte genuegen. Es muss allerdings danach das UsbBulkRead (vermutlich mit CancelIo) abgebrochen werden, falls SleepEx den Timeout signalisiert. Korrektur: WaitForSingleObject.
Besser ist allerdings du schreibst einen Thread der das handhabt. Noch besser du findest jemanden der mit solchen Sachen vertraut ist. Ich bin es, aber ich habe keine Zeit.

Viktorii 2. Okt 2007 11:05

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Zitat:

LPOVERLAPPED ist in C ein "OVERLAPPED *", also ein Zeiger auf ein OVERLAPPED-Record. In Delphi gibt es OVERLAPPED als TOverlapped bereits. LPOVERLAPPED waere dann "type LPOVERLAPPED = ^TOverlapped;". Damit hast du nie ein Record deklariert und die Variable Overlapped ist ein uninitialisierter Zeiger und zeigt in den Wald. Natuerlich bekommst du damit eine Schutzverletzung.
Achsooooo.... LP steht für long pointer. :wall: Naja, wenn da lp anstatt LP gestanden hätte, wäre ich vielleicht sogar selber drauf gekommen. :oops:

Zitat:

Das mit dem warten ob etwas gelesen wurde, sollte sich realisieren lassen. SleepEx sollte genuegen. Es muss allerdings danach das UsbBulkRead (vermutlich mit CancelIo) abgebrochen werden, falls SleepEx den Timeout signalisiert. Korrektur: WaitForSingleObject.
Besser ist allerdings du schreibst einen Thread der das handhabt.
Ansonsten funktioniert mittlerweile die 'unelegantere' Lösung ohne einen neuen Thread fast. Da ich mit Thread Programmierung bis jetzt noch nicht wirklich in Berührung gekommen bin, möchte ich das Problem erstmal so lösen, bevor ich mich mit Threads wirklich auseinander setze.

Die jetzige Lösung funktioniert in sofern, dass die UsbBulkRead-Funktion sofort wieder verlassen wird und die Applikation solange an WaitForSingleObject wartet bis die Daten empfangen wurden bzw. der Timeout erreicht wurde. Werden die Daten gesendet, werden diese auch korrekt empfangen. Werden keine Daten gesendet und somit der Timeout erreicht, kann ich das am Rückgabewerte sehen. Soweit, so gut.

Nur das Abbrechen der UsbBulkRead Funktion mit CancelIO scheint nicht zu klappen. Der Rückgabewert ist FALSE. Mit GetLastError bekomme ich den Rückgabewert 6. Das bringt mich zu einer vielleicht etwas dummen Frage: Gibt es für Delphi irgendwo die defines bzw. 'Konstantenzuweisungen' für die Rückgabewerte? Habe bei WaitForSingleObject beispielsweise gesehen, dass WAIT_TIMEOUT = 258 sein müsste und 0 scheint wohl WAIT_OBJECT_0 zu sein. Bei C hat gibt es ja meistens Header Dateien, in denen die defines/macros für die Rückgabewerte eingetragen sind.

Leider bin ich mit diesen (Windows API ?) Funktionen auch noch nicht so vertraut. Woran könnte es liegen, dass es mit CancelIO nicht funktioniert? Ist es sicher, dass UsbBulkRead wie andere I/O Funktionen abgebrochen werden muss?

Zur Vollständigkeit, so sieht es im Moment bei mir aus (zwei mal Read, das Zweite mal nur wenn das Erste erfolgreich war):

Delphi-Quellcode:
procedure InitOverlapped(var Overlapped : lpOverlapped);
begin
  Overlapped.Offset := 0;
  Overlapped.OffsetHigh := 0;
  Overlapped.Internal := 0;
  Overlapped.InternalHigh := 0;
  Overlapped.hEvent := CreateEvent(nil,False,False,nil);
end;


function ReceiveCommand(...):Integer;
var
  ...
  lpUSBOverlapped : lpOverlapped;
  USBOverlapped  : TOverlapped;

begin
  lpUSBOverlapped := @USBOverlapped;

  InitOverlapped(lpUSBOverlapped);

  bRet := UsbBulkRead(deviceHandle, 0, 0, 0, @USBcb, HEADERSIZE, BytesReceived, lpUSBOverlapped);

  Ret := WaitForSingleObject(USBOverlapped.hEvent, TIMEOUT_VALUE);

  if (Ret = 0) and (USBOverlapped.InternalHigh = HEADERSIZE) then
  begin
    bRet := UsbBulkRead(deviceHandle, 0, 0, 0, Data, USBcb.nDATA, BytesReceived, lpUSBOverlapped);
    Ret := WaitForSingleObject(USBOverlapped.hEvent, TIMEOUT_VALUE);
  end
  else
  if Ret = 258 then
     bRet := CancelIO(USBOverlapped.hEvent);


  if (Ret = 0) and (USBOverlapped.InternalHigh = USBcb.nDATA) then
  begin
    ...
  end
  else
  begin
    Result := USB_RECEIVE_COMMAND_ERROR;
    if Ret = 258 then
      bRet := CancelIO(USBOverlapped.hEvent);
  end;
             
end;
Kann mir jemand Literatur zu Grundlagen für Thread- und API-Programmierung empfehlen? Habe bis jetzt das hier gefunden:

http://www.michael-puff.de/Developer...mit_Delphi.pdf
http://www.michael-puff.de/Developer...orials_pdf.pdf

Werde mich auf jeden Fall damit auseinander setzen.

Vielen Dank für Deine Geduld!

Robert Marquardt 2. Okt 2007 14:55

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Studier mal meine HID-Komponente. In der JVCL oder http://www.soft-gems.net
Dort wird ein Daten-Event mit einem Thread implementiert. Ist allerdings trickreich und fast undokumentiert.
Bei USB Bulk Reads ist uebrigens eine das performante Lesen wirklich trickreich, da man immer mehrere overlapped Reads ausstehen haben muss. Ich weiss nicht inwieweit die DLL dich da unterstuetzt.

Viktorii 4. Okt 2007 14:06

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Zwei Fragen habe ich noch:
1. Also, die HID Komponente ist doch sehr komplex und hilft mir nicht wirklich weiter. Nachdem ich mich jetzt in das Thema Threads am einarbeiten bin, würde ich folgenden Ansatz versuchen:

Ich verpacke einen Lesevorgang in einen separaten Thread:

Delphi-Quellcode:
  bRet := UsbBulkRead(deviceHandle, 0, 0, 0, @USBcb, HEADERSIZE, BytesReceived, lpUSBOverlapped);

  Ret := WaitForSingleObject(USBOverlapped.hEvent, TIMEOUT_VALUE);
Wenn Daten empfangen werden, wird ein Event ausgelöst und der Thread beendet sich. Werden keine Daten empfangen, läuft der Timeout ab und der Thread beendet sich auch. Muss ich in diesem Fall die UsbBulkRead Funktion nicht mehr abbrechen, weil der Thread dann nicht mehr exisitiert? Wäre das so der richtige Ansatz?

2. Die Geschichte mit den Rückgabewerten stört mich immer noch. Beispielsweise der Rückgabewert 6 von GetLastError, wie oben erwähnt. Gibt es eine Möglichkeit herauszufinden, was das genau bedeutet? Finde es auch unelegant die Rückgabewert als 'Magic Values', beispielsweise auf 44 zu prüfen, anstatt auf XYZ_ERROR....

Nochmals vielen Dank.

Robert Marquardt 4. Okt 2007 14:21

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Ich gebe auf. erstens fuehle ich mich gerade mal wieder ziemlich schlecht und zweitens fehlt dir offensichtlich zu viel an Grundlagen.
Kann jemand anders uebernehmen?

Viktorii 5. Okt 2007 07:20

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Zitat:

Zitat von Robert Marquardt
Ich gebe auf. erstens fuehle ich mich gerade mal wieder ziemlich schlecht

Das tut mir leid.


Zitat:

Zitat von Robert Marquardt
und zweitens fehlt dir offensichtlich zu viel an Grundlagen.

Die versuche ich mir ja gerade anzueignen, wie ich weiter oben ja schon gepostet habe. Jeder hat ja mal angefangen.


Zitat:

Zitat von Robert Marquardt
Kann jemand anders uebernehmen?

Würde sich dann vielleicht noch jemand erbarmen, einem armen wissbegierigen Delphi Neuling ein kleines bisschen unter die Arme zu greifen :?:

Ich bedanke mich schon mal im Voraus :!:

Viktorii 8. Okt 2007 07:46

Re: Zugriffsverletzung bei(Kommunikations)Timeout mit overla
 
Kann mir da niemand weiterhelfen? Wäre sehr wichtig für mich :|


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:19 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