Konuyu Oyla:
  • Derecelendirme: 4.5/5 - 2 oy
  • 1
  • 2
  • 3
  • 4
  • 5
Komut Satırından (CMD) Toplu İş Yaptırmak
#1
Merhaba,
Windows hedef platformu için bir çoğumuz uygulama yazarken DOS ekranında komut çalıştırma ihtiyacı duymuşuzdur. 
Bu kapsamda üretilen ve Ücretsiz olarak dağıtılan son derece etkili ve kullanışlı bir Component'ten bahsederek, kullanımı konusunda bilgiler vermek istiyorum.
Kullanacağımız component TurboPack DOSCommand.

Genel Özellikleri;
  • Komut satırında herhangi bir veya birden fazla iş yaptırıma (com, exe ve bat dosyalarını doğrudan çalıştırma),
  • Komutların işlem sonucunu kendi uygulamanızda görebilme (OnNewChar ve OnNewLine olayları ile OutputLnes ve Lines özellikleri),
  • Komutun bittiğini algılama (OnTerminateProcess ve  OnTerminated olayları),
  • Varsayılan dizini belirleyebilme (CurrentDir özelliği),
  • Geçici Ortam Değişkenleri ekleyebilme (Environment özelliği),
  • Karakter Decode ve Encode işlemleri yapabilme (OnCharEncoding ve OnCharDecoding olayları),
  • Çalışmayan ve Hata oluşturan komutları ve hata türünü yakalama (OnExecuteError olayı),
  • Çalıştırılacak Komutların İşletim Sisteminde öncelik sırasını belirleyebilme (Priority özelliği; aldığı değerler----> HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS ve REALTIME_PRIORITY_CLASS),
  • Satır Satır Komut işletebilme (SendLine özelliği)
  • Çalışma anında iş durumu hakkında bilgi edinebilme (OnTerminateProcess olayı ve ProcessInformation özelliği),

Component'i indirmek için: TurboPack - DOSCommand

Örnek Bir Uygulama;

Yukarıdaki linkten TurboPack - DOSCommand Component'ini indirip kuralım.
Tool Palette Penceresi, DOSCommand sekmesinden  form üzerine bir adet DosCommand nesnesi yerleştirelim.


Form üzerine komutları çalıştırmak ve komut geri dönüş değerlerini almak için birer adet Edit, Button ve Memo nesnesi yerleştirelim ve tasarımını aşağıdaki gibi yapalım.


Bir veya birden fazla komut içeren bir toplu işlem dosyası (Batch dosyası) oluşturalım. Benim kullanacağım örnek dosya içeriği aşağıdaki gibi olacak.


Butonu çift tıklayıp, OnClick olayına aşağıdaki kodları yazalım.

procedure TForm1.Button1Click(Sender: TObject);
begin
 DosCommand1.Priority := HIGH_PRIORITY_CLASS; {Çalışacak komutun iş önceliğini Yüksek olarak belirledik}
 DosCommand1.CommandLine := edtKomut.Text; {Çalıştırılacak Komut veya Komutlar listesi}
 DosCommand1.Execute; {Komutları çalıştır}
end;

DosCommand nesnesini seçelim ve Object Inspector Penceresi Events sekmesinde OnNewLine olayını çift tıklayalım ve aşağıdaki kodları yazalım.

procedure TForm1.DosCommand1NewLine(ASender: TObject; const ANewLine: string;
 AOutputType: TOutputType);
begin
 case AOutputType of
   TOutputType.otEntireLine:
     Memo1.Lines.Add(ANewLine);
 end;
end;

Son olarak uygulamayı çalıştırıp, sonucuna bakalım.


İyi çalışmalar...
Cevapla
#2
Bu arada toplu iş dosyasının ilk satırında komutları gizlemek için kullandığım echo off komutunun başında @ işareti olması gerekiyordu (@echo off). Konumuzla pek alakası yok ama hatamı sonradan gördüğüm için düzeltme yapayım dedim.
Cevapla
#3
Merhaba Fesih bey

Delphi ile konsol uygulamasından  veri alma ile ilgili arama yaparken çözümün DelphiCanda olduğunu görünce çok sevindim Smile
C# da yazdığım bir konsol uygulaması ile Delphi vcl uygulamasını anlattığınız bileşeni kullanarak haberleştirmeyi denedim.
Uygulamayı windowsun cmd penceresinden çalıştırılınca, türkçe karakterleri sorunsuz olarak ekranda görebiliyorum.

cmd penceresinden  c:\consol.exe    --- "Şeker gibi" ekrana bir çıktı üretiyor.

Aynı işlemi delphi tarafında
DosCommand1.CommandLine :='c:\consol.exe';

şeklinde denediğimde çıktıyı memo nesnesine aktarıyor fakat türkçe karakterlerde bozulma meydana geliyor.

Makalenizde belirttiğiniz "OnCharEncoding ve OnCharDecoding olayları" nı kullanmayı beceremedim.


Aradan baya zaman geçmiş fakat yardımcı olursanız sevinirm.

Edit:
İlk mesajda geçen link pasif olmuş.
Güncel link : https://github.com/TurboPack/DOSCommand
Cevapla
#4
(28-03-2018, Saat: 00:53)frmman Adlı Kullanıcıdan Alıntı: Merhaba Fesih bey

Delphi ile konsol uygulamasından  veri alma ile ilgili arama yaparken çözümün DelphiCanda olduğunu görünce çok sevindim Smile
C# da yazdığım bir konsol uygulaması ile Delphi vcl uygulamasını anlattığınız bileşeni kullanarak haberleştirmeyi denedim.
Uygulamayı windowsun cmd penceresinden çalıştırılınca, türkçe karakterleri sorunsuz olarak ekranda görebiliyorum.

cmd penceresinden  c:\consol.exe    --- "Şeker gibi" ekrana bir çıktı üretiyor.

Aynı işlemi delphi tarafında
DosCommand1.CommandLine :='c:\consol.exe';

şeklinde denediğimde çıktıyı memo nesnesine aktarıyor fakat türkçe karakterlerde bozulma meydana geliyor.

Makalenizde belirttiğiniz "OnCharEncoding ve OnCharDecoding olayları" nı kullanmayı beceremedim.


Aradan baya zaman geçmiş fakat yardımcı olursanız sevinirm.

Edit:
İlk mesajda geçen link pasif olmuş.
Güncel link : https://github.com/TurboPack/DOSCommand

Merhaba; 

Nereden bulduğumu hatırlamıyorum ama aşağıdaki şekilde çözmüştüm.

function GetDosOutput(CommandLine: string; Work: string = 'C:\'): AnsiString;
var
 SA: TSecurityAttributes;
 SI: TStartupInfo;
 PI: TProcessInformation;
 StdOutPipeRead, StdOutPipeWrite: THandle;
 WasOK: Boolean;
 Buffer: array[0..255] of AnsiChar;
 BytesRead: Cardinal;
 WorkDir: string;
 Handle: Boolean;

function StrOemToAnsi(const aStr : AnsiString) : AnsiString;
   var
     Len : Integer;
   begin
     if aStr = '' then Exit;
     Len := Length(aStr);
     SetLength(Result, Len);
     OemToCharBuffA(PAnsiChar(aStr), PAnsiChar(Result), Len);
   end;

begin
 Result := '';
 with SA do
 begin
   nLength := SizeOf(SA);
   bInheritHandle := True;
   lpSecurityDescriptor := nil;
 end;
 CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
 try
   with SI do
   begin
     FillChar(SI, SizeOf(SI), 0);
     cb := SizeOf(SI);
     dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
     wShowWindow := SW_HIDE;
     hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
     hStdOutput := StdOutPipeWrite;
     hStdError := StdOutPipeWrite;
   end;
   WorkDir := Work;
   Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine),
                           nil, nil, True, 0, nil,
                           PChar(WorkDir), SI, PI);
   CloseHandle(StdOutPipeWrite);
   if Handle then
     try
       repeat
         WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);

         if BytesRead > 0 then
         begin
           Buffer[BytesRead] := #0;
           Result := StrOemToAnsi(Result) + Buffer;
         end;
       until not WasOK or (BytesRead = 0);
       WaitForSingleObject(PI.hProcess, INFINITE);
     finally
       CloseHandle(PI.hThread);
       CloseHandle(PI.hProcess);
     end;
 finally
   CloseHandle(StdOutPipeRead);
 end;
end;


Kullanımı 

