|
Registriert seit: 3. Mai 2006 3 Beiträge |
#3
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. |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |