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 NamedPipe @Buffer[0] exception (https://www.delphipraxis.net/138764-namedpipe-%40buffer%5B0%5D-exception.html)

Corelgott 17. Aug 2009 10:09


NamedPipe @Buffer[0] exception
 
Hi @ all,

ich quäle mich schon seit Tagen mit der Kommunikation per Named Pipes. An sich ist das ja nicht weiter schwer aber:
Ich habe das Problem, dass ich grundsätzlich beim 2. Versuch von Client zum Host etwas zu übermitteln eine Exception bekomme...

Mein Host sieht so aus:

Delphi-Quellcode:
function TPipeHost.createConnection(pipeName: string): HWND;
var
  FSA  : SECURITY_ATTRIBUTES;
  FSD  : SECURITY_DESCRIPTOR;
  verbs : Cardinal;
  Mode : Cardinal;

begin
  self.FPipeName := pipeName;

  verbs := PIPE_TYPE_BYTE or PIPE_WAIT or PIPE_READMODE_BYTE;

  InitializeSecurityDescriptor(@FSD, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@FSD, True, nil, False);
  FSA.lpSecurityDescriptor := @FSD;
  FSA.nLength := sizeof(SECURITY_ATTRIBUTES);
  FSA.bInheritHandle := True;
  result := CreateNamedPipe(PChar('\\.\pipe\' + pipeName), PIPE_ACCESS_DUPLEX, verbs,
                            PIPE_UNLIMITED_INSTANCES, buffersize,
                            buffersize, NMPWAIT_USE_DEFAULT_WAIT, nil);
  if isConsole then
    writeln(Format('Created Pipe with handle: %d', [result]))

end;
Der Client macht dann folgendes:

Delphi-Quellcode:
function TPipeClient.createConnection(pipeName: string): HWND;
var
   FSA : SECURITY_ATTRIBUTES;
   FSD : SECURITY_DESCRIPTOR;
begin
   InitializeSecurityDescriptor(@FSD, SECURITY_DESCRIPTOR_REVISION);
   SetSecurityDescriptorDacl(@FSD, True, nil, False);
   FSA.lpSecurityDescriptor := @FSD;
   FSA.nLength := sizeof(SECURITY_ATTRIBUTES);
   FSA.bInheritHandle := True;

   self.FPipeName := pipeName;

   result := CreateFile(PChar('\\.\pipe\' + pipeName),
                     GENERIC_READ or GENERIC_WRITE,
                     PIPE_TYPE_BYTE,
                     @FSA,
                     OPEN_EXISTING,
                     0,
                     0);
  if isConsole then
    writeln(Format('Connected to Pipe with handle: %d', [result]))
end;
Der Host läuft in seinem eigenen thread und überprüft ob Daten vorhanden sind via:

Delphi-Quellcode:
function TCustomPipe.dataAvailable: Cardinal;
var
  readCount : LongWord;
  rest     : LongWord;

begin
  result   := 0;
  rest     := 0;
  readCount := 0;

  // get data from pipe
  if self.hasValidHandle() then
    PeekNamedPipe(self.getHandle(), @self.FBuffer[0], BufferSize, @readCount, @rest, nil);

  result := rest;
end;
Nun kommt das problem: das auslesen:
Delphi-Quellcode:

type TPipeHost
  strict private
    FBuffer    : TByteDynArray;

// [.... snip .... ]


procedure TCustomPipe.cleanBuffer();
begin
  // resize & init
  setLength(FBuffer, buffersize);
  FillChar(self.FBuffer[0], length(self.FBuffer), #0);
end;

// method to read data
procedure TCustomPipe.readData(var data : TByteList);
var
  dataCount : Cardinal;
  dataRead : Cardinal;
  callBack : Pointer;

begin    
  dataRead := 0;
  dataCount := self.dataAvailable();

  // hwnd <> 0 && hwnd <> INVALID_HANDLE
  if self.hasValidHandle then
  begin
    EnterCriticalSection(self.FSection);

    // got data?
    while(dataCount > 0) do
    begin
      try

        self.cleanBuffer();

        (* Hier kommt das Problem: ReadFile verändert mir die Größe meines Dyn-Array (!?!) und ne Access-Voilation-Exception (Was ein wunder, wenn die API das Array kaputt macht...)
           Sowie ich self.FBuffer[0] übergebe bekomme ich 1. Nur noch mist an Daten & 2. Ne tolle Exception
        *)
        ReadFile(self.getHandle(), self.FBuffer, dataCount, dataRead, nil);

        // process data

        // look if there is any new data left
        dataCount := self.dataAvailable();
      finally
        // dispose buffer
        setLength(buffer, 0);
      end;
    end;
    LeaveCriticalSection(self.FSection);
  end;
end;
Was Mache ich falsch? Darf ich kein Array of Byte übergeben? Was ich bei dem Thread nicht tue ist nach jedem Lesen die datei wieder zu schließen... Wie das in so vielen Beispielen gemacht wird...

Hat wer nen Tipp für mich? Oder vielleicht ein Beispiel? Ich hatte leider bei Lunkie nix gefunden und auch meine Google Exkursionen waren nicht gerade erfolgreich. Die Forum-Suche brachte auch nichts zu tage, was meinen effekt auch nur ansatzweise erklären könnte?

Schon mal vielen Dank für's Nachdenken im Voraus!

Gruß

Corelgott

DeddyH 17. Aug 2009 10:12

Re: NamedPipe @Buffer[0] exception
 
Zitat:

Delphi-Quellcode:
procedure TCustomPipe.cleanBuffer();
begin
  // resize & init
  setLength(FBuffer, buffersize);
  FillChar(self.FBuffer[0], length(self.FBuffer), #0);
end;

Müsste das nicht eher
Delphi-Quellcode:
procedure TCustomPipe.cleanBuffer();
begin
  // resize & init
  setLength(FBuffer, buffersize);
  FillChar(self.FBuffer, length(self.FBuffer), #0);
end;
heißen?

himitsu 17. Aug 2009 10:20

Re: NamedPipe @Buffer[0] exception
 
@FillChar(Self.FBuffer[0]:
nein, ist schon OK so, denn es handelt sich ja um ein dynamisches Array und da würde ohne die [0] auf den Array-Zeiger und nicht auf den Datenbereich gezeigt.

welches genau da Progleme macht ... also ohne die [0] :zwinker:
Delphi-Quellcode:
ReadFile(getHandle(), FBuffer[0], ...

PS: warum verwendest du eigentlich so oft Self?
wo die darüber referenzierten Namen doch eh in selben/aktuellen Namespace liegen und es keine überlagerten Namen an diesen Stellen gibt.

und das Ganze dann auch noch so inkonsequent ... also wenn schon, dann sollte man sowas schon durchgängig gleich behandeln.
Delphi-Quellcode:
procedure TCustomPipe.cleanBuffer();
begin
  // resize & init
  setLength({self.}FBuffer, buffersize); <<<<<<<
  FillChar(self.FBuffer[0], length(self.FBuffer), #0);
end;

Corelgott 17. Aug 2009 10:53

Re: NamedPipe @Buffer[0] exception
 
Hi,

erst mal danke für die Antworten!

wegen meines Self-Fimmels, ich weiss, dass es nicht nötig ist, aber ich habe es mir angewöhnt aus anderes Aprachen wie PHP immer den Scope explizit zu benennen. Dadurch wird das lesen, finde ich zumindest, einfacher und klarer. Gerade wegen der Unart in Delphi With Blöcke zu benutzen... (Wie gesagt, ist ein Tick von mir </Delphi_Synatx_bashing>)

Was das self.FBuffer[0] angeht: Das Problem ist, sowie man das macht kommen nur noch unsinnige Daten, die nichts mehr mit dem zu tun haben, was per WriteFile geschickt wurde (+ auch eine Access Violation)

Deinen Ansatz kann ich durchaus verstehen, würde für mich auch mehr Sinn machen wenn man self.FBuffer[0] übergeben würde. Eben eine Ref auf das erste Element des Arrays... Nichts desto weniger wird es dann nur noch abenteurlicher...

Danke erst mal für die Antworten! Ich bin gerne noch für weitere Ideen offen!

Gruß

Corelgott

himitsu 17. Aug 2009 11:06

Re: NamedPipe @Buffer[0] exception
 
Wie gesagt, ohne die [0] schreibst du, bei einem dynamischen Array, nur in den Speicher des internen Array-Zeigers, welcher ja nur 4 Byte groß ist (Pointer) und die restlichen Daten überschreiben dann entweder andere Variablen/Daten und/oder versuchen in einen nicht reservierten Speicherblock zu schreiben, welches natürlich in einer Exception endet.

Zitat:

Zitat von Corelgott
Was das self.FBuffer[0] angeht: Das Problem ist, sowie man das macht kommen nur noch unsinnige Daten, die nichts mehr mit dem zu tun haben, was per WriteFile geschickt wurde (+ auch eine Access Violation)

tja, dann bleiben grundsätzlich noch 2 Progleme, entweder der Puffer ist zu klein und es wird drüberrausgeschrieben, oder mit der Array-Variable stimmt etwas nicht.

ich würde da wohl eher mal auf Ersteres tippen :angel2:
> also schau einfach mal, wie sich dataCount und buffersize zueinander verhalten




Nja, für mich ist ein übermaß an Self und dergleichen wieder zu unübersichtlich :angel2: ,
aber es ist auch nicht verkehrt, aber wenn schon, dann sollte man wenigstens es überall gleich machen (also in deinem Fall praktisch immer Self. verwenden und nicht mal so und mal so, denn das ist ja im Grunde total unübersichtlich)

Corelgott 17. Aug 2009 11:43

Re: NamedPipe @Buffer[0] exception
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hi,

**hust** joar man sollte das schon Konsitent machen und nicht nur machmal so mit self und so...
Hast ja recht...

Ich denke ich lege mal ne Ausgedünnte Version des Projektes bei... Vielleicht findet sich dann noch mehr Ansatz.

Wie gesagt, das es ist nur ein Test / Versuchs-Projekt... Also bitte nicht wegen der mangelden Kommentare meckern ;)

Gruß

Corelgott

himitsu 17. Aug 2009 12:02

Re: NamedPipe @Buffer[0] exception
 
hab jetzt grad nicht die Zeit/Möglichkeit hier auch noch etwas zu testen, aber versuch du doch erstmal dieses in TCustomPipe.readData:
Delphi-Quellcode:
self.cleanBuffer();
if dataCount > Length(self.FBuffer) then
  Raise Exception.CreateFmt('FBuffer ist zu klein (%d < %d)',
    [Length(self.FBuffer), dataCount]);
ReadFile(self.getHandle(), self.FBuffer, dataCount, dataRead, nil);
dataCount := self.dataAvailable();
PS: du hast in der Prozedur zwar den Parameter data definiert, weißt diesem aber nix zu :zwinker:

und es ist schon etwas "unökonomisch", wenn innerhalb der Leseschleife vor jede und nach jedem Lesedurchgang der Puffer neu erstellt und wieder freigegeben wird (wenn dieser eine feste Größe hat, dann reicht es doch auch vor/nach der Schleife und nicht immer wieder innerhalb Dieser)

Corelgott 17. Aug 2009 12:25

Re: NamedPipe @Buffer[0] exception
 
Liste der Anhänge anzeigen (Anzahl: 1)
hi,

nun ja... data ist eine liste von objekten die ich per var in die routine geben... somit muss ich da nichts zuweisen, da ich ja mit dem schon existenten objekt arbeite.

Was die Buffergröße angeht: Die ist 512 Byte groß. Aber selbst wenn ich nur 13 Byte sende bekomme ich schon die Probleme.
Das es nicht so schön ist, wenn man den Buffer bei jedem Aufruf neu setzt ist unumstrittten. Aber selbst wenn ich das in ner konstankten Größe festlege undn ur einmal initialisiere habe ich die gleiche Problematik. Diese Variante ist auch mehr durch's Testen entstanden, als dass es später so gehandhabt werden soll.

Ich habe nun die Version mal auf einen "statischen" 512 Byte-Buffer umgeschrieben... was an dem Effekt nicht viel ändert. Bis auf die Tatsache, dass ich jetzt keine sinnigen Daten mehr bekomme...

Gruß

Corelgott


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