AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Named Pipe Problem bei mehreren Clients
Thema durchsuchen
Ansicht
Themen-Optionen

Named Pipe Problem bei mehreren Clients

Ein Thema von k4ni · begonnen am 24. Aug 2013
Antwort Antwort
k4ni

Registriert seit: 17. Jul 2007
Ort: Ulm
258 Beiträge
 
Delphi 7 Enterprise
 
#1

Named Pipe Problem bei mehreren Clients

  Alt 24. Aug 2013, 01:33
Guten Tag,

Ich schlage mich gerade mit einem merkwürdigen Problem rum..und zwar:
Ich habe eine DLL in c++ und eine Anwendung in delphi.
Dieses zwei sollen mittels Pipes kommunizieren.
In diesem Fall ist die Anwendung der Server und die DLL der/die Client/s.

Server-Klasse:
Delphi-Quellcode:
unit TPipeServerU;

interface
uses Classes, Windows, SysUtils, Dialogs;


type
  TPipeMessage = record
    size : DWORD;
    dataSize : DWORD;
    header : Byte;
    subheader : Byte;
    data : array[0..511] of char;
  end;

  TPipeServer = class(TThread)
    private
      FHandle: THandle;
      FPipeName: String;
    protected
    public
      constructor CreatePipeServer(aServer, aPipe: String; StartServer: Boolean);
      destructor Destroy; override;

      procedure StartUpServer;
      procedure ShutDownServer;
      procedure Execute; override;
   end;

  TPipeClient = class(TThread)
    private
      FHandle: THandle;
    protected
    public
      constructor CreatePipeClient(handle: THandle);
      procedure Execute; override;
  end;



const
  cShutDownMsg = 'shutdown pipe ';
  cPipeFormat = '\\%s\pipe\%s';


implementation


constructor TPipeServer.CreatePipeServer(
  aServer, aPipe: String; StartServer: Boolean
);
begin
  if aServer = 'then
    FPipeName := Format(cPipeFormat, ['.', aPipe])
  else
    FPipeName := Format(cPipeFormat, [aServer, aPipe]);
  // clear server handle
  FHandle := INVALID_HANDLE_VALUE;
  if StartServer then
    StartUpServer;
  // create the class
  Create(not StartServer);
end;

destructor TPipeServer.Destroy;
begin
  if FHandle = INVALID_HANDLE_VALUE then
    // must shut down the server first
    ShutDownServer;
  inherited Destroy;
end;

procedure TPipeServer.Execute;
var
  I, Written: Cardinal;
  InMsg, OutMsg: TPipeMessage;
begin

  while not Terminated do
  begin
    if FHandle = INVALID_HANDLE_VALUE then
    begin
      // suspend thread for 250 milliseconds and try again
      Sleep(250);
    end else begin
      if ConnectNamedPipe(FHandle, nil) then
        TPipeClient.CreatePipeClient(FHandle);
    end;
  end;
end;

procedure TPipeServer.ShutDownServer;
var
  BytesRead: Cardinal;
  OutMsg, InMsg: TPipeMessage;
  ShutDownMsg: String;
begin
  if not FHandle = INVALID_HANDLE_VALUE then
  begin
    {
    // server still has pipe opened
    OutMsg.Size := SizeOf(OutMsg);
    // prepare shut down message
    with InMsg do
    begin

      Kind := 0;
      ShutDownMsg := cShutDownMsg + FPipeName;
      Count := Succ(Length(ShutDownMsg));
      StrPCopy(Data, ShutDownMsg);

      //
    end;
    CalcMsgSize(InMsg);
    // send shut down message
    CallNamedPipe(
      PChar(FPipeName), @InMsg, InMsg.Size, @OutMsg, OutMsg.Size, BytesRead, 100
    );
    }

    // close pipe on server
    CloseHandle(FHandle);
    // clear handle
    FHandle := INVALID_HANDLE_VALUE;
  end;
end;

procedure TPipeServer.StartUpServer;
begin
  // check whether pipe does exist
  if WaitNamedPipe(PChar(FPipeName), 100 {ms}) then
    raise Exception.Create('Requested PIPE exists already.');
  // create the pipe
  FHandle := CreateNamedPipe(
    PChar(FPipeName), PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES, SizeOf(TPipeMessage), SizeOf(TPipeMessage),
    NMPWAIT_USE_DEFAULT_WAIT, nil
  );
  // check if pipe was created
  if FHandle = INVALID_HANDLE_VALUE then
    raise Exception.Create('Could not create PIPE.');
end;

{ TPipeClient }

procedure TPipeClient.Execute;
var
  I, Written: Cardinal;
  InMsg: TPipeMessage;
  fSucess: Boolean;
begin
  fSucess := false;
  //ShowMessage('Client executing');
  while(true) do
  begin

        InMsg.Size := SizeOf(InMsg);
        fSucess := ReadFile(FHandle, InMsg, InMsg.Size, InMsg.Size, nil);
        if(fSucess) then
        begin
          if (InMsg.header = 01) then
          begin
            if (InMsg.subheader = 02) then
              ShowMessage('Size: '+IntToStr(InMsg.dataSize)+' Msg[0]: '+ IntToHex(integer(InMsg.data[511]), 2));
          end;
        end;
        Sleep(100);
  end;
end;

constructor TPipeClient.CreatePipeClient(handle: THandle);
begin
  FHandle := handle;
  Create(false);
end;


end.
Die C++ DLL:
Code:
DWORD WINAPI Pipe_Thread( LPVOID lpParam )
{
   HANDLE hPipe;
   LPTSTR lpvMessage=TEXT("Default message from client.");
   TCHAR chBuf[512];
   BOOL  fSuccess = FALSE;
   DWORD cbRead, cbToWrite, cbWritten, dwMode;
   LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\testit");
   TPipeMessage Packet;
   
while (1)
   { 
      hPipe = CreateFile( 
         lpszPipename,  // pipe name
         GENERIC_READ |  // read and write access
         GENERIC_WRITE,
         0,             // no sharing
         NULL,          // default security attributes
         OPEN_EXISTING, // opens existing pipe
         0,             // default attributes
         NULL);         // no template file
 
   // Break if the pipe handle is valid.
 
      if (hPipe != INVALID_HANDLE_VALUE)
         break;
 
      // Exit if an error other than ERROR_PIPE_BUSY occurs.
      if (GetLastError() != ERROR_PIPE_BUSY)
      {
         
         //_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
         return -1;
      }
 
      // All pipe instances are busy, so wait for 20 seconds.
 
      if ( ! WaitNamedPipe(lpszPipename, 20000))
      { 
         MessageBox(HWND_DESKTOP,"Blabla2","Titel",MB_OK);
         //printf("Could not open pipe: 20 second wait timed out.");
         return -1;
      } 
   } 
 
// The pipe connected; change to message-read mode.
   dwMode = PIPE_READMODE_MESSAGE;
   fSuccess = SetNamedPipeHandleState( 
      hPipe,   // pipe handle
      &dwMode, // new pipe mode
      NULL,    // don't set maximum bytes
      NULL);   // don't set maximum time
   if ( ! fSuccess)
   {
     // _tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
      return -1;
   }

// Send a message to the pipe server.
 
   cbToWrite = sizeof(Packet);
   //Packet.size = sizeof(Packet);
   Packet.data[0] = 0xAA;
   Packet.data[511] = 0xBB;
   Packet.dataSize = 512;
   Packet.header = 0x01;
   Packet.subheader = 0x02;



   fSuccess = WriteFile( 
      hPipe,                 // pipe handle
      &Packet,               // message
      cbToWrite,             // message length
      &cbWritten,            // bytes written
      NULL);                 // not overlapped

    sprintf(chBuf, "Bytes sent %i", cbWritten);
   MessageBox(HWND_DESKTOP,chBuf,"SsS2",MB_OK);

   if ( ! fSuccess)
   {
      //_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
      return -1;
   }

   //printf("\nMessage sent to server, receiving reply as follows:\n");
 /*
   do
   { 
   // Read from the pipe.
 
      fSuccess = ReadFile( 
         hPipe,   // pipe handle
         chBuf,   // buffer to receive reply
         512*sizeof(TCHAR), // size of buffer
         &cbRead, // number of bytes read
         NULL);   // not overlapped
 
      if ( ! fSuccess && GetLastError() != ERROR_MORE_DATA )
         break;
 
   } while ( ! fSuccess); // repeat loop if ERROR_MORE_DATA

   if ( ! fSuccess)
   {
   
      //_tprintf( TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError() );
      return -1;
   }

   printf("\n<End of message, press ENTER to terminate connection and exit>");
   _getch();
 */
   while(hPipe);
   CloseHandle(hPipe);
 
   return 0;
}
Das Problem ist, wenn ich mit einem 2. Client Versuche zu verbinden scheitert es am
Code:
if ( ! WaitNamedPipe(lpszPipename, 20000))
Wo ist mein Fehler?
Ich hab mir die Multithread-Server Idee von MSDN geklaut.
Dort wird jedesmal nach ConnectNamedPipe() ein neuer Thread erstellt, der thread wartet dann auf nachrichten vom jeweiligen Client.
Der Thread erhält den hPipe parameter und nutzt dieses um auf nachrichten des client abzuwarten bzw diese mittels ReadFile() auszulesen.

Doch der aufruf von ConnectNamedPipe() ändert ja nichts am hPipe handle..so wundere ich mich wie solch ein server-thread den client überhaupt identifizieren kann.

Liebe Grüße
Timo Maier


Edit:
Außerdem ist mir aufgefallen, dass das Empfangen der Nachricht nur sporadisch funktioniert. Manchmal wird beim empfangen der nachricht auf der Serveranwendung in der ShowMessage() nur ein leeres Fenster angezeigt. Doch die header und subheader prüfung funktioniert...

Edit2: Ok hat sich erledigt. Hab übersehen das man für jeden Client ein neues PipeHandle via CreateNamedPipe() erstellen muss.

Geändert von k4ni (24. Aug 2013 um 09:38 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:51 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