29

I need to execute a Windows "find" command from a Delphi software. I've tried to use the ShellExecute command, but it doesn't seem to work. In C, I'd use the system procedure, but here... I don't know. I'd like to do something like this:

System('find "320" in.txt > out.txt');

Edit : Thanks for the answer :) I was trying to run 'Find' as an executable, not as argument for cmd.exe.

1
  • 2
    ShellExecute should work. What's it doing, or not doing or whatever? Commented Sep 21, 2009 at 13:30

2 Answers 2

36

An example using ShellExecute():

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(0, nil, 'cmd.exe', '/C find "320" in.txt > out.txt', nil, SW_HIDE);
  Sleep(1000);
  Memo1.Lines.LoadFromFile('out.txt');
end;

Note that using CreateProcess() instead of ShellExecute() allows for much better control of the process.

Ideally you would also call this in a secondary thread, and call WaitForSingleObject() on the process handle to wait for the process to complete. The Sleep() in the example is just a hack to wait some time for the program started by ShellExecute() to finish - ShellExecute() will not do that. If it did you couldn't for example simply open a notepad instance for editing a file, ShellExecute() would block your parent app until the editor was closed.

Sign up to request clarification or add additional context in comments.

5 Comments

Shouldn't that be Memo1.Lines.LoadFromFile('out.txt');
Er, if the windows is hidden (SW_HIDE), why should we put a Sleep(1000) after the ShellExecute ?
I'm working with 100MB text files so I'm going for the WaitForSingleObject option. thanks ! :)
ShellExecute() will start the application but not wait for it to finish - otherwise you couldn't really use it to open a standard GUI program. It may not matter with running find on a small text file, but for longer executing tasks you have to make sure they are finished before you process the results in out.txt. That's why using CreateProcess() and waiting on the process handle is a much better way to do it.
Alternatively use ShellExecuteEx, which can be used with wait functions
22

Variant 1 (using the "advanced" CreateProcess):

This will run a 'DOS' program and retrieve its output:

function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string;  { Run a DOS program and retrieve its output dynamically while it is running. }
var
  SecAtrrs: TSecurityAttributes;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  pCommandLine: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  Result := '';
  with SecAtrrs do begin
    nLength := SizeOf(SecAtrrs);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SecAtrrs, 0);
  try
    with StartupInfo do
    begin
      FillChar(StartupInfo, SizeOf(StartupInfo), 0);
      cb := SizeOf(StartupInfo);
      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), StartupInfo, ProcessInfo);
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := windows.ReadFile(StdOutPipeRead, pCommandLine, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            pCommandLine[BytesRead] := #0;
            Result := Result + pCommandLine;
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
      finally
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

Variant 2:

Capture console output in [Realtime] and how it in a TMemo:

procedure CaptureConsoleOutput(const ACommand, AParameters: String; AMemo: TMemo);
 const
   CReadBuffer = 2400;
 var
   saSecurity: TSecurityAttributes;
   hRead: THandle;
   hWrite: THandle;
   suiStartup: TStartupInfo;
   piProcess: TProcessInformation;
   pBuffer: array[0..CReadBuffer] of AnsiChar;      <----- update
   dRead: DWord;
   dRunning: DWord;
 begin
   saSecurity.nLength := SizeOf(TSecurityAttributes);
   saSecurity.bInheritHandle := True;  
   saSecurity.lpSecurityDescriptor := nil; 

   if CreatePipe(hRead, hWrite, @saSecurity, 0) then
   begin    
     FillChar(suiStartup, SizeOf(TStartupInfo), #0);
     suiStartup.cb := SizeOf(TStartupInfo);
     suiStartup.hStdInput := hRead;
     suiStartup.hStdOutput := hWrite;
     suiStartup.hStdError := hWrite;
     suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;    
     suiStartup.wShowWindow := SW_HIDE; 

     if CreateProcess(nil, PChar(ACommand + ' ' + AParameters), @saSecurity,
       @saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess)
       then
     begin
       repeat
         dRunning  := WaitForSingleObject(piProcess.hProcess, 100);        
         Application.ProcessMessages(); 
         repeat
           dRead := 0;
           ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);          
           pBuffer[dRead] := #0; 

           OemToAnsi(pBuffer, pBuffer);
           AMemo.Lines.Add(String(pBuffer));
         until (dRead < CReadBuffer);      
       until (dRunning <> WAIT_TIMEOUT);
       CloseHandle(piProcess.hProcess);
       CloseHandle(piProcess.hThread);    
     end; 

     CloseHandle(hRead);
     CloseHandle(hWrite);
   end;
end;

Source: delphi.wikia.com

2 Comments

For Variant 1, in the newer versions of Delphi (eg. 10.3) remove the "windows." prefix in the line starting "WasOK := windows.ReadFile" otherwise it fails to compile.
Actually, it is better if you use the full qualifyer: "winapi.windows.readfile"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.