Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Problemas con Ejecutar comando cmd y capturar su salida (https://www.clubdelphi.com/foros/showthread.php?t=94862)

JoAnCa 07-09-2020 06:20:49

Problemas con Ejecutar comando cmd y capturar su salida
 
Hola a todos

Pues buscando como ejecutar un comando con el CMD y obtener la salida para almacenarla en una variable, me encontré con este truco de seoane, pero parece que como es algo antiguo y para delphi 7 o anterior, en delphi XE7 no funciona como debe ser.

En la salida en lugar de mostrar el resultado como debe ser, muestra letras chinas

Por ejemplo:
Esta salida
Cita:

dir /a:-d /s /b "o:\" | find /c ":\"
me debe dar 67 y me da 㜶਍

un dir C: me devuelve esto:

Cita:

䔠潶畬敭敤氠⁡湵摩摡䌠攠⁳楓瑳浥ൡ
䔠ꍮ敭潲搠⁥敳楲⁥敤潶畬敭獥›㉄䌸䘭㘶ല
਍䐠物捥潴楲敤挠尺਍਍㔱〯⼸〲〲†〱㐺‴⹡洠*†㰠䥄㹒†††††䵁D਍㤱〯⼷〲〲†ㄱ㈺‹⹰洠*†㰠䥄㹒†††††湉整l਍㘰〯⼹〲〲†〱㐺‷⹰洠*†㰠䥄㹒†††††䵋汐祡牥਍㜰ㄯ⼲〲㤱†㔰ㄺ‴⹡洠*† 㰠䥄㹒†††††敐晲潌獧਍㐰〯⼹〲〲†㠰㐺′⹰洠*†㰠䥄㹒†††††牐杯慲楆敬൳
㔰〯⼹〲〲†㤰㌺′⹰洠*†㰠䥄㹒†††††牐杯慲楆敬⁳砨㘸ഩ
〳〯⼵〲〲†㔰ㄺ‸⹰洠*†㰠䥄㹒†††††整灭਍㜱〯⼵〲〲†㔰㈺‵⹰洠*†㰠䥄㹒†††††獕牥൳
㠲〯⼸〲〲†㌰㈺‶⹰洠*†㰠䥄㹒†††††楗摮睯൳
㠲〯⼸〲〲†㔰㐺‷⹰洠*†㰠䥄㹒†††††慸灭൰
†††††††〠愠捲楨潶⁳††††††〠戠瑹獥਍†††††††〱搠物⁳㈠ⰳ㐲ⰰ㠶ⰳ㈵‰祢整⁳楬牢獥਍
Que cambios se deben hacer para que muestre los resultados de forma correcta?

ElKurgan 07-09-2020 09:11:00

Tiene toda la pinta de ser problema con los caracteres Unicode. Recuerda que desde Delphi 2009 los antiguos WideString son los actuales String, y los antiguos String son los actuales AnsiString. Lo mismo pasa con los Char y PChar, que ahora son AnsiChar y PAnsiChar.

Prueba a cambiar las definiciones de la función, a ver si funciona

No tengo a mano Delphi ahora mismo para probarlo.

Un saludo

JoAnCa 07-09-2020 15:06:26

Hice los cambios de tipos de char y ansichar y demas como me explicas, pero en algunas funciones me da incompatibilidad de tipos


Entonces, depurando el código para ver el lugar donde se "pierde" la codificación, encontre que es en esta línea:

Código Delphi [-]
PeekNamedPipe(read_stdout, @Buffer, SizeOf(Buffer) - 1, @bread, @avail, nil);

y no entiendo nada mas, no se como seguir para arreglar el problema :confused:

Al González 07-09-2020 16:27:21

¿Podrías mostrarnos un poco más de código? Incluyendo el código de read_stdout (sea variable, función...) y la declaración de Buffer (sea variable, parámetro...). Gracias.

JoAnCa 07-09-2020 17:57:16

Cita:

Empezado por Al González (Mensaje 538450)
¿Podrías mostrarnos un poco más de código? Incluyendo el código de read_stdout (sea variable, función...) y la declaración de Buffer (sea variable, parámetro...). Gracias.


En realidad no puse el codigo completo porque puse el enlace en el primer post, pero de igual forma aqui lo tienes

Código Delphi [-]
function IsWinNT: boolean;
var
  OSV: OSVERSIONINFO;
begin
  OSV.dwOSVersionInfoSize := sizeof(osv);
  GetVersionEx(OSV);
  result := OSV.dwPlatformId = VER_PLATFORM_WIN32_NT;
end;

function CmdExec(Cmd: string): string;
var
  Buffer: array[0..4096] of Char;
  si: STARTUPINFO;
  sa: SECURITY_ATTRIBUTES;
  sd: SECURITY_DESCRIPTOR;
  pi: PROCESS_INFORMATION;
  newstdin, newstdout, read_stdout, write_stdin: THandle;
  exitcod, bread, avail: Cardinal;
begin
  Result:= '';
  if IsWinNT then
  begin
    InitializeSecurityDescriptor(@sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(@sd, true, nil, false);
    sa.lpSecurityDescriptor := @sd;
  end
  else sa.lpSecurityDescriptor := nil;
  sa.nLength := sizeof(SECURITY_ATTRIBUTES);
  sa.bInheritHandle := TRUE;
  if CreatePipe(newstdin, write_stdin, @sa, 0) then
  begin
    if CreatePipe(read_stdout, newstdout, @sa, 0) then
    begin
      GetStartupInfo(si);
      with si do
      begin
        dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
        wShowWindow := SW_HIDE;
        hStdOutput := newstdout;
        hStdError := newstdout;
        hStdInput := newstdin;
      end;
      Fillchar(Buffer, SizeOf(Buffer), 0);
      GetEnvironmentVariable('COMSPEC', @Buffer, SizeOf(Buffer) - 1);
      StrCat(@Buffer,PChar(' /c ' + Cmd));
      if CreateProcess(nil, @Buffer, nil, nil, TRUE, CREATE_NEW_CONSOLE, nil, nil, si, pi) then
      begin
        repeat
          PeekNamedPipe(read_stdout, @Buffer, SizeOf(Buffer) - 1, @bread, @avail, nil);
          if bread > 0 then
          begin
            Fillchar(Buffer, SizeOf(Buffer), 0);
            ReadFile(read_stdout, Buffer, bread, bread, nil);
            Result:= Result + String(PChar(@Buffer));
          end;
          Application.ProcessMessages;
          GetExitCodeProcess(pi.hProcess, exitcod);
        until (exitcod <> STILL_ACTIVE) and (bread = 0);
      end;
      CloseHandle(read_stdout);
      CloseHandle(newstdout);
    end;
    CloseHandle(newstdin);
    CloseHandle(write_stdin);
  end;
end;

Forma de uso:

Código Delphi [-]
ShowMessage(CmdExec('dir c:\'));

Al González 08-09-2020 06:23:52

Cita:

Empezado por JoAnCa (Mensaje 538453)
no puse el codigo completo porque puse el enlace en el primer post

Oh, es verdad. :o

Revisando el código, da la impresión de que podría funcionar si cambias esta línea:
Código Delphi [-]
Result:= Result + String(PChar(@Buffer));
por:
Código Delphi [-]
Result:= Result + ANSIString(PANSIChar(@Buffer));
o simplemente:
Código Delphi [-]
Result := Result + PANSIChar (@Buffer);

JoAnCa 09-09-2020 03:34:44

Pues con el cambio que me indicas ya sale bien, con las dos opciones :cool:

Muchas gracias

Al González 09-09-2020 05:48:17

¡De nada!

Me alegra en verdad haberte podido ayudar. :):)

Por lo visto esta sentencia
Código Delphi [-]
ReadFile(read_stdout, Buffer, bread, bread, nil);
lee caracteres ANSI (de un byte) y los mete a la variable Buffer. Esta tiene la declaración
Código Delphi [-]
Buffer: array[0..4096] of Char;
, que en su momento era una matriz de 4097 bytes. Pero ahora el tipo Char es Unicode y tiene un tamaño de dos bytes, por lo que la matriz duplica su tamaño en las versiones modernas de Delphi. Este cambio no parece dar problema con el resto de las funciones donde se utiliza la variable Buffer. GetEnvironmentVariable, por ejemplo, fue en su momento actualizada para Unicode. Sin embargo la lectura con ReadFile de lo que arroja el comando ejecutado depende de cómo trabaje la consola de Windows (al parecer sigue siendo ANSI).

Así, cuando se realiza la concatenación
Código Delphi [-]
Result:= Result + String(PChar(@Buffer));
en Delphi 2009 o superior, se toman los bytes de Buffer de dos en dos, siendo cada par, es decir, cada Char moderno, interpretado como un carácter Unicode.

Digamos que en un String ANSI 'Hola', donde el valor como Byte de esos cuatro caracteres es 72, 111, 108 y 97, unir por pares dichos valores nos daría (por su conformación binaria) los valores Word 28488 (111 desplazado ocho bits a la izquierda más 72) y 24940 (97 desplazado ocho bits a la izquierda más 108). Si buscamos en Google los caracteres Unicode de valor decimal 28488 y 24940, aparece que estos son y , respectivamente. No tengo la más remota idea de qué significan; no vayan a decir hola de esa manera cuando viajen por oriente. :D

El efecto del problema que se producía puede ser comprobado con un código como este:
Código Delphi [-]
procedure TfmMain.Button1Click(Sender: TObject);
Var
  A :ANSIString;
begin
  A := 'Hola';
  ShowMessage (PChar (A));  // W1044 Suspicious typecast of AnsiString to PWideChar
end;

Me gustaría hacer un reconocimiento al autor de la útil función CmdExec, Domingo Seoane. Tengo buenos recuerdos de él como miembro de este club. Durante años hizo innumerables aportaciones técnicas en materia de Delphi que hasta la fecha siguen sirviendo a programadores como JoAnCa. Mi respeto absoluto para Domingo, ojalá lo leamos por acá nuevamente.

Un abrazo caracterizado. :)

Al González.

JoAnCa 09-09-2020 15:01:23

Cita:

Empezado por Al González (Mensaje 538468)
¡De nada!

Me alegra en verdad haberte podido ayudar. :):)

Por lo visto esta sentencia Código Delphi [-]ReadFile(read_stdout, Buffer, bread, bread, nil);

lee caracteres ANSI (de un byte) y los mete a la variable Buffer. Esta tiene la declaración Código Delphi [-]Buffer: array[0..4096] of Char;

, que en su momento era una matriz de 4097 bytes. Pero ahora el tipo Char es Unicode y tiene un tamaño de dos bytes, por lo que la matriz duplica su tamaño en las versiones modernas de Delphi. Este cambio no parece dar problema con el resto de las funciones donde se utiliza la variable Buffer. GetEnvironmentVariable, por ejemplo, fue en su momento actualizada para Unicode. Sin embargo la lectura con ReadFile de lo que arroja el comando ejecutado depende de cómo trabaje la consola de Windows (al parecer sigue siendo ANSI).

Así, cuando se realiza la concatenación Código Delphi [-]Result:= Result + String(PChar(@Buffer));

en Delphi 2009 o superior, se toman los bytes de Buffer de dos en dos, siendo cada par, es decir, cada Char moderno, interpretado como un carácter Unicode.

Digamos que en un String ANSI 'Hola', donde el valor como Byte de esos cuatro caracteres es 72, 111, 108 y 97, unir por pares dichos valores nos daría (por su conformación binaria) los valores Word 28488 (111 desplazado ocho bits a la izquierda más 72) y 24940 (97 desplazado ocho bits a la izquierda más 108). Si buscamos en Google los caracteres Unicode de valor decimal 28488 y 24940, aparece que estos son y , respectivamente. No tengo la más remota idea de qué significan; no vayan a decir hola de esa manera cuando viajen por oriente. :D

El efecto del problema que se producía puede ser comprobado con un código como este:
Código Delphi [-]procedure TfmMain.Button1Click(Sender: TObject); Var A :ANSIString; begin A := 'Hola'; ShowMessage (PChar (A)); // W1044 Suspicious typecast of AnsiString to PWideChar end;


Me gustaría hacer un reconocimiento al autor de la útil función CmdExec, Domingo Seoane. Tengo buenos recuerdos de él como miembro de este club. Durante años hizo innumerables aportaciones técnicas en materia de Delphi que hasta la fecha siguen sirviendo a programadores como JoAnCa. Mi respeto absoluto para Domingo, ojalá lo leamos por acá nuevamente.

Un abrazo caracterizado. :)

Al González.


Vaya, muy buena explicación, muy instructiva para mi, ya que siempre me gusta saber el por qué las "cosas" no funcionan


La franja horaria es GMT +2. Ahora son las 12:50:58.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi