unit ExifTool;
interface
uses Classes, SysUtils, Vcl.Forms, Util;
function ExecuteExifTool(const Command : WideString;
Output, Errors : TStrings) : TFunctionResult;
implementation
uses Windows, MainForm, StatusForm;
function ExecuteExifTool(const Command : WideString;
Output, Errors : TStrings) : TFunctionResult;
var
Buffer : array[0..255] of Byte;
CreationFlags : DWORD;
NumberOfBytesRead : DWORD;
PipeErrorsRead : THandle;
PipeErrorsWrite : THandle;
PipeOutputRead : THandle;
PipeOutputWrite : THandle;
ProcessInfo : TProcessInformation;
SecurityAttr : TSecurityAttributes;
StartupInfo : TStartupInfo;
tmpWaitR : DWORD;
c: integer;
Str: String;
OutStream, ErrStream: TMemoryStream;
Ok: bool;
begin
c:= 0;
FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
SecurityAttr.nLength := SizeOf(TSecurityAttributes);
SecurityAttr.bInheritHandle := True;
SecurityAttr.lpSecurityDescriptor := nil;
CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);
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;
{ Start the child process. }
Ok:= CreateProcessW(nil, // No module name (use command line)
(PWideChar(Command)), // Command line
nil, // Process handle not inheritable
nil, // Thread handle not inheritable
True, // handles are inherited
CreationFlags, // creation flags
nil, // Use parent's environment block
nil, // Use parent's starting directory
StartupInfo, // Pointer to STARTUPINFO structure
ProcessInfo); // Pointer to PROCESS_INFORMATION structure
if Ok then
begin
Result:= frOk;
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsWrite);
OutStream:= TMemoryStream.Create; ErrStream:= TmemoryStream.Create;
try
repeat // until (tmpWaitR <> WAIT_TIMEOUT)
tmpWaitR := WaitForSingleObject(ProcessInfo.hProcess, 100);
NumberOfBytesRead := 0;
if PeekNamedPipe(PipeOutputRead, nil, 0, nil, @NumberOfBytesRead, nil) and (NumberOfBytesRead > 0) then begin
while ReadFile(PipeOutputRead, Buffer, Length(Buffer)-1, NumberOfBytesRead, nil) and not frmStatus.Cancel do begin
OutStream.Write(Buffer, NumberOfBytesRead);
{ Show where we are in the process }
for var i:= 0 to NumberOfBytesRead -1 do
begin
Str:= Str + Char(Buffer[i]);
if Char(Buffer[i]) = #$0A then
begin
if Str.Contains('SourceFile') then
begin
Inc(c);
//if c mod 10 = 0 then // restrict screen update for improving performance
//begin
Str:= UTF8ToString(RawByteString(Str));
frmStatus.FilesProcessed:= c;
// Remove '",#0A#0D' at the end of the jsonstring and ' "SourceFile": "' at the beginning
Str:= Str.Remove(Str.Length-4,4).Remove(0,17);
frmStatus.ProcessingFile:= Str.Replace('/','\');
frmMain.StatusBar.Panels[3].Text:= c.ToString;
frmMain.StatusBar.Panels[9].Text:= TruncateFileName(frmStatus.ProcessingFile, (frmMain.StatusBar.Panels[9].Width-10) div 9);
Application.ProcessMessages;
//end;
end;
Str:= '';
end;
end;
end; // while ReadFile
end; // if PeekNamedPipe
NumberOfBytesRead := 0;
if PeekNamedPipe(PipeErrorsRead, nil, 0, nil, @NumberOfBytesRead, nil) and (NumberOfBytesRead > 0) then begin
while ReadFile(PipeErrorsRead, Buffer, Length(Buffer)-1, NumberOfBytesRead, nil) do begin
ErrStream.Write(Buffer, NumberOfBytesRead);
end;
end;
application.ProcessMessages;
until (tmpWaitR <> WAIT_TIMEOUT) or frmStatus.Cancel;
{ Show final values }
if frmStatus.Cancel then
Result:= frCancel
else
begin
frmStatus.FilesProcessed:= c;
frmStatus.ProcessingFile:= Str;
frmMain.StatusBar.Panels[3].Text:= c.ToString;
frmMain.StatusBar.Panels[9].Text:= TruncateFileName(Str, (frmMain.StatusBar.Panels[9].Width-10) div 9);
end;
OutStream.Position:=0; Output.LoadFromStream(OutStream);
ErrStream.Position:=0; Errors.LoadFromStream(ErrStream);
finally
OutStream.Free;
ErrStream.free;
end;
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
CloseHandle(PipeOutputRead);
CloseHandle(PipeErrorsRead);
end
else
begin
CloseHandle(PipeOutputRead);
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsRead);
CloseHandle(PipeErrorsWrite);
Result:= frError;
end; // if result
end;
initialization
finalization
end.