Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.06.02;
Скачать: CL | DM;

Вниз

Работа с внешним консольным приложением   Найти похожие ветки 

 
ES   (2013-01-14 19:31) [0]

Есть общеизвестный пример: http://www.sources.ru/delphi/system/capturing_output_from_console.shtml

Есть также общеизвестная программа openssl.exe

Как бы доработать пример так, что сначала вызывается программа с параметрами "openssl.exe -a -b -c ...", далее она ожидает ввода с клавиатуры (если бы её запускать с cmd), нужно её подсунуть содержимое на сотню другую байт (контент), который она зашифрует и выдаст ответ, который и надо считать.

Вот считывание в примере и реализовано, но как бы сделать запись в консоль?


 
брат Птибурдукова   (2013-01-14 19:40) [1]

Создавай два пайпа. Второй запуздыривай сюда:  hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем


 
Rouse_ ©   (2013-01-14 19:40) [2]

Вон тут Пашка Голубь доделывал этот пример: http://forum.sources.ru/index.php?showtopic=83650


 
Игорь Шевченко ©   (2013-01-14 19:52) [3]

if not WasOK then
    raise Exception.Create( "Ошибка выполнения или компиляции: " +
           Chr( 10 ) + Chr( 13 ) + CommandLine )

Для интернетов самое то


 
Игорь Шевченко ©   (2013-01-14 20:01) [4]

http://www.gunsmoker.ru/2010/05/90.html


 
ES   (2013-01-14 21:57) [5]


> Вон тут Пашка Голубь доделывал этот пример

в принципе, я как-то так и делал.
Только для тестов использовал конструкцию - сначала запускал процесс "ftp", в консоль передавал

WriteFile(StdinPipeWrite,"-help",5,Written,nil);

Ничего не получалось. Это приводило к тому, что консоль отображалась на экране, программа ничего не считывала (после данного кода шло считывание ReadFile).

Возможно, управление до WriteFile доходило раньше, чем консоль успевала инициализироваться или типа того... Но дело в том, что вроде бы в ответ на команда OpenSSL ничего не выдает, запускаешь - а просто курсор мигает и все. Как понять когда начать программно вводить команды?...


 
ES   (2013-01-15 11:10) [6]

Я ошибся в том, что забыл писать Enter! то есть, перевод строки: #13#10

Возник другой вопрос, почему я запускаю команду "ftp":

WasOK := CreateProcess( nil,
                           "ftp",
                           nil,
...


дальше стандартный код, подходим к считыванию данных из консоли:

>WasOK := ReadFile( StdOutPipeRead, Buffer, 255, BytesRead, nil );

на этом месте исполнение зависает навсегда. Хотя если в консоле набрать ftp, то мы видим приглашение:

C:\Users\Admin>ftp
ftp>


Почему это приглашение "ftp>" не считывается?

Хотя писать можно, да...


 
ES   (2013-01-18 20:30) [7]

Камрады,  измучился весь с этим Openssl.exe

Есть такая команда:

> openssl.exe smime -sign -signer public.pem -inkey private.pem -nochain -nocerts -outform PEM -nodetach

Формирует криптоконтейнер PKCS#7, где:

public.pem - публичный ключ
private.pem - приватный 2048-битный ключ

После такого запроса openssl.exe переходит в режим ввода текста с консоли для упаковки в контейнер. Если эту операцию делать ручками, то набираешь текст (при этом Enter - всего лишь перевод строки), далее я знаю что можно нажать [Enter], [CTRL]+[Z], далее Enter - после чего openssl.exe набранный текст упаковывает в контейнер. Вот как это выглядит:

C:\MySSL>openssl
.exe smime -sign -signer public.pem -inkey private.pem -nochain -nocerts
-outform PEM -nodetach
Loading "screen" into random state - done
Вводим любой текст типа
Только что до этого нажал Enter... сейчас еше раз нажму
вот..
^Z
-----BEGIN PKCS7-----
MIIC7QYJKoZIhvcNAQcCoIIC3jCCAtoCAQExCzAJBgUrDgMCGgUAMGsGCSqGSIb3
DQEHAaBeBFyCoq6kqKwgq+6hrqkg4qWq4eIg4qivoA0Kkq6r7KquIOfiriCkriDt
4q6jriCtoKagqyBFbnRlci4uLiDhpannoOEgpeilIOCgpyCtoKas4w0Koq7iLi4a
DQoNCjGCAlkwggJVAgEBMFcwSTELMAkGA1UEBhMCUlUxGDAWBgNVBAoTD1BTIFlh
bmRleC5Nb25leTEgMB4GA1UEAxMXWWFuZGV4IE1vbmV5IElzc3VpbmcgQ0ECCh4/
wpMAAAAALeYwCQYFKw4DAhoFAKCB2DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB
MBwGCSqGSIb3DQEJBTEPFw0xMzAxMTgxNjIxNTlaMCMGCSqGSIb3DQEJBDEWBBTy
0giOb5OyNlgHLQcU/GrIl6G4UzB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQB
KjALBglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3
DQMCAgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDAN
BgkqhkiG9w0BAQEFAASCAQBTARbvY0i+2iXk/c0BuE83V7lFhXFFgu68NQ+87Xq+
eRcwoPBFp8gfuHj+QLOP4Lz9qI99Uqg0jv4i1rjUQpQ/9kSqrc6jnd/DNn4+IovZ
cVZyWFNEWC5qe8qVMRS98zhYynJfqgpQz97J7A9ZryjYedp8iud0gLwGnbT+mP7U
q9cecoJoad1kLNvDPIcn3W0PX1lzMekG7ALeaIkosE8GyNKnAlYMS1sjZPoh+6eX
lKZUuf+CAdR2XWDgETGhPt9voNAM562bLnTWUqDVxr9MUI+5gvqd3fqVlqVWw9sU
XPvUgVl/Tkv1E4RHQBP6EbEW2n3SpfMSFaado2ek5FSx
-----END PKCS7-----


Вот как переделать пример: http://forum.sources.ru/index.php?showtopic=83650

Чтобы все сработало? Там передается [Enter] - и для внешней программы это событие, но для openssl это нифига не событие. У меня все подвисает, что логично, видимо openssl продолжает дальше ждать ввода текста.

Я пробовал после WriteFile делать:

SetEndOfFile(StdinPipeWrite)

Но ответом идет False с кодом GetLastError = 87, что означает:
"Параметр задан неверно".

После WriteFile делать CloseHandle(StdinPipeWrite) тоже пробовал.

Что делать? Как запустить openssl и передать в него текст, сообщив о конце передачи текста?! (


 
Игорь Шевченко ©   (2013-01-18 20:39) [8]

А записать в StdinPipeWrite #$1A ?


 
ES   (2013-01-19 00:37) [9]


> А записать в StdinPipeWrite #$1A ?

попробовал... ничего не вышло, такое же зависание...


 
ES   (2013-01-19 00:38) [10]

читал: http://en.wikipedia.org/wiki/End-of-file

от безысходности писал также:

#04
#26
#13#17

тоже не получилось.


 
брат Птибурдукова   (2013-01-19 00:43) [11]

Попробуй в командной строке выполнить openssl.exe smime -sign -signer public.pem -inkey private.pem -nochain -nocerts -outform PEM -nodetach < stdin , где в файле stdin будут лежать все твои ответы. Когда заработает, тогда и начинай программу мучить. Потому что похоже, что ошибка не в твоей программе, а во входных данных.


 
ES   (2013-01-19 11:51) [12]


> Попробуй в командной строке выполнить openssl.exe smime
> -sign -signer public.pem -inkey private.pem -nochain -nocerts
> -outform PEM -nodetach < stdin

конечно, все работает. Только в openssl это указывается доп. командой:

-in InFile.content

все ок.


 
брат Птибурдукова   (2013-01-19 13:21) [13]


>  Только в openssl это указывается доп. командой
Так ты уверен, что с перенаправлением вывода всё работает так же корректно, как с дополнительным параметром?


 
ES   (2013-01-20 23:55) [14]


> Так ты уверен, что с перенаправлением вывода всё работает
> так же корректно, как с дополнительным параметром?

да, попробовал сейчас команду:

> openssl.exe smime -sign -signer public.pem -inkey private.pem -nochain -nocerts -outform PEM -nodetach < req.xml

отработало просто на ура, тут же выдало ответ и вышло из программы openssl.


 
ES   (2013-01-21 00:07) [15]

Причем, для openssl вроде как есть оттестированный рабочий код для PHP, его структура вроде бы понятна и на дельфи нечто такое же получается:

$descriptorspec = array(
           0 => array("pipe", "r"), // stdin is a pipe that the child will read from
           1 => array("pipe", "w"), // stdout is a pipe that the child will write to
           2 => array("pipe", "w")); // stderr is a file to write to

       $process = proc_open(
           "openssl smime -sign -signer " . $certificate .
                   " -inkey " . $privkey .
                   " -nochain -nocerts -outform PEM -nodetach",
           $descriptorspec, $pipes);

       if (is_resource($process)) {
           // $pipes now looks like this:
           // 0 => writeable handle connected to child stdin
           // 1 => readable handle connected to child stdout

           fwrite($pipes[0], $source);
           fclose($pipes[0]);

           $pkcs7 = stream_get_contents($pipes[1]);
...


но тем не менее на дельфи изобразить не получается ((


 
Игорь Шевченко ©   (2013-01-21 00:21) [16]

уже код пора выкладывать


 
ES   (2013-01-21 11:35) [17]

Код вот:

procedure TExternalProgRunner.Execute;
var
 CommandLine: string;
 StdOutPipeRead, StdOutPipeWrite, StdInPipeRead, StdInPipeWrite: THandle;
 SA                             : TSecurityAttributes;
 SI                             : TStartupInfo;
 PI                             : TProcessInformation;
 WasOK                          : BOOL;
 Buffer                         : array[0..255] of Char;
 BytesRead, Written             : Cardinal;
 WaitRes: Cardinal;
 hArray: array[0..1] of THandle;
 sCurDir: string;
 aaa: integer;
begin
 With SA do
 Begin
   nLength := SizeOf( SA );
   bInheritHandle := True;
   lpSecurityDescriptor := nil;
 end;
 // создаём пайп для перенаправления стандартного вывода
 CreatePipe( StdOutPipeRead,  // дескриптор чтения
              StdOutPipeWrite, // дескриптор записи
              @SA,              // аттрибуты безопасности
              0                // количество байт принятых для пайпа - 0 по умолчанию
             );
 // создаём пайп для перенаправления стандартного ввода
 CreatePipe( StdInPipeRead,  // дескриптор чтения
              StdInPipeWrite, // дескриптор записи
              @SA,              // аттрибуты безопасности
              0                // количество байт принятых для пайпа - 0 по умолчанию
             );

 try
   // Создаём дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
   // а так же проверяем, чтобы он не показывался на экране.
   with SI do
   Begin
      FillChar( SI, SizeOf( SI ), 0 );
      cb := SizeOf( SI );
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE or SW_SHOWMINNOACTIVE;
      hStdInput := StdInPipeRead; //GetStdHandle(STD_INPUT_HANDLE);
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
   end;

   CommandLine := """ + FAppPath + "" " + FParam ;
   sCurDir := ExtractFileDir(FAppPath);
   WasOK := CreateProcess( nil,
                           PChar( CommandLine ),
                           nil,
                           nil,
                           True,
                           0,
                           nil,
                           PChar(sCurDir),
                           SI,
                           PI );
   FhProcess := PI.hProcess ;

   // если процесс может быть создан, то дескриптор, это его вывод
   CloseHandle( StdOutPipeWrite );
   if not WasOK then
   begin
     FStatus := prsEnd ;
   end
   else
   begin
     FStatus := prsRun ;
     FisSuccessRun := true ;
     try
       // если указано - пишем в консоль текст
       if FConsoleInput <> "" then
       begin
         WriteFile(StdinPipeWrite, FConsoleInput[1], length(FConsoleInput),
             Written, nil);
       end;
       // получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
       Repeat
         // читаем блок символов (могут содержать возвраты каретки и переводы строки)
         WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil );
         // есть ли что-нибудь ещё для чтения?
         if BytesRead > 0 then
         Begin
            // завершаем буфер PChar-ом
            Buffer[BytesRead] := #0;
             // добавляем буфер в общий вывод
             FcsData.Enter;
             try
               FAllRead := FAllRead + Buffer;
             finally
               FcsData.Leave;
             end;
          end;
       Until not WasOK or ( BytesRead = 0 ) or (Terminated);
       {SetLength(FAllRead, Length(Line));
       if Line <> "" then
         OemToChar(PChar(Line), PChar(FAllRead)) ;}

       SetEvent(FeventFinishRead) ;
       // ждём, пока завершится консольное приложение
       hArray[0] := pi.hProcess;
       hArray[1] := FeventTerminate ;
       WaitRes := WaitForMultipleObjects(2, @hArray, false, INFINITE);
       if WaitRes = WAIT_OBJECT_0 then // значит, терминировался именно процесс запущенный
         FStatus := prsEnd ;
       FExitCode := 0;
       GetExitCodeProcess( pi.hProcess, FExitCode );
     finally
       // Закрываем все оставшиеся дескрипторы
       CloseHandle( PI.hThread );
       CloseHandle( pi.hProcess );
     end;
   end;
 finally
   SetEvent(FeventFinishRead) ;
   CloseHandle( StdOutPipeRead );
   if StdInPipeWrite <> 0 then
     CloseHandle( StdInPipeWrite );
   if StdInPipeRead <> 0 then
     CloseHandle(StdInPipeRead);
 end;
end;


На строчке:

> WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil );

поток зависает.


 
Игорь Шевченко ©   (2013-01-21 15:34) [18]

WaitForInputIdle вставить не поможет перед началом работы с вводом-выводом?


 
Юрий Зотов ©   (2013-01-21 17:47) [19]

Я делал иначе, без пайпов. В своей программе создаю консоль и запускаю чужое консольное приложение, как дочерний процесс с наследуемыми дескрипторами ввода-вывода. В результате чужая программа разделяет с моей одну и ту же консоль - то есть, моя программа может в консоль писать, а та читает.


 
ES   (2013-01-22 11:28) [20]


> WaitForInputIdle вставить не поможет перед началом работы
> с вводом-выводом?

вставил перед WriteFile.
WaitForInputIdle(pi.hProcess, INFINITE)  возвращает 0xFFFFFFFF.
GetLastError = 18: "Больше файлов не осталось"

Результата не дало.


 
Игорь Шевченко ©   (2013-01-22 14:41) [21]


>  // если процесс может быть создан, то дескриптор, это его
> вывод
>    CloseHandle( StdOutPipeWrite );


Зачем этот вызов ?


 
Игорь Шевченко ©   (2013-01-22 14:57) [22]

надо закрыть описатели записи в stdout и чтения stdin

     Win32Check(CloseHandle(StdInPipeRead));
     Win32Check(CloseHandle(StdOutPipeWrite));



Страницы: 1 вся ветка

Текущий архив: 2013.06.02;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.008 c
15-1358877111
Дмитрий С
2013-01-22 21:51
2013.06.02
Почтовый ретранслятор.


6-1222503093
Cryxalis
2008-09-27 12:11
2013.06.02
как заставить INDY юзать уже занятый порт?


15-1359391746
Медвежонок Пятачок
2013-01-28 20:49
2013.06.02
Формат пакета TClientDataSet D7 и XE2


15-1359103131
DevilDevil
2013-01-25 12:38
2013.06.02
Оптимальный размер буфера для чтения/записи файла


15-1359029346
Студент
2013-01-24 16:09
2013.06.02
Колонка и микрофон.