Memo1.Lines.Text := GetDosOutput('TREE D:\');
Amatör Küme Bilgisayar Programcısı
WWW
Cevapla
#5
Teşekkürler @barutali

Memo2.Lines.Text := GetDosOutput('TREE C:\1');

Önerdiğiniz yöntemi de denedim, C:\1 klasörü içerisine türkçe karakter içeren klasörler isimlerinde bozulma meydana geldi.
Cevapla
#6
Aşağıdaki kod türkçe karakter problemi çıkartmadan çalışıyor.
DosCommand komponenti çıktı parametrelerini işlem devam ederken gösterebiliyor. Güzel bir özellik.
Çok fazla sayıda dosya içeren bir klasörün listelenmesi işleminde, işlem devam ederken ekrana yansımanı göze çok güzel görünüyor.
Diğer çözümlerde işlem bittikden sonra sonuç görünüyor.

Kaynak


procedure RunDosInMemo(DosApp: string; AMemo:TMemo);
const
   READ_BUFFER_SIZE = 2400;
var
   Security: TSecurityAttributes;
   readableEndOfPipe, writeableEndOfPipe: THandle;
   start: TStartUpInfo;
   ProcessInfo: TProcessInformation;
   Buffer: PAnsiChar;
   BytesRead: DWORD;
   AppRunning: DWORD;
begin
   Security.nLength := SizeOf(TSecurityAttributes);
   Security.bInheritHandle := True;
   Security.lpSecurityDescriptor := nil;

   if CreatePipe({var}readableEndOfPipe, {var}writeableEndOfPipe, @Security, 0) then
   begin
       Buffer := AllocMem(READ_BUFFER_SIZE+1);
       FillChar(Start, Sizeof(Start), #0);
       start.cb := SizeOf(start);

       // Set up members of the STARTUPINFO structure.
       // This structure specifies the STDIN and STDOUT handles for redirection.
       // - Redirect the output and error to the writeable end of our pipe.
       // - We must still supply a valid StdInput handle (because we used STARTF_USESTDHANDLES to swear that all three handles will be valid)
       start.dwFlags := start.dwFlags or STARTF_USESTDHANDLES;
       start.hStdInput := GetStdHandle(STD_INPUT_HANDLE); //we're not redirecting stdInput; but we still have to give it a valid handle
       start.hStdOutput := writeableEndOfPipe; //we give the writeable end of the pipe to the child process; we read from the readable end
       start.hStdError := writeableEndOfPipe;

       //We can also choose to say that the wShowWindow member contains a value.
       //In our case we want to force the console window to be hidden.
       start.dwFlags := start.dwFlags + STARTF_USESHOWWINDOW;
       start.wShowWindow := SW_HIDE;

       // Don't forget to set up members of the PROCESS_INFORMATION structure.
       ProcessInfo := Default(TProcessInformation);

       //WARNING: The unicode version of CreateProcess (CreateProcessW) can modify the command-line "DosApp" string. 
       //Therefore "DosApp" cannot be a pointer to read-only memory, or an ACCESS_VIOLATION will occur.
       //We can ensure it's not read-only with the RTL function: UniqueString
       UniqueString({var}DosApp);

       if CreateProcess(nil, PChar(DosApp), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, start, {var}ProcessInfo) then
       begin
           //Wait for the application to terminate, as it writes it's output to the pipe.
           //WARNING: If the console app outputs more than 2400 bytes (ReadBuffer),
           //it will block on writing to the pipe and *never* close.
           repeat
               Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 100);
               Application.ProcessMessages;
           until (Apprunning <> WAIT_TIMEOUT);

           //Read the contents of the pipe out of the readable end
           //WARNING: if the console app never writes anything to the StdOutput, then ReadFile will block and never return
           repeat
               BytesRead := 0;
               ReadFile(readableEndOfPipe, Buffer[0], READ_BUFFER_SIZE, {var}BytesRead, nil);
               Buffer[BytesRead]:= #0;
               OemToAnsi(Buffer,Buffer);
               AMemo.Text := AMemo.text + String(Buffer);
           until (BytesRead < READ_BUFFER_SIZE);
       end;
       FreeMem(Buffer);
       CloseHandle(ProcessInfo.hProcess);
       CloseHandle(ProcessInfo.hThread);
       CloseHandle(readableEndOfPipe);
       CloseHandle(writeableEndOfPipe);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin {button 1 code}
  RunDosInMemo('chkdsk.exe c:\',Memo1);
end;
Cevapla
#7
DosCommand komponentinin türkçe karakter problemini , onCharDecoding  olayında  "code page" kullanarak aşağıdaki şekilde çözdüm.
Kod içerisindeki linkde code page ile ilgili açıklayıcı tablo bulunmaktadır.

Not: onCharDecoding   olayı oluşturulunca onNewLine olayı otomatik olarak devre dışı kalıyor. 

Konsoldan cevap olarak çok fazla veri geldiğinde yukarıda paylaştığım
procedure RunDosInMemo(DosApp: string; AMemo:TMemo);
ile başlayan yöntem kilitlenip kalıyordu. DosCommand komponenti sorunsuz çalışıyor.


function TForm1.DosCommand1CharDecoding(ASender: TObject;
 ABuf: TStream): string;
var s:string;
   Bytes: TBytes;
begin

    SetLength(Bytes, abuf.Size);
    abuf.Read(bytes[0],ABuf.Size -1);

    // https://msdn.microsoft.com/en-us/library...s.85).aspx
    // 857 ibm857 OEM Turkish; Turkish (DOS)
    // 1026 IBM1026 IBM EBCDIC Turkish (Latin 5)
    // 1254 windows-1254 ANSI Turkish; Turkish (Windows)
    s:= TEncoding.GetEncoding(857).GetString(bytes);

    Memo1.Lines.Add(s);

end;
Cevapla
#8
Merhaba,
Linux hedef platformunda Terminal (CMD) üzerinde bir komutu çalıştırmak için de bir çözüm paylaşılmış.
Learn How To Execute External Commands On Linux From An Auto Tables For RAD Server API Endpoint
Cevapla
#9
Teşekkürler, çok güzel birkonu olmuş.
Cevapla
#10
(17-09-2016, Saat: 22:56)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Merhaba,
Windows hedef platformu için bir çoğumuz uygulama yazarken DOS ekranında komut çalıştırma ihtiyacı duymuşuzdur. 
Bu kapsamda üretilen ve Ücretsiz olarak dağıtılan son derece etkili ve kullanışlı bir Component'ten bahsederek, kullanımı konusunda bilgiler vermek istiyorum.
Kullanacağımız component TurboPack DOSCommand.

Genel Özellikleri;
  • Komut satırında herhangi bir veya birden fazla iş yaptırıma (com, exe ve bat dosyalarını doğrudan çalıştırma),
  • Komutların işlem sonucunu kendi uygulamanızda görebilme (OnNewChar ve OnNewLine olayları ile OutputLnes ve Lines özellikleri),
  • Komutun bittiğini algılama (OnTerminateProcess ve  OnTerminated olayları),
  • Varsayılan dizini belirleyebilme (CurrentDir özelliği),
  • Geçici Ortam Değişkenleri ekleyebilme (Environment özelliği),
  • Karakter Decode ve Encode işlemleri yapabilme (OnCharEncoding ve OnCharDecoding olayları),
  • Çalışmayan ve Hata oluşturan komutları ve hata türünü yakalama (OnExecuteError olayı),
  • Çalıştırılacak Komutların İşletim Sisteminde öncelik sırasını belirleyebilme (Priority özelliği; aldığı değerler----> HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS ve REALTIME_PRIORITY_CLASS),
  • Satır Satır Komut işletebilme (SendLine özelliği)
  • Çalışma anında iş durumu hakkında bilgi edinebilme (OnTerminateProcess olayı ve ProcessInformation özelliği),

Component'i indirmek için: TurboPack - DOSCommand

Örnek Bir Uygulama;

Yukarıdaki linkten TurboPack - DOSCommand Component'ini indirip kuralım.
Tool Palette Penceresi, DOSCommand sekmesinden  form üzerine bir adet DosCommand nesnesi yerleştirelim.


Form üzerine komutları çalıştırmak ve komut geri dönüş değerlerini almak için birer adet Edit, Button ve Memo nesnesi yerleştirelim ve tasarımını aşağıdaki gibi yapalım.


Bir veya birden fazla komut içeren bir toplu işlem dosyası (Batch dosyası) oluşturalım. Benim kullanacağım örnek dosya içeriği aşağıdaki gibi olacak.


Butonu çift tıklayıp, OnClick olayına aşağıdaki kodları yazalım.

procedure TForm1.Button1Click(Sender: TObject);
begin
 DosCommand1.Priority := HIGH_PRIORITY_CLASS; {Çalışacak komutun iş önceliğini Yüksek olarak belirledik}
 DosCommand1.CommandLine := edtKomut.Text; {Çalıştırılacak Komut veya Komutlar listesi}
 DosCommand1.Execute; {Komutları çalıştır}
end;

DosCommand nesnesini seçelim ve Object Inspector Penceresi Events sekmesinde OnNewLine olayını çift tıklayalım ve aşağıdaki kodları yazalım.

procedure TForm1.DosCommand1NewLine(ASender: TObject; const ANewLine: string;
 AOutputType: TOutputType);
begin
 case AOutputType of
   TOutputType.otEntireLine:
     Memo1.Lines.Add(ANewLine);
 end;
end;

Son olarak uygulamayı çalıştırıp, sonucuna bakalım.


İyi çalışmalar...

Öncelikle bilgilendirme için çok teşekkürler Fesih Bey. Birşey sormak istiyorum içeriğinde çalıştırdığımız komutların azılarında E/H sorusu var , sanırım yönetici yetkisinden içindeki komutları bu yetki sorununa takılmadan nasıl çalıştırabilir. Mesela ben sql server durdurmak istiyorum. Bir süre sonra çalıştırmak istiyorum ama durdurmaya çalıştığımda E/H kısmında kaldığı için çalışmıyor.
Cevapla




Konuyu Okuyanlar: 1 Ziyaretçi