Redirecionar STDOUT de programas console
Boa tarde amigos,
Gostaria de saber se existe alguma maneira de redirecionar a saída padrão de programas que rodam em modo console para outro tipo de stream.
No meu caso, queria redirecionar a saída de programas como o gbak e o gfix para um handle e deste handle capturar as linhas retornadas, utilizando-as em algum outro componente.
Sei que existe uma opção de gravação do log, mas ocorre um erro quando eu abro o arquivo quando o mesmo está sendo gravado.
Se possível, postem um exemplo.
Danilo Christiano
Gostaria de saber se existe alguma maneira de redirecionar a saída padrão de programas que rodam em modo console para outro tipo de stream.
No meu caso, queria redirecionar a saída de programas como o gbak e o gfix para um handle e deste handle capturar as linhas retornadas, utilizando-as em algum outro componente.
Sei que existe uma opção de gravação do log, mas ocorre um erro quando eu abro o arquivo quando o mesmo está sendo gravado.
Se possível, postem um exemplo.
Danilo Christiano
Danilo Christiano
Curtidas 0
Respostas
Danilo Christiano
21/07/2004
Nota:
Os programas externos são chamados através da função CreateProcess da API do windows e creio que utilizando o parâmetro hStdOutput da estrutura STARTUPINFO (que é um parâmetro da função CreateProcess) posso conseguir este redirecionamento.
Caso alguém saiba utilizar estas funções, poste algum exemplo.
Danilo Christiano
Os programas externos são chamados através da função CreateProcess da API do windows e creio que utilizando o parâmetro hStdOutput da estrutura STARTUPINFO (que é um parâmetro da função CreateProcess) posso conseguir este redirecionamento.
Caso alguém saiba utilizar estas funções, poste algum exemplo.
Danilo Christiano
GOSTEI 0
Beppe
21/07/2004
Sim, é por aí. Procure por estas palavras-chave aqui que vc acha.
GOSTEI 0
Vinicius2k
21/07/2004
Danilo, vc solucionou isso?
Eu tentei de tudo e não consegui... por fim pedi que a rotina deletasse o arquivo de log antes de criar o processo (estou usando exatamente para o GBAK)...
Outro problema que enfrento é que o GBAK, ou tem saída no vídeo ou no log... então, se eu coloco a saída para o vídeo, se o banco for pequeno o usuário nem vê a janela e se eu coloco para o arquivo de log, se o banco for grande, fica a ´tela preta´ até terminar o processo...
E outra, tentei amenizar essa ´tela preta´ que no Win 2K é tela cheia, tendando determinar que fosse em janela, designando, posição, tamanho e outras coisas e simplesmente não tenho resultado... não importa os valores de parametros que eu passe para STARTUPINFO que o console sempre é executado em tela cheia...
Se alguém tiver alguma idéia será muito bem vinda...
T+
Eu tentei de tudo e não consegui... por fim pedi que a rotina deletasse o arquivo de log antes de criar o processo (estou usando exatamente para o GBAK)...
if FileExists(´nome_do_log´) then DeleteFile(´nome_do_log´);
Outro problema que enfrento é que o GBAK, ou tem saída no vídeo ou no log... então, se eu coloco a saída para o vídeo, se o banco for pequeno o usuário nem vê a janela e se eu coloco para o arquivo de log, se o banco for grande, fica a ´tela preta´ até terminar o processo...
E outra, tentei amenizar essa ´tela preta´ que no Win 2K é tela cheia, tendando determinar que fosse em janela, designando, posição, tamanho e outras coisas e simplesmente não tenho resultado... não importa os valores de parametros que eu passe para STARTUPINFO que o console sempre é executado em tela cheia...
Se alguém tiver alguma idéia será muito bem vinda...
T+
GOSTEI 0
Nildo
21/07/2004
Estes programas são 32 ou 16 bits? Se forem 32 vocês podem hookar as APIs que ele utiliza para escrever na tela e redirecionar o texto para seu aplicativo.
GOSTEI 0
Vinicius2k
21/07/2004
O GBAK é 32 bits... ele usa a API do Firebird e eu até já estou estudando-a e espero conseguir criar meu sistema de back-up para o Firebird...
Fiz aqui uma coisa que amenizou o problema... continuo direcionando a saída para o Log (e isso é realmente o que eu desejo), mas como o GBAK não dá nenhuma saída do vídeo, fiz uma aplicação console para servir de intermediária, que dá um WriteLn na tela com uma mensagem de ´Executando processo blá blá´ e eh este console que chama o GBAK... assim que termina exibo o Log já dentro da aplicação original...
T+
Fiz aqui uma coisa que amenizou o problema... continuo direcionando a saída para o Log (e isso é realmente o que eu desejo), mas como o GBAK não dá nenhuma saída do vídeo, fiz uma aplicação console para servir de intermediária, que dá um WriteLn na tela com uma mensagem de ´Executando processo blá blá´ e eh este console que chama o GBAK... assim que termina exibo o Log já dentro da aplicação original...
T+
GOSTEI 0
Nildo
21/07/2004
Seria legal se desse para mostrar exatamente o que está acotnecendo no momento certo né? É possível sim. Só é preciso saber se esses programas utilizam alguma API pra escrever na tela. Qual função eles utilizam pra escrever na tela?
GOSTEI 0
Vinicius2k
21/07/2004
Seria legal se desse para mostrar exatamente o que está acotnecendo no momento certo né?
Com certeza :D
Na boa... eu vou até postar isso para dos devs do Firebird...
o param -v determina que eu quero ´verbose´ para me mostrar na tela...
dae se eu quiser um log eu uso -y + nome do log, só que não dá o verbose em conjunto na tela... pq não os dois... não deve ser nada de tão absurdo assim de fazer...
Só é preciso saber se esses programas utilizam alguma API pra escrever na tela. Qual função eles utilizam pra escrever na tela?
Eu ainda estou estudando e não sei lhe responder isso nildo... mas a documentação é essa : http://www.ibphoenix.com/downloads/60ApiGuide.zip
T+
GOSTEI 0
Danilo Christiano
21/07/2004
Bom,
Utilizar o arquivo de log criado por um determinado programa (via função específica, como -verbose) ou utilizar a interface do programa (como as funções dos nossos ib´s e fb´s) não deixa de ser uma solução prática e eficiente.
Mas, se eu precisar utilizar o retorno de outros programas??? Vou precisar estudar a ducumentação de quantos programas forem necessários, como o ping e o ftp nativos do Windows, sempre???
Seria bacana se eu pudesse chamar qualquer programa console e via a API do Windows direcionar a saída deste para onde eu bem entendesse.
Procurei posts sobre este tópico no fórum, como Beppe me sugeriu. Encontrei saídas e sugestões que eu havia pensado antes, modificando o parâmetro hStdOutput da função startupinfo. Não sei se é pelo meu S.O. (win2000) ou é algum erro de código, mas não consigo fazer a saída ser redirecionada.
Mudo a cor do console para azul, vermelho, cor de fonte, crio o arquivo de log temporário... Faço tudo que é necessário, mas não funciona!!!
Caso alguem consiga fazer o redirecionamento funcionar sob 98/nt/2000/xp e afins, postem. Pelo amor de deus!!!
Danilo Christiano
Utilizar o arquivo de log criado por um determinado programa (via função específica, como -verbose) ou utilizar a interface do programa (como as funções dos nossos ib´s e fb´s) não deixa de ser uma solução prática e eficiente.
Mas, se eu precisar utilizar o retorno de outros programas??? Vou precisar estudar a ducumentação de quantos programas forem necessários, como o ping e o ftp nativos do Windows, sempre???
Seria bacana se eu pudesse chamar qualquer programa console e via a API do Windows direcionar a saída deste para onde eu bem entendesse.
Procurei posts sobre este tópico no fórum, como Beppe me sugeriu. Encontrei saídas e sugestões que eu havia pensado antes, modificando o parâmetro hStdOutput da função startupinfo. Não sei se é pelo meu S.O. (win2000) ou é algum erro de código, mas não consigo fazer a saída ser redirecionada.
Mudo a cor do console para azul, vermelho, cor de fonte, crio o arquivo de log temporário... Faço tudo que é necessário, mas não funciona!!!
Caso alguem consiga fazer o redirecionamento funcionar sob 98/nt/2000/xp e afins, postem. Pelo amor de deus!!!
Danilo Christiano
GOSTEI 0
Beppe
21/07/2004
OFF: Não interessa se é usado Write ou cout <<, a API usada é WriteConsoleA ou WriteColsoleW.
Há uma flag na estrutura startupinfo que vc precisa setar também. Vc está usando pipes não? è preciso esperar até que haja algum resultado no seu buffer, senão a resposta é nula.
Há uma flag na estrutura startupinfo que vc precisa setar também. Vc está usando pipes não? è preciso esperar até que haja algum resultado no seu buffer, senão a resposta é nula.
GOSTEI 0
Nildo
21/07/2004
Christiano, seria muito problema pra você ser notificado a cada vez que fosse chamada o WriteConsoleA/W, e você poder ver os parametros que é passado para ele?
GOSTEI 0
Nildo
21/07/2004
Ou melhor, você pode usar a função ReadConsole.
GOSTEI 0
Beppe
21/07/2004
Ou melhor, você pode usar a função ReadConsole.
Não é assim tão fácil, porque o console do seu aplicativo não precisa ser necessariamente o mesmo do novo processo, além do que um processo GUI raramente terá um, de qualquer forma.
GOSTEI 0
Beppe
21/07/2004
Danilo, segue o código que você estava procurando. Antes de tomar como solução, assegure-se que ele o retorna a 100¬ da saída em 100¬ dos casos. Digo isto porque já tive problemas no passado.
Bom proveito.
procedure TForm1.Button2Click(Sender: TObject); const BufferSize = 4096; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; Stdin, Stdout: record Rd, Wr: Cardinal; end; Data: TStringStream; Buffer: array[0..BufferSize - 1] of Byte; Bytes: Cardinal; begin Data := TStringStream.Create(´´); try SA.nLength := SizeOf(SA); SA.lpSecurityDescriptor := nil; SA.bInheritHandle := True; // pipe que servirá como entrada para o novo processo Win32Check(CreatePipe(Stdout.Rd, Stdout.Wr, @SA, 0)); DuplicateHandle(GetCurrentProcess, Stdout.Rd, GetCurrentProcess, @Stdout.Rd, 0, False, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS); // pipe que servirá como saída para o novo processo Win32Check(CreatePipe(Stdin.Rd, Stdin.Wr, @SA, 0)); DuplicateHandle(GetCurrentProcess, Stdin.Wr, GetCurrentProcess, @Stdin.Wr, 0, False, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS); FillChar(SI, SizeOf(SI), 0); SI.cb := SizeOf(SI); SI.wShowWindow := SW_HIDE; SI.hStdInput := Stdin.Rd; SI.hStdOutput := Stdout.Wr; SI.hStdError := Stdout.Wr; SI.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; Win32Check(CreateProcess(´C:\Arquivos de programas\Borland\Delphi5\Bin\dcc32.exe´, nil, nil, nil, True, 0, nil, nil, SI, PI)); // espera que o processo-filho termine while WaitForSingleObject(PI.hProcess, 100) = WAIT_TIMEOUT do Application.ProcessMessages; Win32Check(CloseHandle(PI.hThread)); Win32Check(CloseHandle(PI.hProcess)); Win32Check(CloseHandle(Stdin.Rd)); Win32Check(CloseHandle(Stdin.Wr)); Win32Check(CloseHandle(Stdout.Wr)); // lê a saída encontrada no fim de leitura da pipe do processo filho // até esgotar repeat if not ReadFile(Stdout.Rd, Buffer, SizeOf(Buffer), Bytes, nil) or (Bytes = 0) then Break; Data.Write(Buffer, Bytes); until False; Win32Check(CloseHandle(Stdout.Rd)); Memo1.Lines.Text := Data.DataString; finally Data.Free; end; end;
Bom proveito.
GOSTEI 0