Autor |
Beitrag |
kraz
Beiträge: 24
|
Verfasst: Mi 29.10.03 21:10
Hallo, ich spiele gerade ein bischen mit dem edonkey-server herum.
Ich habe eine kleine oberfläche gemacht, mit der man die ini vom server anpassen kann.
Den Server starte ich so:
Delphi-Quelltext 1:
| ShellExecute(0, 'open', PChar(RootDir+'dserver.exe'), PChar(''), nil, SW_HIDE); |
wenn man den server in der eingabeaufforderung startet ist man in dem server-modus und kann dort z.b. mit der eingabe des buchstaben "g" den status abfagen ...
ich kann schon aufs fenster zugreifen ... aber wie komme ich an die ausgabe ?
unter postmessage und getmessage finde ich hier nichts womit ich was anfangen kann und mit der hilfe komme ich sowieso nie zurecht ...
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.Button3Click(Sender: TObject); var h: hwnd; begin
try h := FindWindowByTitle(RootDir+'dserver.exe'); if h <> 0 then except end; end; |
wenn ich es so nicht hinbekomme muss ich auf die logdatei zugreifen ...
denke aber nicht das das ne saubere lösung ist ...
Danke im Vorraus !!
Moderiert von Tino: Delphi-Tags hinzugefügt.
_________________ Man muss schnell laufen, um dort zu bleiben wo man ist ...
|
|
jaenicke
Beiträge: 19289
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 30.10.03 08:33
Also, zunächst mal: Ich hab so was auch schon gebraucht, da hab ich das auch benutzt:
Der folgende Quelltext ist von Martin Strohal:
(Delphi-Source.de)
www.delphi-treff.de/...m-programm-anzeigen/
Im folgenden steht eine Funktion, die eine Kommandozeile aufruft und die Ausgabe, sowie Fehler in eine Stringliste schreibt.
Danach folgt noch ein Beispiel für die Benutzung der Funktion.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:
| function GetConsoleOutput(const Command: String; var Output, Errors: TStringList): Boolean; var StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; SecurityAttr: TSecurityAttributes; PipeOutputRead: THandle; PipeOutputWrite: THandle; PipeErrorsRead: THandle; PipeErrorsWrite: THandle; Succeed: Boolean; Buffer: array [0..255] of Char; NumberOfBytesRead: DWORD; Stream: TMemoryStream; begin FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0); SecurityAttr.nLength := SizeOf(SecurityAttr); SecurityAttr.bInheritHandle := true; SecurityAttr.lpSecurityDescriptor := nil;
CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0); CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);
FillChar(StartupInfo, SizeOf(TStartupInfo), 0); StartupInfo.cb:=SizeOf(StartupInfo); StartupInfo.hStdInput := 0; StartupInfo.hStdOutput := PipeOutputWrite; StartupInfo.hStdError := PipeErrorsWrite; StartupInfo.wShowWindow := sw_Hide; StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
if CreateProcess(nil, PChar(command), nil, nil, true, CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then begin result:=true; CloseHandle(PipeOutputWrite); CloseHandle(PipeErrorsWrite);
Stream := TMemoryStream.Create; try while true do begin succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil); if not succeed then break; Stream.Write(Buffer, NumberOfBytesRead); end; Stream.Position := 0; Output.LoadFromStream(Stream); finally Stream.Free; end; CloseHandle(PipeOutputRead);
Stream := TMemoryStream.Create; try while true do begin succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil); if not succeed then break; Stream.Write(Buffer, NumberOfBytesRead); end; Stream.Position := 0; Errors.LoadFromStream(Stream); finally Stream.Free; end; CloseHandle(PipeErrorsRead);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE); CloseHandle(ProcessInfo.hProcess); end else begin result:=false; CloseHandle(PipeOutputRead); CloseHandle(PipeOutputWrite); CloseHandle(PipeErrorsRead); CloseHandle(PipeErrorsWrite); end; end; |
Zitat: | Aufrufbeispiel:
Für die Ausgabe und die Fehler wird zunächst eine Stringliste erzeugt. Anschließend wird obige Funktion aufgerufen. Ist sie erfolgreich ausgeführt wurden (Rückgabewert ist true), wird die Output-Stringliste in einem Memo angezeigt.
Um Kommandozeilenbefehle ausführen zu können, die keine eigenständigen Anwendungen sind (wie der DOS-Befehl "dir" im folgenden Beispiel) muss der Name des Kommandozeileninterpreters davor stehen. "cmd.exe" ist das unter Windows NT/2000/XP und "command.com" unter Windows 9x. Der Parameter /c sorgt dafür, dass der Kommandozeilenbefehl ausgeführt und die Kommandozeile anschließend wieder geschlossen wird.
|
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TForm1.Button1Click(Sender: TObject); var output, errors: TStringList; begin output:=TStringList.Create; try errors:=TStringList.Create; if GetConsoleOutput('cmd /c dir c:\', output, errors) then Memo1.Lines.AddStrings(output); finally output.free; errors.free; end; end; |
Grüße, S.J.
// EDIT: Link aktualisiert
Zuletzt bearbeitet von jaenicke am Mo 05.07.10 21:36, insgesamt 2-mal bearbeitet
|
|
kraz
Beiträge: 24
|
Verfasst: Fr 31.10.03 08:28
Titel: danke !
Hallo
Ich habe das obrige mal getestet und funktioniert wunderbar mit dir ...
aber bei der dserver.exe bekomme ich nichts in memo rein.
ich denke das ist, weil die funktion keinen true wert liefert bis der server wieder geschlossen wird.
Nach dem start von dserver befindet man sich in einem eingabemodus:
Der Befehl "g" liefert z.b. die userzahl und anzahl der files ...
Außerdem gelangt man damit ja nicht an den laufenden Prozess ...
Der server würde nochmal gestartet und ich erhielte natürlich dann die werte des 2. - neuen servers : user=0 files=0
irgendwie muss ich doch an den laufenden prozess kommen ...
also einem laufenden fenster nen befehl zusenden und ausgabe auslesen.
Ich hoffe da gibts noch ne andere möglichkeit ...
_________________ Man muss schnell laufen, um dort zu bleiben wo man ist ...
|
|
kraz
Beiträge: 24
|
Verfasst: Fr 31.10.03 08:35
kann ich das hier irgendwie für meine zwecke verwenden ?
hab ich unter "netsend nachricht abfangen" gefunden ...
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TForm1.Button1Click(Sender: TObject); var hWnd, hLabel: Cardinal; buffer: array[0..255] of Char; begin hWnd := FindWindow(nil, 'Nachrichtendienst '); hLabel := GetDlgItem(hWnd, $0000FFFF); SendMessage(hLabel, WM_GETTEXT, sizeof(buffer), Integer(@buffer)); ShowMessage(string(buffer)); end; |
Moderiert von Tino: Delphi-Tags hinzugefügt.
_________________ Man muss schnell laufen, um dort zu bleiben wo man ist ...
|
|
jaenicke
Beiträge: 19289
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 15.11.03 13:54
Titel: Ich hab da was...
Hallo!
Ich hab durch Zufall hier im Forum als ich selbst was gesucht habe genau das gefunden, das das macht!
Das funktioniert im Grunde so wie der Quelltext, den ich gepostet hatte!
Die folgende Komponente stammt von maxk!
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227:
|
unit ConsoleSession;
interface
uses Windows, Messages, SysUtils, Classes, ExtCtrls;
type TGotBytesEvent=procedure(Sender:TObject;Buffer:string) of object; TConsoleSession = class(TComponent) constructor Create(AOwner:TComponent); override; destructor Destroy; override; procedure Write(Buffer:string); procedure WriteLn(Buffer:string); function Running:boolean; procedure ReadFromPipe; procedure Execute; procedure Terminate(ExitCode:Cardinal=0); procedure WaitFor(TimeOut:Cardinal=0); function GetProcess:Cardinal; private FCommandLine:string; FReadBufferSize:Cardinal; FWriteBufferSize:Cardinal; FReadInterval:integer; FOnGotBytes:TGotBytesEvent; Timer:TTimer; hIReadPipe,hIWritePipe,hOReadPipe,hOWritePipe:THandle; ProcessInfo:TProcessInformation; procedure FReadFromPipe(Sender:TObject); procedure SetReadInterval(NewInterval:integer); procedure CleanUp; protected public published property CommandLine:string read FCommandLine write FCommandLine; property ReadBufferSize:Cardinal read FReadBufferSize write FReadBufferSize; property WriteBufferSize:Cardinal read FWriteBufferSize write FWriteBufferSize; property ReadInterval:integer read FReadInterval write SetReadInterval; property OnGotBytes:TGotBytesEvent read FOnGotBytes write FOnGotBytes; end;
procedure Register;
implementation
constructor TConsoleSession.Create(AOwner:TComponent); begin inherited Create(AOwner);
if csDesigning in ComponentState then begin MessageBox(0,'Please note that TConsoleSession isn''t able to run 16-Bit '+ 'applications under systems higher than Windows NT.'+#13+'When you have any'+ ' ideas so mail me: delphi_max@lycos.de - Thanks'+#13+'This message only '+ 'appears in design time.','Warning',MB_ICONEXCLAMATION); end;
FCommandLine:='cmd.exe'; FReadBufferSize:=256; FWriteBufferSize:=65535; Timer:=TTimer.Create(nil); Timer.OnTimer:=FReadFromPipe; SetReadInterval(100); end;
destructor TConsoleSession.Destroy; begin Timer.Free; if Running then TerminateProcess(ProcessInfo.hProcess,0); CleanUp; inherited Destroy; end;
procedure TConsoleSession.Write(Buffer:string); var BytesToWrite,BytesWritten:Cardinal; begin if not Running then raise Exception.Create(Name+': Command not running'); while length(Buffer)>0 do begin BytesToWrite:=length(Buffer); if BytesToWrite>FWriteBufferSize then BytesToWrite:=FWriteBufferSize; WriteFile(hIWritePipe,Buffer[1],BytesToWrite,BytesWritten,nil); Delete(Buffer,1,BytesWritten); end; end;
procedure TConsoleSession.WriteLn(Buffer:string); begin Write(Buffer+#13#10); end;
function TConsoleSession.Running:boolean; begin Result:=WaitForSingleObject(ProcessInfo.hProcess,0)=WAIT_TIMEOUT; end;
procedure TConsoleSession.ReadFromPipe; begin if not Running then raise Exception.Create(Name+': Command not running'); FReadFromPipe(nil); end;
procedure TConsoleSession.Execute; var SecAttr:TSecurityAttributes; StartupInfo:TStartUpInfo; begin if not Running then CleanUp else raise Exception.Create(Name+': Already running');
with SecAttr do begin nLength:=SizeOf(TSecurityAttributes); bInheritHandle:=true; lpSecurityDescriptor:=nil; end;
if not CreatePipe(hIReadPipe,hIWritePipe,@SecAttr,0) then raise Exception.Create(Name+': Unable to create write pipe'); if not Createpipe(hOReadPipe,hOWritePipe,@SecAttr,0) then raise Exception.Create(Name+': Unable to create read pipe');
FillChar(StartupInfo,Sizeof(StartupInfo),#0); with StartupInfo do begin cb:=SizeOf(StartupInfo); hStdOutput:=hOWritePipe; hStdInput:=hIReadPipe; dwFlags:=STARTF_USESTDHANDLES+STARTF_USESHOWWINDOW; wShowWindow:=SW_HIDE; end;
if not CreateProcess(nil,PChar(FCommandLine),@SecAttr,@SecAttr,True, NORMAL_PRIORITY_CLASS,nil,nil,StartupInfo,ProcessInfo) then raise Exception.Create(Name+': Unable to start command.'+#13+'Error: '+ IntToStr(GetLastError)); end;
procedure TConsoleSession.Terminate(ExitCode:Cardinal=0); begin if not Running then raise Exception.Create(Name+': Command not running'); TerminateProcess(ProcessInfo.hProcess,ExitCode); CleanUp; end;
procedure TConsoleSession.WaitFor(TimeOut:Cardinal=0); var Tick:Cardinal; begin Tick:=GetTickCount; while (Tick+TimeOut>GetTickCount) and (Running) and (TimeOut=0) do begin Sleep(1); end; end;
function TConsoleSession.GetProcess:Cardinal; begin if not Running then raise Exception.Create(Name+': Command not running'); Result:=ProcessInfo.hProcess; end;
procedure TConsoleSession.FReadFromPipe(Sender:TObject);
function BytesInPipe:Cardinal; begin if not PeekNamedPipe(hOReadPipe,nil,0,nil,@Result,nil) then Result:=0; end;
var BytesRead:DWord; Buffer:string; begin if (not Running) or (BytesInPipe=0) then exit; try Timer.Enabled:=False; repeat BytesRead:=0; SetLength(Buffer,FReadBufferSize); ReadFile(hOReadPipe,Buffer[1],length(Buffer),BytesRead,nil); SetLength(Buffer,BytesRead); OemToAnsi(@Buffer[1],@Buffer[1]); if Assigned(FOnGotBytes) then FOnGotBytes(Self,Buffer); until BytesInPipe=0; finally Timer.Enabled:=True; end; end;
procedure TConsoleSession.SetReadInterval(NewInterval:integer); begin Timer.Interval:=NewInterval; FReadInterval:=Timer.Interval; end;
procedure TConsoleSession.CleanUp; begin if ProcessInfo.hProcess<>0 then CloseHandle(ProcessInfo.hProcess); if ProcessInfo.hThread<>0 then CloseHandle(ProcessInfo.hThread); if hOReadPipe<>0 then CloseHandle(hOReadPipe); if hOWritePipe<>0 then CloseHandle(hOWritePipe); if hIReadPipe<>0 then CloseHandle(hIReadPipe); if hIWritePipe<>0 then CloseHandle(hIWritePipe);
ProcessInfo.hProcess:=0; ProcessInfo.hThread:=0; hOReadPipe:=0; hOWritePipe:=0; hIWritePipe:=0; hOWritePipe:=0; end;
procedure Register; begin RegisterComponents('maxk', [TConsoleSession]); end;
end. |
Damit kannst Du Daten während der Ausführung eines DOS-Programms in die Konsole schreiben und lesen!
Viel Erfolg,
S.J.
|
|
oOXTCOo
Beiträge: 141
Windows XP Prof. 3
Delphi 7
|
Verfasst: Sa 15.01.11 07:40
ich würde genau sowas brauchen...
ich möchte laufend von einem dos programm, den inhalt auslesen (in einem intervall), ich muss nichts zum fenster senden, einfach nur im intervall lesen.
wie kann ich mit hilfe dieser komponente einfach den kompletten fenster hinhalt in einem intervall im memo anzeigen oder in einem string schreiben?
mit dieser komponente wird mir zwar der inhalt angezeigt, aber nur nach einiger wartezeit und in zwei teilen...
gibt es nicht eine einfachere möglichkeit einfach per fenster handle den inhalt aus einem dos fenster zu lese wie bei einem windows handle?
danke!
|
|
jaenicke
Beiträge: 19289
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 15.01.11 09:45
Was das Programm dann macht ist vermutlich den Ausgabecursor zu positionieren. Damit habe ich mich noch nie weiter beschäftigt das auszulesen.
Vorstellen kann ich mir mehrere Möglichkeiten, z.B. die entsprechende API zu hooken und das so mitzubekommen.
Ich habe das bei einem Programm, das auf diese Weise einen prozentualen Fortschritt ausgegeben hat, einfach per Logik entsprechend interpretiert. Weil da klar war welche Ausgabe wann kommt. Da ging es mir auch nicht um die Anzeige des Ergebnisses, sondern um dessen Aufbereitung.
oOXTCOo hat folgendes geschrieben : | gibt es nicht eine einfachere möglichkeit einfach per fenster handle den inhalt aus einem dos fenster zu lese wie bei einem windows handle? |
Nicht dass ich wüsste.
|
|
oOXTCOo
Beiträge: 141
Windows XP Prof. 3
Delphi 7
|
Verfasst: Sa 15.01.11 22:04
ich habe mir die sources genauer angesehen und das problem schein daran zu liegen das mein ziel dos programm auch nur den fortschritt anzeigt um dem es mir genau geht und den ich gerne in meinem programm anzeigen würde.
es funktioniert perfekt mit konsolen programmen die immer eine neue zeile erstellen, aber mein ziel konsolen programm positioniert den cursor immer neu und zeigt den neuen status immer in der selben zeile an, darum verute ich mal, wird das ergebnis immer nur dann angezeigt wenn der buffer voll ist oder das programm abgeschlossen hat.
kannst du mir einen tip geben wie das über die api direkt funktioniert?
oder was wäre wenn ich den buffer kleiner mache so das immer nur die grösse des letzten ergebnisses rein passt?
dann müsste ich doch erzwingen können, das immer der buffer ausgegeben wird.
ich kanns leider im moment nicht testen, bin gerade nicht bei meinem computer, aber ich könnte mir vorstellen das es funktioniert.
die letze idee die ich hätte wäre die konsolen anwendung zu patchen damit sie mir immer eine neue zeile ausgiebt.
aber das wollte ich vermeiden, da diese anwendung nicht von mir ist und ich die nicht verändern dürfte.
|
|
oOXTCOo
Beiträge: 141
Windows XP Prof. 3
Delphi 7
|
Verfasst: So 16.01.11 04:32
problem über die win api gelöst!
wer selbiges problem hat: entwickler-forum.de/...ht=ReadConsoleOutput (funktioniert bestens!)
|
|
hjl
Beiträge: 22
Erhaltene Danke: 2
Windows 10 Enterprise (64 Bit)
Delphi 7, Delphi XE2, Delphi Berlin (10.1), Delphi Tokyo (10.2)
|
Verfasst: Do 27.10.16 12:11
jaenicke hat folgendes geschrieben : | Also, zunächst mal: Ich hab so was auch schon gebraucht, da hab ich das auch benutzt:
Der folgende Quelltext ist von Martin Strohal:
(Delphi-Source.de)
www.delphi-treff.de/...m-programm-anzeigen/
Im folgenden steht eine Funktion, die eine Kommandozeile aufruft und die Ausgabe, sowie Fehler in eine Stringliste schreibt.
Danach folgt noch ein Beispiel für die Benutzung der Funktion.
... |
Von diesem Quelltext von Martin Strohal habe ich auch abgeschaut.
Es geht mir aber nur um die Ausführung eines bestimmten Programmes ohne Benutzeroberfläche (es kann auch mit Benutzeroberfläche laufen und hat für den Lauf ohne Benutzeroberfläche eine eigenen Eingabeparameter).
Dieses Programm macht auf die Konsole laufend Textausgaben - je nach Anwendungsfall auch stundenlang.
Auf der Basis dieses Quelltextes ist mir das auch gelungen, die Ausgaben in einem Memo-Fenster anzeigen zu lassen.
Das Programm läuft sonst völlig im Hintergrund.
Nur habe ich die lokalen Funktionsvariablen global deklariert und die eigentliche Funktion durch einen Button-Click ausführen lassen.
Nun soll dieses Programm aber auch vorzeitig beendet werden können, zu einem beliebigen Zeitpunkt, wenn auch erst nach 1 oder ein paar Stunden.
Unter dem programm-eigenen DOS-Fenster schafft man das mit den Tastatureingaben 'e' mit Abspeichern der Ergebnisse, 'n' ohne Abspeichern der Ergebnisse,
anschließend ist das Programm mit der Enter-Taste zu schließen, wobei dann das eigene programmeigene DOS-Fenster auch geschlossen wird.
Das Programm verwendet im Lauf ohne Benutzeroberfläche dazu die Tastaturabfragen wie z.B. GetasyncKeyState(69) <> 0.
Meine Versuche, mit Eingaben in der Anwendung darauf Einfluß zu nehmen, sind bisher fehlgeschlagen.
Gern würde ich dies mit 2 Buttons machen, also Beenden mit und ohne Abspeichern.
Kann man nicht mit StartupInfo.hStdInput die Eingabeschnittstelle PipeInputWrite festlegen und mit
WriteFile(PipeInputWrite, cbuf, 1, NumberOfBytesWrite, nil);
darauf eingeben?
Weitere Angaben: Delphi 7, Windows 7 Prof. 64 Bit, 4-Prozessorkern-Rechner
|
|
jaenicke
Beiträge: 19289
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 27.10.16 14:32
Ich habe danach (siehe oben) ja noch eine Variante für lesen und schreiben gepostet:
www.entwickler-ecke.....php?p=109159#109159
Ich hatte danach noch eine deutlich bessere Variante weiterentwickelt (TDOSCommand, das Original lief nicht unter neueren Delphiversionen), aber die finde ich grad nicht... das Original müsste bei dir aber mit Delphi 7 laufen.
Vielleicht hilft dir auch diese aktuellere Komponente weiter:
github.com/TurboPack/DOSCommand
Für diesen Beitrag haben gedankt: hjl
|
|
|