AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi pipes(read+write) zu mehr als einer Konsolenanwendung
Thema durchsuchen
Ansicht
Themen-Optionen

pipes(read+write) zu mehr als einer Konsolenanwendung

Ein Thema von Icyclemort · begonnen am 6. Mai 2006 · letzter Beitrag vom 8. Mai 2006
 
Icyclemort

Registriert seit: 3. Mai 2006
3 Beiträge
 
#3

Re: pipes(read+write) zu mehr als einer Konsolenanwendung

  Alt 8. Mai 2006, 21:01
So, ich habe das Problem selbst gefunden und behoben. (ursprünglich wurde ein Teil von [sa: tSECURITYATTRIBUTES] nicht gesetzt (vererbare handles), was hier zu Problemen führte.

Da ich den ursprünglichen Code kräfig aufgebohrt habe (nun unbeschränkt viele Threads mit voller Duplex-Verbindung zu den jeweiligen Konsolenanwendungen mit anonymen pipes), dachte ich mir, ich kann ja mal den Rest der Welt daran teilhaben lassen:




Zuerst die unit, die zum Zugriff auf die Funktionen der Threads dient. (der unbedarfte Anwender braucht sich nur mit dieser einen Konstante und den 3 folgenden Prozeduren zu beschäfigen):

Delphi-Quellcode:
unit AnonymousPipeChannels;

interface

uses
  AnonymousPipeThreads, Classes;

const
  MAX_NUMBER_OF_CHANNELS = 4; // Maximale Anzahl an Kanälen/zu startenden
                                      // Konsolenanwendungen einstellen


  procedure RunProcess(xyz: string; channel : integer); // Startet Anwendung XYZ auf dem angegeben Kanal

  procedure LineToSend(msg: string; channel : integer); // Sendet eine Nachricht an die Anwendunge, die
                                                          // auf dem angegebenen Kanal "lauscht"

  procedure LineReceived(msg: string; channel : integer); // Hier kommen Nachrichten von den Anwendunge an...
                                                          // Was damit passiert ist natürlich Eure Sache



var
  AnonymousPipeThread: array[1..MAX_NUMBER_OF_CHANNELS] of TAnonymousPipeThread;




implementation


procedure RunProcess(xyz: string; channel : integer);
begin
  AnonymousPipeThread[channel] := TAnonymousPipeThread.Create(true);
  AnonymousPipeThread[channel].Priority := tpLower; // Priorität könnte man hier ändern

  AnonymousPipeThread[channel].FileToRun := xyz;
  AnonymousPipeThread[channel].Channel := channel;
  AnonymousPipeThread[channel].LineOut:= LineReceived; // hier wird die Ausgabe-Prozedur festgelegt
                                                        // Man kann also für jeden Thread/Kanal/Anwendung
                                                        // eine andere Prozedur zur Ausgabe benutzen.

  AnonymousPipeThread[channel].Resume;
end;


procedure LineToSend(msg: string; channel : integer);
begin
  AnonymousPipeThread[channel].LineIn(msg);
end;


procedure LineReceived(msg: string; channel : integer);
begin
        // Hier muss die Eingabe der Konsolenanwendung (auf dem jeweiligen channel)
        // verarbeitet werden...
end;

end.



Es folgt das eigentlich Wesentliche:
Delphi-Quellcode:
unit AnonymousPipeThreads;

interface

uses
  Classes, Windows, Sysutils;

const
  CRLF=#13#10;
  bufsize=1024; // 1KByte buffer

type
  TAnonymousPipeThread = class(TThread)
  private
    Sendbuffer : string;
    FChannel : Integer;
    FFileToRun : string;
    procedure SetChannel(Value: Integer);
    procedure SetFileToRun(Value: string);
  protected
    procedure Execute; override;
    procedure SplitLines(s: string);
    function IsWinNT: Boolean;
  public
    LineOut: procedure(s: string; channel : integer); // Variable setzen um die Ausgaben der gestarteten
                                                       // Anwendungen auf eine (oder auch verschiedene)
                                                       // Prozedur(en) der mit der folgenden Syntax
                                                       // (AusgabeDerEngine; EngineIdentifier)
                                                       // umzuleiten.

    property Channel : Integer write SetChannel; // > id um mehrere gestarte Prozesse und deren
                                                       // Ausgaben zu unterscheiden...

    property FileToRun : string write SetFileToRun; // > diser Eigenschaft wird entnommen welche
                                                       // Anwendung der Thread starten soll
                                                       // (z.B. 'C:\Windows\System32\cmd.exe' )

    procedure LineIn (s: string); // Mit dem Aufruf dieser Prozedur sendet der Thread
                                                       // den String an die gestarte Konsolenanwendung
  end;




implementation



procedure TAnonymousPipeThread.Execute;
var
  newstdin, newstdout, read_stdout, write_stdin: tHandle;

  buf: array [0..bufsize-1] of char;

  si: tSTARTUPINFO;
  sa: tSECURITYATTRIBUTES;
  sd: tSECURITYDESCRIPTOR;
  pi: tPROCESSINFORMATION;

  bread, avail: dword;

  ProcessExitCode: Cardinal;
  LoopEnd: Boolean;
begin
  // Konfigurieren der Sicherheits-Attribute
  If IsWinNT then begin
    InitializeSecurityDescriptor(@sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(@sd, true, nil, false);
    sa.lpSecurityDescriptor:=@sd;
    sa.bInheritHandle:=true; //hier war der urspr. Fehler
  end else sa.lpSecurityDescriptor:=nil;
  // 1. Pipe erstellen
  If not CreatePipe(newstdin, write_stdin, @sa, 1024) then begin
    LineOut('Error creating first pipe', FChannel);
    exit;
  end;
  // 2. Pipe erstellen
  If not CreatePipe(read_stdout, newstdout, @sa, 1024) then begin
    LineOut('Error creating second pipe', FChannel);
    CloseHandle(newstdin);
    CloseHandle(write_stdin);
    exit;
  end;
  // StartupInfo für den zu startenden Prozess konfigurieren...
  GetStartupInfo(si);
  si.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  si.wShowWindow:=SW_HIDE;
  si.hStdOutput:=newstdout;
  si.hStdError:=newstdout;
  si.hStdInput:=newstdin;


  // Prozess (Konsolenanwendung) starten...
  If not CreateProcess(pchar(FFileToRun), nil, nil, nil, true, CREATE_NEW_CONSOLE, nil, pChar(ExtractFilePath(FFileToRun)), si, pi)
   then begin
    LineOut('Error creating process: '+ FFileToRun, FChannel);
    CloseHandle(newstdin);
    CloseHandle(newstdout);
    CloseHandle(read_stdout);
    CloseHandle(write_stdin);
    exit;
  end;
  // Loop (infinite till process quits)
  fillchar(buf, sizeof(buf), 0);
  LoopEnd:=false;
  Sendbuffer:='';
  Repeat
    Sleep(1);
    GetExitCodeProcess(pi.hProcess, ProcessExitCode);
    If (ProcessExitCode<>STILL_ACTIVE) then LoopEnd:=True;
    PeekNamedPipe(read_stdout, @buf, bufsize, @bread, @avail, nil);
    // eingehende Nachrichten
    If (bread<>0) then begin
      fillchar(buf, bufsize, 0);
      If (avail>bufsize) then
        While (bread>=bufsize) do begin
          ReadFile(read_stdout, buf, bufsize, bread, nil);
          SplitLines(buf);
          fillchar(buf, bufsize, 0);
        end
      else begin
        ReadFile(read_stdout, buf, bufsize, bread, nil);
        SplitLines(buf);
      end;
    end;
    // ausgehende Nachrichten
    While (Length(Sendbuffer)>0) do begin
      WriteFile(write_stdin, Sendbuffer[1], 1, bread, nil);
      Delete(Sendbuffer, 1, 1);
    end;
  until LoopEnd;
  // Aufräumarbeiten...
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
  CloseHandle(newstdin);
  CloseHandle(newstdout);
  CloseHandle(read_stdout);
  CloseHandle(write_stdin);
end;


procedure TAnonymousPipeThread.SplitLines(s: string);
var t: string;
begin
  While pos(#$A, s)<>0 do begin
    t:=copy(s, 1, pos(#$A, s)-1);
    LineOut(t, FChannel); // Ausgabe (von der Konsolenanwendung)
    delete(s, 1, pos(#$A, s));
  end;
  If length(s)>0 then LineOut(s, FChannel);
end;


procedure TAnonymousPipeThread.LineIn(s: string);
begin
  Sendbuffer:=Sendbuffer+s+CRLF; // Eingabe (an die Konsolenanwendung)
end;


procedure TAnonymousPipeThread.SetChannel(Value: Integer);
begin
  FChannel:=Value;
end;


procedure TAnonymousPipeThread.SetFileToRun(Value: string);
begin
  FFileToRun:=Value;
end;


function TAnonymousPipeThread.IsWinNT: Boolean;
var osv: tOSVERSIONINFO;
begin
  osv.dwOSVersionInfoSize:=sizeof(osv);
  GetVersionEx(osv);
  result:=osv.dwPlatformID=VER_PLATFORM_WIN32_NT;
end;

end.


Ich wünsche viel Spaß damit, mir hat es jedenfalls viel zu viel Mühe bereitet....




mfg,
Sebastian Leibnitz.
  Mit Zitat antworten Zitat
 


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 10:53 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz