Skip to main content
Stack Overflow
  1. About
  2. For Teams

Return to Answer

missing FreeMem(); finally see a difference between me executing a CMD.EXE command and OP executing a program
Source Link
AmigoJack
  • 6.4k
  • 2
  • 20
  • 36
...
El día de la bestia (1995)
Jo Nesbø's Headhunters - Hodejegerne (2011)
Léon (Directors Cut) (1994)
Sånger från andra våningen - Songs from the Second Floor (2000)
دختری در شب تنها به خانه می‌رود - A Girl Walks Home Alone at Night (2014)
アウトレイジ ビヨンド - Outrage - Beyond (2012)
アキレスと亀 - Achilles and the Tortoise (2008)
葉問3 - Ip Man 3 (2015)
賽德克•巴萊 - Warriors of the Rainbow - Seediq Bale (2011)
살인의 추억 - Memories of Murder (2003)
신세계 - New World (2013)
...

Screenshot of Unicode console output

...
El día de la bestia (1995)
Jo Nesbø's Headhunters - Hodejegerne (2011)
Léon (Directors Cut) (1994)
Sånger från andra våningen - Songs from the Second Floor (2000)
دختری در شب تنها به خانه می‌رود - A Girl Walks Home Alone at Night (2014)
アウトレイジ ビヨンド - Outrage - Beyond (2012)
アキレスと亀 - Achilles and the Tortoise (2008)
葉問3 - Ip Man 3 (2015)
賽德克•巴萊 - Warriors of the Rainbow - Seediq Bale (2011)
살인의 추억 - Memories of Murder (2003)
신세계 - New World (2013)
...

Screenshot of Unicode console output

function StringToWideString
( p: PAnsiChar // Source to convert
; iLenSrc: Integer // Source's length
; iSrcCodePage: DWord= CP_UTF8 // Source codepage
): WideString; // Target is UTF-16
var
 iLenDest: Integer;
begin
 iLenDest:= MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, nil, 0 );
 SetLength( result, iLenDest );
 if iLenDest> 0 then // Otherwise we get ERROR_INVALID_PARAMETER
 if MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, PWideChar(result), iLenDest )= 0 then begin
 result:= '';
 end;
end;
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
; bExpectUtf8: Boolean // Does the text make no sense? Then set this to TRUE.
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pWide, pCmd: PWideChar; // Read buffer in UTF-16; Command line for process creation
 pAnsi: PAnsiChar; // Read buffer in UTF-8
 pBuf: Pointer; // Read buffer in general, either ANSI or WIDE
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 pCmd:= nil;

 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 1, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 if bExpectUtf8 then begin
 pAnsi:= pBuf;
 pAnsi[iRead]:= #0;
 sOut:= sOut+ StringToWideString( pAnsi, iRead ); // Convert UTF-8 into UTF-16
 end else begin
 pWide:= pBuf;
 pWide[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pWide; // Add to overall output
 end;
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
 if pCmd<> nil then FreeMem( pCmd );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
 bUtf8: Boolean;
begin
 // In theory this should turn TRUE for you and FALSE for me.
 // If it doesn't work, of course, try setting it hardcoded to either TRUE or FALSE.
 bUtf8:= GetACP()= CP_UTF8;
 if GetCmdOutput
 ( 'cmd.exe /U /C dir /B M:\IN\*' // What should be executed?
 , sOut // Retrieving the output
 , bUtf8 // Will the output be UTF-16 or UTF-8?
 )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

Update: The above only works on console commands (such as dir) - it doesn't turn a program (an .exe) into magically outputting Unicode instead of Ansi. If you want to perform this on a console program instead of one of cmd.exe's commands you will most likely still get Ansi output, no matter what. How to write such a console program is a different topic - and then it also doesn't need cmd.exe as shell to be started in.

...
El día de la bestia (1995)
Jo Nesbø's Headhunters - Hodejegerne (2011)
Léon (Directors Cut) (1994)
Sånger från andra våningen - Songs from the Second Floor (2000)
دختری در شب تنها به خانه می‌رود - A Girl Walks Home Alone at Night (2014)
アウトレイジ ビヨンド - Outrage - Beyond (2012)
アキレスと亀 - Achilles and the Tortoise (2008)
葉問3 - Ip Man 3 (2015)
賽德克•巴萊 - Warriors of the Rainbow - Seediq Bale (2011)
살인의 추억 - Memories of Murder (2003)
신세계 - New World (2013)
...

Screenshot of Unicode console output

function StringToWideString
( p: PAnsiChar // Source to convert
; iLenSrc: Integer // Source's length
; iSrcCodePage: DWord= CP_UTF8 // Source codepage
): WideString; // Target is UTF-16
var
 iLenDest: Integer;
begin
 iLenDest:= MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, nil, 0 );
 SetLength( result, iLenDest );
 if iLenDest> 0 then // Otherwise we get ERROR_INVALID_PARAMETER
 if MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, PWideChar(result), iLenDest )= 0 then begin
 result:= '';
 end;
end;
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
; bExpectUtf8: Boolean // Does the text make no sense? Then set this to TRUE.
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pWide, pCmd: PWideChar; // Read buffer in UTF-16; Command line for process creation
 pAnsi: PAnsiChar; // Read buffer in UTF-8
 pBuf: Pointer; // Read buffer in general, either ANSI or WIDE
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 1, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 if bExpectUtf8 then begin
 pAnsi:= pBuf;
 pAnsi[iRead]:= #0;
 sOut:= sOut+ StringToWideString( pAnsi, iRead ); // Convert UTF-8 into UTF-16
 end else begin
 pWide:= pBuf;
 pWide[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pWide; // Add to overall output
 end;
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
 bUtf8: Boolean;
begin
 // In theory this should turn TRUE for you and FALSE for me.
 // If it doesn't work, of course, try setting it hardcoded to either TRUE or FALSE.
 bUtf8:= GetACP()= CP_UTF8;
 if GetCmdOutput
 ( 'cmd.exe /U /C dir /B M:\IN\*' // What should be executed?
 , sOut // Retrieving the output
 , bUtf8 // Will the output be UTF-16 or UTF-8?
 )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;
...
El día de la bestia (1995)
Jo Nesbø's Headhunters - Hodejegerne (2011)
Léon (Directors Cut) (1994)
Sånger från andra våningen - Songs from the Second Floor (2000)
دختری در شب تنها به خانه می‌رود - A Girl Walks Home Alone at Night (2014)
アウトレイジ ビヨンド - Outrage - Beyond (2012)
アキレスと亀 - Achilles and the Tortoise (2008)
葉問3 - Ip Man 3 (2015)
賽德克•巴萊 - Warriors of the Rainbow - Seediq Bale (2011)
살인의 추억 - Memories of Murder (2003)
신세계 - New World (2013)
...

Screenshot of Unicode console output

function StringToWideString
( p: PAnsiChar // Source to convert
; iLenSrc: Integer // Source's length
; iSrcCodePage: DWord= CP_UTF8 // Source codepage
): WideString; // Target is UTF-16
var
 iLenDest: Integer;
begin
 iLenDest:= MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, nil, 0 );
 SetLength( result, iLenDest );
 if iLenDest> 0 then // Otherwise we get ERROR_INVALID_PARAMETER
 if MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, PWideChar(result), iLenDest )= 0 then begin
 result:= '';
 end;
end;
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
; bExpectUtf8: Boolean // Does the text make no sense? Then set this to TRUE.
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pWide, pCmd: PWideChar; // Read buffer in UTF-16; Command line for process creation
 pAnsi: PAnsiChar; // Read buffer in UTF-8
 pBuf: Pointer; // Read buffer in general, either ANSI or WIDE
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 pCmd:= nil;

 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 1, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 if bExpectUtf8 then begin
 pAnsi:= pBuf;
 pAnsi[iRead]:= #0;
 sOut:= sOut+ StringToWideString( pAnsi, iRead ); // Convert UTF-8 into UTF-16
 end else begin
 pWide:= pBuf;
 pWide[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pWide; // Add to overall output
 end;
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
 if pCmd<> nil then FreeMem( pCmd );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
 bUtf8: Boolean;
begin
 // In theory this should turn TRUE for you and FALSE for me.
 // If it doesn't work, of course, try setting it hardcoded to either TRUE or FALSE.
 bUtf8:= GetACP()= CP_UTF8;
 if GetCmdOutput
 ( 'cmd.exe /U /C dir /B M:\IN\*' // What should be executed?
 , sOut // Retrieving the output
 , bUtf8 // Will the output be UTF-16 or UTF-8?
 )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

Update: The above only works on console commands (such as dir) - it doesn't turn a program (an .exe) into magically outputting Unicode instead of Ansi. If you want to perform this on a console program instead of one of cmd.exe's commands you will most likely still get Ansi output, no matter what. How to write such a console program is a different topic - and then it also doesn't need cmd.exe as shell to be started in.

unexpected ACP
Source Link
AmigoJack
  • 6.4k
  • 2
  • 20
  • 36
function StringToWideString
( p: PAnsiChar // Source to convert
; iLenSrc: Integer // Source's length
; iSrcCodePage: DWord= CP_UTF8 // Source codepage
): WideString; // Target is UTF-16
var
 iLenDest: Integer;
begin
 iLenDest:= MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, nil, 0 );
 SetLength( result, iLenDest );
 if iLenDest> 0 then // Otherwise we get ERROR_INVALID_PARAMETER
 if MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, PWideChar(result), iLenDest )= 0 then begin
 result:= '';
 end;
end;
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
; bExpectUtf8: Boolean // Does the text make no sense? Then set this to TRUE.
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pBufpWide, pCmd: PWideChar; // Read buffer;buffer in UTF-16; Command line for process creation
 pAnsi: PAnsiChar; // Read buffer in UTF-8
 pBuf: Pointer; // Read buffer in general, either ANSI or WIDE
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 21, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 pBuf[iReadif bExpectUtf8 then begin
 pAnsi:= pBuf;
 pAnsi[iRead]:= #0;
 sOut:= sOut+ StringToWideString( pAnsi, iRead ); // Convert UTF-8 into UTF-16
 end else begin
  pWide:= pBuf;
 pWide[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pBuf;pWide; // Add to overall output
 end;
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
 bUtf8: Boolean;
begin
 // In theory this should turn TRUE for you and FALSE for me.
 // If it doesn't work, of course, try setting it hardcoded to either TRUE or FALSE.
 bUtf8:= GetACP()= CP_UTF8;
 if GetCmdOutput( 'cmd.exe /U /C dir /B M:\IN\*' // What should be executed?
 , sOut // Retrieving the output
 , bUtf8 // Will the output be UTF-16 or UTF-8?
  )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

It should also compile for newer Delphi versions. However, if your Windows system's default codepage or your process is set to always use UTF-8 in API calls, you have to call my function with TRUE instead of FALSE as third parameter - that's why I must check the active codepage (ACP) first.

DOS never existed in Windows NT, the "black" window is not DOS.

function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pBuf, pCmd: PWideChar; // Read buffer; Command line for process creation
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 2, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 pBuf[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pBuf; // Add to overall output
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
begin
 if GetCmdOutput( 'cmd.exe /U /C dir /B M:\IN\*', sOut )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

It should also compile for newer Delphi versions. DOS never existed in Windows NT, the "black" window is not DOS.

function StringToWideString
( p: PAnsiChar // Source to convert
; iLenSrc: Integer // Source's length
; iSrcCodePage: DWord= CP_UTF8 // Source codepage
): WideString; // Target is UTF-16
var
 iLenDest: Integer;
begin
 iLenDest:= MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, nil, 0 );
 SetLength( result, iLenDest );
 if iLenDest> 0 then // Otherwise we get ERROR_INVALID_PARAMETER
 if MultiByteToWideChar( iSrcCodePage, 0, p, iLenSrc, PWideChar(result), iLenDest )= 0 then begin
 result:= '';
 end;
end;
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
; bExpectUtf8: Boolean // Does the text make no sense? Then set this to TRUE.
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pWide, pCmd: PWideChar; // Read buffer in UTF-16; Command line for process creation
 pAnsi: PAnsiChar; // Read buffer in UTF-8
 pBuf: Pointer; // Read buffer in general, either ANSI or WIDE
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 1, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 if bExpectUtf8 then begin
 pAnsi:= pBuf;
 pAnsi[iRead]:= #0;
 sOut:= sOut+ StringToWideString( pAnsi, iRead ); // Convert UTF-8 into UTF-16
 end else begin
  pWide:= pBuf;
 pWide[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pWide; // Add to overall output
 end;
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
 bUtf8: Boolean;
begin
 // In theory this should turn TRUE for you and FALSE for me.
 // If it doesn't work, of course, try setting it hardcoded to either TRUE or FALSE.
 bUtf8:= GetACP()= CP_UTF8;
 if GetCmdOutput( 'cmd.exe /U /C dir /B M:\IN\*' // What should be executed?
 , sOut // Retrieving the output
 , bUtf8 // Will the output be UTF-16 or UTF-8?
  )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

It should also compile for newer Delphi versions. However, if your Windows system's default codepage or your process is set to always use UTF-8 in API calls, you have to call my function with TRUE instead of FALSE as third parameter - that's why I must check the active codepage (ACP) first.

DOS never existed in Windows NT, the "black" window is not DOS.

Source Link
AmigoJack
  • 6.4k
  • 2
  • 20
  • 36

It works for me using Delphi 7 in Windows 7, giving the following output:

...
El día de la bestia (1995)
Jo Nesbø's Headhunters - Hodejegerne (2011)
Léon (Directors Cut) (1994)
Sånger från andra våningen - Songs from the Second Floor (2000)
دختری در شب تنها به خانه می‌رود - A Girl Walks Home Alone at Night (2014)
アウトレイジ ビヨンド - Outrage - Beyond (2012)
アキレスと亀 - Achilles and the Tortoise (2008)
葉問3 - Ip Man 3 (2015)
賽德克•巴萊 - Warriors of the Rainbow - Seediq Bale (2011)
살인의 추억 - Memories of Murder (2003)
신세계 - New World (2013)
...

Screenshot of Unicode console output

My major differences are:

  • Delphi 7 still defaults to ANSI instead of WIDE, hence I have to use Widestring and PWideChar. Nowaday Delphi versions default to Unicode, so this would be String and PChar
  • For the same reason the WIDE functions (ending with W) must be called.
  • I execute cmd.exe /U because as per its manual to enable Unicode pipes.
  • Made the buffer of WideChars, too, instead of putting that to bytes only (AnsiChar). For nowadays Delphi versions you should have declared it simply as Char. Most likely this is your fault.
  • Actually looking for errors that may occur.
function GetCmdOutput
( sCmd: Widestring // Command line for process creation
; out sOut: Widestring // Expected console output
): Word; // Flag wise error indicator
const
 BUFLEN= 50000ドル; // 50* 1024= 51200
var
 vSA: TSecurityAttributes; // For pipe creation
 vSI: TStartupInfo; // To indicate pipe usage
 vPI: TProcessInformation; // To later close handles
 hRead, hWrite: THandle; // Pipe
 bRead: Boolean; // Was ReadFile() successful?
 iRead: Cardinal; // How many bytes were read by ReadFile()?
 pBuf, pCmd: PWideChar; // Read buffer; Command line for process creation
label
 Finish;
begin
 // No error occurred yet, no output so far
 result:= 0;
 sOut:= '';
 // Creating 1 pipe with 2 handles: one for reading, other for writing
 vSA.nLength:= SizeOf( vSA );
 vSA.bInheritHandle:= TRUE;
 vSA.lpSecurityDescriptor:= nil;
 if not CreatePipe( hRead, hWrite, @vSA, 0 ) then begin
 result:= 01ドル; // GetLastError() for more details
 exit;
 end;
 // Prepare pipe usage when creating process
 FillChar( vSI, SizeOf( vSI ), 0 );
 vSI.cb:= SizeOf( vSI );
 vSI.dwFlags:= STARTF_USESTDHANDLES;
 vSI.hStdInput:= GetStdHandle( STD_INPUT_HANDLE );
 if vSI.hStdInput= INVALID_HANDLE_VALUE then begin
 result:= 02ドル; // GetLastError() for more details
 goto Finish;
 end;
 vSI.hStdOutput:= hWrite;
 vSI.hStdError:= hWrite;
 // Create process via command line only
 sCmd:= sCmd+ #0; // PWideChar must be NULL terminated
 GetMem( pCmd, 32000 ); // CreateProcessW() expects a writable parameter
 CopyMemory( @pCmd[0], @sCmd[1], Length( sCmd )* 2 ); // Copy bytes from Widestring to PWideChar
 if not CreateProcessW( nil, pCmd, nil, nil, TRUE, 0, nil, nil, vSI, vPI ) then begin
 result:= 04ドル; // GetLastError() for more details
 goto Finish;
 end;
 // Closing write handle of pipe, otherwise reading will block
 if not CloseHandle( hWrite ) then result:= result or 10ドル; // GetLastError() for more details
 hWrite:= 0;
 // Read all console output
 GetMem( pBuf, BUFLEN );
 try
 repeat
 bRead:= ReadFile( hRead, pBuf^, BUFLEN- 2, iRead, nil ); // Leave 2 bytes for NULL terminating WideChar
 if (bRead) and (iRead> 0) then begin
 pBuf[iRead div 2]:= #0; // Last character is NULL
 sOut:= sOut+ pBuf; // Add to overall output
 end;
 until (not bRead) or (iRead= 0);
 finally
 // Release process handles
 if not CloseHandle( vPI.hThread ) then result:= result or 20ドル; // GetLastError() for more details
 if not CloseHandle( vPI.hProcess ) then result:= result or 40ドル; // GetLastError() for more details;
 end;
 FreeMem( pBuf );
Finish:
 // Pipe must always be released
 if hWrite<> 0 then begin
 if not CloseHandle( hWrite ) then result:= result or 80ドル; // GetLastError() for more details
 end;
 if not CloseHandle( hRead ) then result:= result or 100ドル; // GetLastError() for more details
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 sOut: Widestring;
begin
 if GetCmdOutput( 'cmd.exe /U /C dir /B M:\IN\*', sOut )<> 0 then Caption:= 'Error(s) occurred!';
 TntMemo1.Text:= sOut;
end;

It should also compile for newer Delphi versions. DOS never existed in Windows NT, the "black" window is not DOS.

lang-pascal

AltStyle によって変換されたページ (->オリジナル) /