- 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.
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.
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
WidestringandPWideChar. Nowaday Delphi versions default to Unicode, so this would beStringandPChar - For the same reason the WIDE functions (ending with
W) must be called. - I execute
cmd.exe /Ubecause 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 asChar. 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.