2009-05-08 9 views
3

J'essaie plusieurs échantillons sur internet et aucun d'entre eux ne fonctionne - les scripts ne sont pas exécutés - (peut-être parce qu'ils sont pré-Delphi 2009 unicode?).Comment dans Delphi 2009 rediriger la console (stin, sterr)?

Je dois exécuter des scripts python et passer des arguments à eux, comme:

python "..\Plugins\RunPlugin.py" -a login -u Test -p test 

et capturer la sortie à une chaîne & les erreurs à l'autre.

C'est ce que j'ai maintenant:

procedure RunDosInMemo(DosApp:String; var OutData: String); 
var 
    SA: TSecurityAttributes; 
    SI: TStartupInfo; 
    PI: TProcessInformation; 
    StdOutPipeRead, StdOutPipeWrite: THandle; 
    WasOK: Boolean; 
    Buffer: array[0..255] of Char; 
    BytesRead: Cardinal; 
    WorkDir: string; 
    Handle: Boolean; 
begin 
    OutData := ''; 
    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 or CREATE_UNICODE_ENVIRONMENT; 
     wShowWindow := SW_HIDE; 
     hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin 
     hStdOutput := StdOutPipeWrite; 
     hStdError := StdOutPipeWrite; 
    end; 
    WorkDir := 'C:\'; 
    Handle := CreateProcess(nil, PChar(DosApp), 
          nil, nil, True, 0, nil, 
          PChar(WorkDir), SI, PI); 
    CloseHandle(StdOutPipeWrite); 
    if Handle then 
    begin 
     try 
     repeat 
      WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); 
      if BytesRead > 0 then 
      begin 
      Buffer[BytesRead] := #0; 
      OutData := OutData + String(Buffer); 
      end; 
     until not WasOK or (BytesRead = 0); 
     WaitForSingleObject(PI.hProcess, INFINITE); 
     finally 
     CloseHandle(PI.hThread); 
     CloseHandle(PI.hProcess); 
     end; 
    end else begin 
     raise Exception.Create('Failed to load python plugin'); 
    end; 
    finally 
    CloseHandle(StdOutPipeRead); 
    end; 
end; 

Répondre

4

Je ne suis pas certain que le WaitForSingleObject est la voie à suivre ... Je pense que mieux boucle avec GetExitCodeProcess (pi.hProcess, iExitCode) jusqu'à ce que iExitCode <> STILL_ACTIVE, puis vérifiez les données sur chaque passage dans la boucle.

Le code tel qu'il est écrit ne fonctionne pas non plus sous Delphi 2007, il ne s'agit donc pas d'un problème Unicode Delphi 2009.

Changer votre boucle interne aux travaux suivants:

if Handle then 
begin 
    try 
    repeat 
     WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); 
     for ix := 0 to BytesRead-1 do 
     begin 
      OutData := OutData + AnsiChar(Buffer[ix]); 
     end; 
     GetExitCodeProcess(pi.hProcess,iExit); 
    until (iExit <> STILL_ACTIVE); 
    finally 
    CloseHandle(PI.hThread); 
    CloseHandle(PI.hProcess); 
    end; 

J'ai fait les corrections/ajouts suivants aux variables locales:

Buffer: array[0..255] of byte; 
iExit : Cardinal; 
IX : integer; 

J'ai aussi déplacé le CloseHandle (StdOutPipeWrite) juste avant la fermer de la StdOutPipeRead.

+0

Si le script ne s'est pas exécuté auparavant, ces modifications ne permettent pas l'exécution du script. La boucle "for" s'exécute une fois de trop; corrige cela, et l'affectation de zéro dans Buffer [BytesRead] n'a plus aucun effet. Évitez le type-cast en déclarant Buffer comme un tableau d'AnsiChar dès le début. Quel est le but de votre appel WaitForSingleObject? Vous ignorez la valeur de retour. –

+0

L'appel waitforsingleobject est un espace réservé pour "graver du temps". Un sleep aurait aussi fonctionné, mais waitforsingleobject avec un timeout dit simplement d'attendre que l'application ne fonctionne plus. J'essayais juste de ne pas écraser le CPU dans la boucle. – skamradt

+0

Ok, je copie ce code d'ailleurs, je n'ai pas de compétences en développement d'API donc je comprends à peine ce qui se passait. Merci pour votre contribution! – mamcx

7

Create_Unicode_Environment est une création processus drapeau, destiné à être utilisé dans le paramètre dwCreationFlags de CreateFile. Ce n'est pas un indicateur à utiliser dans l'enregistrement TStartupInfo. Les fonctions de l'API sont susceptibles d'échouer si vous leur donnez des valeurs de drapeau qu'ils ne comprennent pas, et ils sont susceptibles de faire des choses étranges si vous leur donnez des valeurs de drapeau qui signifient autre chose que ce que vous attendiez.

Vous déclarez un tampon de 256 Char s; rappeler que Char dans Delphi 2009 est un type Unicode de 2 octets. Vous appelez alors ReadFile et dites-lui que le tampon est 255 octets long au lieu de la valeur réelle, 512. Lorsque la documentation indique qu'une valeur est le nombre d'octets, prenez cela comme repère pour utiliser la fonction SizeOf. Puisque ReadFile lit des octets, il serait judicieux de déclarer votre tableau de tampons comme un tableau d'éléments de la taille d'un octet, tel que AnsiChar. Ainsi, lorsque vous définissez Buffer[BytesRead], vous n'incluez pas deux fois les données réellement lues.

La version Unicode de CreateProcess peut modifier son argument de ligne de commande. Vous devez vous assurer que la chaîne que vous transmettez à ce paramètre a un nombre de références de 1. Appelez UniqueString(DosApp) avant d'appeler le CreateProcess. En cas de défaillance d'une fonction API, vous voudrez bien sûr savoir pourquoi. Ne pas seulement composent une raison. Utilisez les fonctions fournies, telles que Win32Check et RaiseLastOSError. À tout le moins, appelez GetLastError, comme MSDN vous dit. Ne lancez pas un type d'exception générique quand un type plus spécifique est facilement disponible.

Questions connexes