[Delphi] Wyjście aplikacji konsolowej a Unicode.

0

Witam!

Niedawno przesiadłem się na Delphi 2010 i już pojawił się pierwszy problem z Unicode.
Przy pomocy mojego programu, uruchamiam zewnętrzną aplikację konsolową, a następnie przechwytuję jej wyjście, a przynajmniej takie jest założenie ;).
Niestety jestem marnym programistą, więc przy tym muszę posługiwać się modułem Redirect Console, napisanym przez Sonic'a (do ściągnięcia ze strony torry.net).

Kod modułu RedirectConsole.pas:

unit RedirectConsole;

interface

const
  CRLF=#13#10;

var
  RC_SendBuf: string;
  RC_End: Boolean;
  RC_ExitCode: Cardinal;

procedure RC_Run(Command: string);
procedure RC_LineIn(s: string);
var       RC_LineOut: procedure(s: string);

implementation

uses Windows, Forms;

procedure RC_LineIn(s: string);
begin
  RC_SendBuf:=RC_SendBuf+s+CRLF;
end; // RC_LineIn;

function IsWinNT: Boolean;
var osv: tOSVERSIONINFO;
begin
  osv.dwOSVersionInfoSize:=sizeof(osv);
  GetVersionEx(osv);
  result:=osv.dwPlatformID=VER_PLATFORM_WIN32_NT;
end; // IsWinNT

procedure SplitLines(s: string);
var t: string;
begin
  while pos(CRLF, s)<>0 do begin
    t:=copy(s, 1, pos(CRLF, s)-1);
    RC_LineOut(t);
    delete(s, 1, pos(CRLF, s)+1);
  end;
  if length(s)>0 then RC_LineOut(s);
end; // SplitLines

procedure RC_Run(Command: string);
const bufsize=1024; // 1KByte buffer
var
  buf: array [0..bufsize-1] of char;
  si: tSTARTUPINFO;
  sa: tSECURITYATTRIBUTES;
  sd: tSECURITYDESCRIPTOR;
  pi: tPROCESSINFORMATION;
  newstdin, newstdout, read_stdout, write_stdin: tHandle;
  bread, avail: dword;
begin
  // Configuraciones de seguridad para WinNT
  if IsWinNT then begin
    InitializeSecurityDescriptor(@sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(@sd, true, nil, false);
    sa.lpSecurityDescriptor:=@sd;
  end else sa.lpSecurityDescriptor:=nil;
  // Creamos Pipe A
  if not CreatePipe(newstdin, write_stdin, @sa, 0) then begin
    RC_LineOut('Error creating Pipe A');
    exit;
  end;
  // Creamos Pipe B
  if not CreatePipe(read_stdout, newstdout, @sa, 0) then begin
    RC_LineOut('Error creating Pipe B');
    CloseHandle(newstdin);
    CloseHandle(write_stdin);
    exit;
  end;
  // Configuramos si
  GetStartupInfo(si);
  si.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  si.wShowWindow:=SW_HIDE;
  si.hStdOutput:=newstdout;
  si.hStdError:=newstdout;
  si.hStdInput:=newstdin;
  // Creamos proceso
  if not CreateProcess(PChar(command), nil, nil, nil, true,
    CREATE_NEW_CONSOLE, nil, nil, si, pi) then begin
    RC_LineOut('Error creating process: '+command);
    CloseHandle(newstdin);
    CloseHandle(newstdout);
    CloseHandle(read_stdout);
    CloseHandle(write_stdin);
    exit;
  end;
  // Loop principal
  fillchar(buf, sizeof(buf), 0);
  RC_End:=false;
  RC_SendBuf:='';
  repeat
    // application.processmessages;
    Application.HandleMessage;
    GetExitCodeProcess(pi.hProcess, RC_ExitCode);
    if (RC_ExitCode<>STILL_ACTIVE) then RC_End:=True;
    PeekNamedPipe(read_stdout, @buf, bufsize, @bread, @avail, nil);
    // Comprobamos texto de salida
    if (bread<>0) then begin
      fillchar(buf, bufsize, 0);
      if (avail>bufsize) then
        while (bread>=bufsize) do begin
          ReadFile(read_stdout, buf, bufsize, bread, nil);
          SplitLines(buf);
          fillchar(buf, bufsize, 0);
        end
      else begin
        ReadFile(read_stdout, buf, bufsize, bread, nil);
        SplitLines(buf);
      end;
    end;
    // Comprobamos texto de entrada
    while (Length(RC_SendBuf)>0) do begin
      WriteFile(write_stdin, RC_SendBuf[1], 1, bread, nil);
      Delete(RC_SendBuf, 1, 1);
    end;
  until RC_End;
  // Cerramos las cosas
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
  CloseHandle(newstdin);
  CloseHandle(newstdout);
  CloseHandle(read_stdout);
  CloseHandle(write_stdin);
end; // RC_Run

end.

Jednak ze względu na Unicode w Delphi 2010, zamiast normalnych liter dostaję w moim programie krzaczki w stylu: "楍牣獯景⁴楗摮睯⁳". Próbowałem coś z tym zrobić, próbowałem różnych funkcji do konwersji znaków znalezionych w necie, niektórzy wręcz sugerowali, żeby zmienić czcionkę w komponencie wyświetlającym wynik, jednak nic z tych rzeczy nie pomaga, a mój poziom wiedzy nie pozwala mi wybrnąć z tego problemu. Sam kod Redirect Console jest dla mnie zbyt skomplikowany, żebym mógł z niego cokolwiek wywnioskować. Próbowałem modyfikować na różne sposoby zmienną s w procedurze SplitLines (ona zawiera przechwycony tekst, więc chyba właśnie ją powinienem przekonwertować na Unicode ?), jednak wszystkie moje próby okazały się bezskuteczne.
Tak więc, czy mógłby ktoś bardziej doświadczony w tym temacie, pomóc mi dostosować ten kod do standardów Unicode ?

Z góry dziękuję za odpowiedzi
Pozdrawiam

0

Te komponenty też mają dla mnie skomplikowany kod,
a przynajmniej taki, w który nie wnikałem, ale spróbuj:
TDosCommand lub tego, ktory nazywa się o ile dobrze
pamiętam UnitedCmd. Może z nimi będzie ok. Chociaż
z tego co wiem - powstały dla wcześniejszych Delphi.

0

Szukając odpowiedzi na mój problem, spotkałem się właśnie z tymi komponentami i niestety oba nie działają jak należy (pisane były przed wyjściem Delphi z Unicode, o czym zresztą wspomniałeś). Nad tym problemem siedzę już pół dnia i niestety wciąż nie zbliżyłem się nawet do rozwiązania.
Coś czuję, że zbyt szybko odinstalowałem Delphi 7. Na nim ten kod działał bezbłędnie, stąd wiem, że to problem z Unicode.

0

Też przeniosłem się z d7 na d2010 i oczywiście mam identyczny problem xD
Do teraz nie znalazłem rozwiązania dla tego problemu bo między innymi nie miałem syper potrzeby aby przenieść projekt z d7 na 2010.
Mój projekt w d7 działa bez problemu... po poprawieniu i skomplikowaniu problem z tymi znaczkami wydaje się najmniejszym.

Będę śledził ten temat.

PS. sprawdź sobie wpisywanie unicode do stringgrid (np z memo + cyrylica). Wszędzie wyświetla się ok tylko nie w grid-zie. (Wyświetlają się ??? co ciekawe po pobraniu stringu z komórki w edicie cyrylica wyświetlana jest poprawnie.)

0

zamień string na AnsiString

0
Misiekd napisał(a)

zamień string na AnsiString

Jeśli chodzi Ci o zmienne, to próbowałem pozamieniać wszystko ze String (czyli w nowym Delphi UnicodeString) na ANSIString, jednak wtedy krzaczki wciąż występowały (choć zmieniały się głównie na pytajniki: "????4???????‹????4???").

0

Ok, doszedłem do tego :). Jednak po południu się zupełnie inaczej myśli niż w nocy.

Więc tak:

  • zamieniłem wszystkie Stringi na ANSIString (w Delphi 2009+ String to domyślnie UnicodeString).
  • zamieniłem Char na ANSIChar (w Delphi 2009+ Char nie jest już jednobajtowy, domyślnie jest to WideChar)
  • w związku z powyższymi zmianami, program się wykrzaczał. Okazuje się, że w nowych Delphi funkcja CreateProcess zmieniła się, w związku z powyższymi zmianami. Tak więc musiałem użyć funkcji w wersji ANSI, czyli CreateProcessA, a to znowu pociągnęło za sobą kilka prostych zmian w kodzie (głównie używanie funkcji w wersji ANSI, zamiast domyślnych Unicodowych).

Jeśli kogoś interesują wszystkie zmiany, których trzeba dokonać w module Redirect Console, to oto ich lista:

RC_SendBuf: AnsiString;
procedure RC_Run(Command: AnsiString);
procedure RC_LineIn(s: AnsiString);
var       RC_LineOut: procedure(s: AnsiString);
buf: array [0..bufsize-1] of AnsiChar;
si: tSTARTUPINFOA;
GetStartupInfoA(si);
CreateProcessA(PAnsiChar(command),

Tak przy okazji - znalazłem serię znakomitych artykułów (po angielsku), dzięki którym można szybko podłapać o co chodzi z tym Unicode (dopóki nie zainstalowałem Delphi 2010, to omijałem te tematy):
Część 1
Część 2
Część 3

Dziękuję wszystkim za pomoc, program działa teraz jak należy :).
Pozdrawiam

0

dzieki...okazało sie ze nie trzeba zmieniać CreateProcessw na CreateProcessa
chodziło o buffor: Bufor:array[0..127] of ansichar;

1 użytkowników online, w tym zalogowanych: 0, gości: 1