Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Kommando chcp mit CreateProcess ausführen (https://www.delphipraxis.net/188751-kommando-chcp-mit-createprocess-ausfuehren.html)

Photoner 4. Apr 2016 13:42

Kommando chcp mit CreateProcess ausführen
 
Hi,

Ich habe gerade ein Problem das Codepages betrifft. Weil das Kommando "chcp" in einer von Hand geöffneten Konsole etwas anderes angibt als die Funktion
Delphi-Quellcode:
GetACP
war ich eine ganze Weile auf der falschen Fährte. Ich wollte das auch einmal parallel in einer Konsole sehen und habe dafür eine kleine Anwendung zusammengesteckt. Ich komme aber nicht darauf, warum diese keine richtige Ausgabe für das Kommando "chcp" liefert. Habt ihr eine Idee?

Der Code:

Die Funktion
Delphi-Quellcode:
function GetConsoleOutput(Command : string;Output, Errors : TStringList) : Boolean;
ist von Delphitreff.de.

Delphi-Quellcode:
function GetConsoleOutput(Command : string;
                          Output, Errors : TStringList) : Boolean;
var
  Buffer           : array[0..255] of Char;
  CreationFlags    : DWORD;
  NumberOfBytesRead : DWORD;
  PipeErrorsRead   : THandle;
  PipeErrorsWrite  : THandle;
  PipeOutputRead   : THandle;
  PipeOutputWrite  : THandle;
  ProcessInfo      : TProcessInformation;
  SecurityAttr     : TSecurityAttributes;
  StartupInfo      : TStartupInfo;
  Stream           : TMemoryStream;
begin
  //Initialisierung ProcessInfo
  FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);

  //Initialisierung SecurityAttr
  FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
  SecurityAttr.nLength             := SizeOf(TSecurityAttributes);
  SecurityAttr.bInheritHandle      := True;
  SecurityAttr.lpSecurityDescriptor := nil;

  //Pipes erzeugen
  CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
  CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);

  //Initialisierung StartupInfo
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb         := SizeOf(TStartupInfo);
  StartupInfo.hStdInput  := 0;
  StartupInfo.hStdOutput := PipeOutputWrite;
  StartupInfo.hStdError  := PipeErrorsWrite;
  StartupInfo.wShowWindow := SW_HIDE;
  StartupInfo.dwFlags    := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;

  CreationFlags := CREATE_DEFAULT_ERROR_MODE or
                   CREATE_NEW_CONSOLE or
                   NORMAL_PRIORITY_CLASS;

  // Folgende Zeile ist nur für Delphi ab 2009 erforderlich:
{$IF System.CompilerVersion>=12}
  UniqueString(Command);
{$ENDIF}

  if CreateProcess(nil,
                   PChar(Command),
                   nil,
                   nil,
                   True,
                   CreationFlags,
                   nil,
                   nil,
                   StartupInfo,
                   ProcessInfo) then
  begin
    Result := True;
    //Write-Pipes schließen
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsWrite);

    //Ausgabe Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil) do
      begin
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Output.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeOutputRead);

    //Fehler Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil) do
      begin
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Errors.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeErrorsRead);

    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    CloseHandle(ProcessInfo.hProcess);
  end
  else
  begin
    Result := False;
    CloseHandle(PipeOutputRead);
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsRead);
    CloseHandle(PipeErrorsWrite);
  end;
end;
Link dazu:
http://www.delphi-treff.de/tipps-tri...ramm-anzeigen/

Delphi-Quellcode:
program KonsolenTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  WinAPI.Windows,
  System.Classes,
  Console in 'Console.pas'; //für Funktion GetConsoleOutput

var
  outp : TStringList;
  err  : TStringList;
  line : String;
begin
  outp := TStringList.Create;
  err  := TStringList.Create;
  try
    try
      { TODO -oUser -cConsole Main : Code hier einfügen }
      WriteLn('GetACP Result:');
      WriteLn(IntToStr(GetACP));
      WriteLn('"cmd chcp" with CreateProcess:');
      if GetConsoleOutput('cmd chcp',outp,err) then
      begin
        WriteLn('OutPut:');
        Writeln(outp.Text);
        WriteLn('Errors:');
        Writeln(err.Text);
      end
      else
        Writeln('CreateProcess failed');
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
    ReadLn(line);
  finally
    outp.Free;
    err.Free;
  end;
end.
Ergebnis ist nur der Standardtext (Windows Version + Copyright + Pfad):

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Alle Rechte vorbehalten.

<aktueller Pfad == Pfad zu Konsolentest.exe>

Zacherl 4. Apr 2016 14:01

AW: Kommando chcp mit CreateProcess ausführen
 
Wenn du in der CMD direkt was ausführen willst, musst du beim Starten noch den /C Switch übergeben, wenn ich mich recht erinnere, also
Code:
cmd /C chcp

Photoner 4. Apr 2016 14:08

AW: Kommando chcp mit CreateProcess ausführen
 
Bei
Delphi-Quellcode:
GetConsoleOutput('cmd /c chcp',outp,err)
ist outp.Text ein leerer String. Klappt das bei dir?

Zacherl 4. Apr 2016 14:32

AW: Kommando chcp mit CreateProcess ausführen
 
Zitat:

Zitat von Photoner (Beitrag 1334601)
Klappt das bei dir?

Ja, bei mir enthält die Output StringList wie erwartet den Text "Aktive Codepage: 850.".

Photoner 4. Apr 2016 15:24

AW: Kommando chcp mit CreateProcess ausführen
 
Zitat:

Zitat von Zacherl (Beitrag 1334604)

Ja, bei mir enthält die Output StringList wie erwartet den Text "Aktive Codepage: 850.".

Bei mir leider nicht. Außerdem liefert mir
Delphi-Quellcode:
GetAcp
ein Ergebnis von 1252.

Ich habe mit dem Problem schon zu viel Zeit verbracht...

Ganz blöde Geschichte, die mir ein Vorgänger hinterlassen hat:

Die Intention war mit einem TWriter Objekt X Bytes eines Records zu schreiben.

Anstatt das mit einem Pointer und der Funktion
Delphi-Quellcode:
procedure Write(const Buf; Count: Longint);
zu erledigen wurde es in einer Schleife mit
Delphi-Quellcode:
procedure WriteChar(Value: Char);
gemacht (Adresse als PChar casten und in der Schleife einen Char schreiben und den Pointer inkrementieren).

Aus dem Char wird intern entweder ein AnsiString (vor Delphi 2009) oder ein UTF8String. Das zweitere ist aber abhängig von der derzeitigen Codepage und damit für verschiedene Regioneneinstellungen unterschiedlich.

Ich kann die Warnung von MS nur voll unterstützen:

Note The ANSI code pages can be different on different computers, or can be changed for a single computer, leading to data corruption. For the most consistent results, applications should use Unicode, such as UTF-8 or UTF-16, instead of a specific code page, unless legacy standards or data formats prevent the use of Unicode

himitsu 4. Apr 2016 15:26

AW: Kommando chcp mit CreateProcess ausführen
 
Zitat:

Außerdem liefert mir GetAcp ein Ergebnis von 1252.
Was erwartet man da auch Anderes?

MSDN-Library durchsuchenGetACP Retrieves the current Windows ANSI code page identifier for the operating system.
Und nicht von deinem Programm/Thread. (GetThreadLocale)



CreateProcess führt nur Programme und Skripte aus.

CHCP ist aber kein Programm, sondern ein "Befehl", den NUR das Konsolenprogramm CMD.exe kennt.
https://technet.microsoft.com/en-us/.../bb490874.aspx
Also mußt due die CMD.exe aufrufen und der sagen, dass sie den Befehl ausführen soll.



GUI-Anwendungen werden von Windows mit der CodePage für ANSI initialisiert
und Konsolenanwendungen mit der OEM-Codepage,
weswegen du in deiner VCL-Anwendung (GUI) natürlich was Anderes bekommst.

Man kann das zwar umschalten,
MSDN-Library durchsuchenSetFileApisToOEM MSDN-Library durchsuchenSetFileApisToANSI MSDN-Library durchsuchenAreFileApisANSI
aber man kann auch einfach nur das Gewünschte benutzen/abfragen.
MSDN-Library durchsuchenCP_ACP MSDN-Library durchsuchenGetACP
MSDN-Library durchsuchenCP_OEMCP MSDN-Library durchsuchenGetOEMCP

Und 850 ist die OEM-CodePage. (DOS/Konsole)
https://de.wikipedia.org/wiki/Codepage_850

Delphi-Quellcode:
type
  OEMString = type AnsiString(CP_OEMCP);

var
  IchBinOEM: OEMString;

IchBinOEM := 'Hallöle';
WriteLn(IchBinOEM);
Oder du kannst WideCharToMultiByte verwenden und benutzt die OEM-CodePage als Ziel.

Dalai 4. Apr 2016 15:53

AW: Kommando chcp mit CreateProcess ausführen
 
Zitat:

Zitat von himitsu (Beitrag 1334607)
CHCP ist aber kein Programm, sondern ein "Befehl", den NUR das Konsolenprogramm CMD.exe kennt.

Stimmt nicht: Wenn man %SystemRoot%\system32\chcp.com umbenennt, funktioniert kein chcp mehr auf einer CMD.

MfG Dalai

Photoner 4. Apr 2016 16:06

AW: Kommando chcp mit CreateProcess ausführen
 
Zitat:

Zitat von himitsu (Beitrag 1334607)
Zitat:

Außerdem liefert mir GetAcp ein Ergebnis von 1252.
Was erwartet man da auch Anderes?

Ich hatte 850 erwartet, weil chcp mir das lieferte und habe versucht manuell mit dieser Codepage zu konvertieren.
Delphi-Quellcode:
System.DefaultSystemCodePage
wird aber mit
Delphi-Quellcode:
GetACP
gesetzt. So lernt man dazu :-D.

P.S.: Delphi Konsolenanwendungen verwenden damit auch die CP 1252. Wäre es dann nicht konsequenter GetOEMCP zu verwenden?

himitsu 4. Apr 2016 16:16

AW: Kommando chcp mit CreateProcess ausführen
 
Jo, auch grade gemerkt. :oops:
War mir sicher, dass das sowas wie ECHO war.

Zitat:

Zitat von Photoner (Beitrag 1334610)
P.S.: Delphi Konsolenanwendungen verwenden damit auch die CP 1252. Wäre es dann nicht konsequenter GetOEMCP zu verwenden?

Eigentlich schon.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz