// 유니코드 커맨드라인 실행하고 출력값 얻어오자. .
// 주의! 콘솔로 출력되는 문자열은 안시문자열이다.
// GetConsoleOutputCP(), SetConsoleOutputCP() API 참조.
type
TRunCmdCallback = procedure (const aRetStr: AnsiString; aParam: Pointer);
// 실행결과 문자열을 RunCmdCallback으로 조금씩 받아 조립할 때 사용.
// 콜백의 호출 주기는 상수 BUFFER_SIZE 의 값과 WaitForSingleObject() 의 두번째 인자인 시간값으로 결정된다.
function RunWideCmd(Command: WideString; RunCmdCallback: TRunCmdCallback; Param: Pointer = nil): DWORD; overload;
const
BUFFER_SIZE = 512; // 버퍼 크기가 작으면 RunCmdCallback이 좀 더 자주 일어남
var
hReadPipe : THandle;
hWritePipe : THandle;
SI : TStartUpInfo;
PI : TProcessInformation;
SA : TSecurityAttributes;
SD : TSecurityDescriptor;
BytesRead : DWORD;
Avail, wrResult : DWORD;
RetStr: AnsiString;
begin
{ Dos Application }
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@SD, True, nil, False);
SA.nLength := SizeOf(SA);
SA.lpSecurityDescriptor := @SD;
SA.bInheritHandle := True;
CreatePipe(hReadPipe, hWritePipe, @SA, BUFFER_SIZE);
end else
CreatePipe(hReadPipe, hWritePipe, nil, BUFFER_SIZE);
try
{ STARTUPINFO를 설정한다. }
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(TStartUpInfo);
SI.wShowWindow := SW_HIDE;
SI.dwFlags := STARTF_USESHOWWINDOW;
{ STARTF_USESTDHANDLES 를 빼면 PIPE로 입출력이 Redirect 되지 않는다. }
SI.dwFlags := SI.dwFlags or STARTF_USESTDHANDLES;
SI.hStdOutput := hWritePipe;
SI.hStdError := hWritePipe;
{ Process를 생성한다. }
if CreateProcessW(nil, PWChar(Command), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
begin
Result := 0;
while Result = 0 do
begin
// 대기시간이 길면 반응이 느리다. 짧으면 콜백이 그만큼 자주 일어남. 적당히 조절해 사용.
wrResult := WaitForSingleObject(PI.hProcess, 5);
{ Pipe에 출력된 내용이 있는 지 조사한다. }
if PeekNamedPipe(hReadPipe, nil, 0, nil, @Avail, nil) then
begin
if Avail > 0 then
begin
{ Redirect된 화면 내용을 얻어낸다. }
SetLength(RetStr, Integer(Avail));
ReadFile(hReadPipe, PAnsiChar(RetStr)[0], Avail, BytesRead, nil);
if Assigned(RunCmdCallback) then RunCmdCallback(RetStr, Param);
end;
end;
{ WAIT_TIMEOUT이 아니면 루프를 마친다. }
if wrResult <> WAIT_TIMEOUT then Result := 1;
end;
{ 실행이 끝났다.. }
GetExitCodeProcess(PI.hProcess, Result);
CloseHandle(PI.hProcess);
CloseHandle(PI.hThread);
end;
finally
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
end;
end;
// 실행결과 문자열 전체를 한꺼번에 리턴받을 때 사용.
function RunWideCmd(Command : WideString; var RetStr: AnsiString): DWORD; overload;
const
BUFFER_SIZE = 512;
var
hReadPipe : THandle;
hWritePipe : THandle;
SI : TStartUpInfo;
PI : TProcessInformation;
SA : TSecurityAttributes;
SD : TSecurityDescriptor;
BytesRead : DWORD;
Avail, wrResult : DWORD;
LastSize: Integer;
begin
{ Dos Application }
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@SD, True, nil, False);
SA.nLength := SizeOf(SA);
SA.lpSecurityDescriptor := @SD;
SA.bInheritHandle := True;
CreatePipe(hReadPipe, hWritePipe, @SA, BUFFER_SIZE);
end else
CreatePipe(hReadPipe, hWritePipe, nil, BUFFER_SIZE);
try
{ STARTUPINFO를 설정한다. }
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(TStartUpInfo);
SI.wShowWindow := SW_HIDE;
SI.dwFlags := STARTF_USESHOWWINDOW;
{ STARTF_USESTDHANDLES 를 빼면 PIPE로 입출력이 Redirect 되지 않는다. }
SI.dwFlags := SI.dwFlags or STARTF_USESTDHANDLES;
SI.hStdOutput := hWritePipe;
SI.hStdError := hWritePipe;
{ Process를 생성한다. }
if CreateProcessW(nil, PWideChar(Command), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
begin
Result := 0;
while Result = 0 do
begin
wrResult := WaitForSingleObject(PI.hProcess, 5);
{ Pipe에 출력된 내용이 있는 지 조사한다. }
if PeekNamedPipe(hReadPipe, nil, 0, nil, @Avail, nil) then
begin
if Avail > 0 then
begin
{ Redirect된 화면 내용을 얻어낸다. }
LastSize := Length(RetStr);
SetLength(RetStr, LastSize + Integer(Avail));
ReadFile(hReadPipe, PChar(RetStr)[LastSize], Avail, BytesRead, nil);
end;
end;
{ WAIT_TIMEOUT이 아니면 루프를 마친다. }
if wrResult <> WAIT_TIMEOUT then Result := 1;
end;
{ 실행이 끝났다.. }
GetExitCodeProcess(PI.hProcess, Result);
CloseHandle(PI.hProcess);
CloseHandle(PI.hThread);
end;
finally
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
end;
end;