AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Kommando chcp mit CreateProcess ausführen
Thema durchsuchen
Ansicht
Themen-Optionen

Kommando chcp mit CreateProcess ausführen

Ein Thema von Photoner · begonnen am 4. Apr 2016 · letzter Beitrag vom 4. Apr 2016
Antwort Antwort
Photoner

Registriert seit: 6. Dez 2012
Ort: Nürnberg
103 Beiträge
 
Delphi 10.1 Berlin Starter
 
#1

Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 13:42
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 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 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>
Chris
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 14:01
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
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Photoner

Registriert seit: 6. Dez 2012
Ort: Nürnberg
103 Beiträge
 
Delphi 10.1 Berlin Starter
 
#3

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 14:08
Bei GetConsoleOutput('cmd /c chcp',outp,err) ist outp.Text ein leerer String. Klappt das bei dir?
Chris
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#4

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 14:32
Klappt das bei dir?
Ja, bei mir enthält die Output StringList wie erwartet den Text "Aktive Codepage: 850.".
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Photoner

Registriert seit: 6. Dez 2012
Ort: Nürnberg
103 Beiträge
 
Delphi 10.1 Berlin Starter
 
#5

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 15:24

Ja, bei mir enthält die Output StringList wie erwartet den Text "Aktive Codepage: 850.".
Bei mir leider nicht. Außerdem liefert mir 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 procedure Write(const Buf; Count: Longint); zu erledigen wurde es in einer Schleife mit 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
Chris
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.116 Beiträge
 
Delphi 12 Athens
 
#6

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 15:26
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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu ( 4. Apr 2016 um 16:18 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.680 Beiträge
 
Delphi 5 Professional
 
#7

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 15:53
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
  Mit Zitat antworten Zitat
Photoner

Registriert seit: 6. Dez 2012
Ort: Nürnberg
103 Beiträge
 
Delphi 10.1 Berlin Starter
 
#8

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 16:06
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. System.DefaultSystemCodePage wird aber mit GetACP gesetzt. So lernt man dazu .

P.S.: Delphi Konsolenanwendungen verwenden damit auch die CP 1252. Wäre es dann nicht konsequenter GetOEMCP zu verwenden?
Chris
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.116 Beiträge
 
Delphi 12 Athens
 
#9

AW: Kommando chcp mit CreateProcess ausführen

  Alt 4. Apr 2016, 16:16
Jo, auch grade gemerkt.
War mir sicher, dass das sowas wie ECHO war.

P.S.: Delphi Konsolenanwendungen verwenden damit auch die CP 1252. Wäre es dann nicht konsequenter GetOEMCP zu verwenden?
Eigentlich schon.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:19 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