hello.
trying to cover this topic, i decided to use directly pipes in delphi to support unicode files (+paths) in my program.
i am turning to you for help cause i have no more ideas what i can do wrong.
the code i paste below input file 'a.flac' to encoder through pipe, and receive the results through another one. function seems to work, but weirdly stops receiving data after about 98% done. the piece i save on the disc matches with respondent reference WAV file, except it is only a 98% piece. notice: whole flac file IS passed to encoder as i can see in in task manager.
('a.flac' -> within debugging, oryginally file is passed as parameter 'Param')
(i am sure its not text/binary mode issue cause 98% could not be encoded if it was, and, there are single \x0A characters in received output)
//edit3: if you think you can help, here is the link to the packege with the code and everything needed to test it.
function Run_EN(App:Widestring; Param:Widestring) : string;
var
SA : TSecurityAttributes;
SI : TStartupInfo;
PI : TProcessInformation;
StdOutPipeRead,
StdOutPipeWrite,
StdInPipeRead,
StdInPipeWrite,
StdErrPipeRead,
StdErrPipeWrite : THandle;
WasOKwrite,
WasOKread,
ProcessOK : Boolean;
Buffer : array[0..65535] of char; //or bytes, whatever
BytesRead,
BytesWritten,
Code : Cardinal;
F : TTntFileStream;
M : TMemoryStream;
Read : longword;
begin
//SAME1
{SA.nLength:=sizeof(SA);
SA.lpSecurityDescriptor:=nil;
SA.bInheritHandle:=TRUE;
if (CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 65536)and CreatePipe(StdInPipeRead, StdInPipeWrite, @SA, 65536) and CreatePipe(StdErrPipeRead, StdErrPipeWrite, @SA, 65536)) then
begin
ZeroMemory(@SI, sizeof(SI));
with SI do
begin
cb := sizeof(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := StdInPipeRead;
hStdOutput := StdOutPipeWrite;
hStdError := StdErrPipeWrite;
end;}
//\SAME1
//SAME_2: just longer
if (CreatePipe(StdOutPipeRead, StdOutPipeWrite, nil, 65536)and CreatePipe(StdInPipeRead, StdInPipeWrite, nil, 65536) and CreatePipe(StdErrPipeRead, StdErrPipeWrite, nil, 65536)) then
begin
DuplicateHandle(GetCurrentProcess(), StdOutPipeWrite,GetCurrentProcess(), @StdOutPipeWriteDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), StdInPipeRead,GetCurrentProcess(), @StdInPipeReadDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), StdErrPipeWrite,GetCurrentProcess(), @StdErrPipeWriteDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);
ZeroMemory(@SI, sizeof(SI));
with SI do
begin
cb := sizeof(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := StdInPipeReadDup;
hStdOutput := StdOutPipeWriteDup;
hStdError := StdErrPipeWriteDup;
end;
//\SAME_2
ProcessOK:=CreateProcessW(nil,pwidechar('flac.exe -d -o - --totally-silent -'), nil, nil, TRUE, 0, nil, pwidechar(FWorkDir), SI, PI);
//SAME_1
{ CloseHandle(StdInPipeRead);StdInPipeRead:=0;
CloseHandle(StdOutPipeWrite);StdOutPipeWrite:=0;
CloseHandle(StdErrPipeWrite);StdErrPipeWrite:=0;}
//\SAME_1
//SAME_2
CloseHandle(StdInPipeReadDup);StdInPipeReadDup:=0;
CloseHandle(StdOutPipeWriteDup);StdOutPipeWriteDup:=0;
CloseHandle(StdErrPipeWriteDup);StdErrPipeWriteDup:=0;
//\SAME_2
F:=TFileStream.Create('a.flac', fmOpenRead or fmShareDenyWrite);
M:=TMemoryStream.Create;
WasOKwrite:=true;
bytesWritten:=1;
BytesToWrite := F.Read(FileBuffer, 65536);
if ProcessOK then
while (BytesToWrite>0)and(WasOKwrite)and(bytesWritten>0) do
begin
WasOKwrite:=WriteFile(StdInPipeWrite, FileBuffer, BytesToWrite, bytesWritten, nil);
while WaitForSingleObject(PI.hProcess, 20)=WAIT_OBJECT_0 do Application.ProcessMessages;
repeat
if PeekNamedPipe(StdOutPipeRead,nil,0,nil,@BytesToRead,nil) and (BytesToRead>0) then
//Peek must be or hangs
//after ~98% it says '0' bytes left
begin
setlength(PipeBuffer,BytesToRead);
WasOKread:=ReadFile(StdOutPipeRead, PipeBuffer[0], BytesToRead, bytesRead, nil);
Written:=M.Write(PipeBuffer[0], bytesRead);
end
else Break;
until not WasOKread or (BytesRead = 0);
//there is no error anyway
{ repeat
if PeekNamedPipe(StdErrPipeRead,nil,0,nil,@BytesToRead,nil) and (BytesToRead>0) then
WasOKread:=ReadFile(StdErrPipeRead, Buffer, BytesToRead, bytesRead, nil)
else Break;
until not WasOKread or (BytesRead = 0);}
BytesToWrite := F.Read(FileBuffer, 65536);
end;
//save result to compare externally
M.SaveToFile('a.wav');
M.Free;
F.Free;
SetLength(PipeBuffer,0);
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
TerminateProcess(PI.hProcess, Code);
CloseHandle(StdOutPipeRead);
CloseHandle(StdErrPipeRead);
CloseHandle(StdInPipeWrite);
end;
end;
thing that makes me wondering:
in a simplier version of this function, where i only capture the output and pass input to encoder normally
'flac.exe -d -c --totally-silent a.flac'
my 'reading' code looks like this (the simplier only-reading version works fine !!!):
if ProcessOK then
repeat
WasOKread:=ReadFile(StdOutPipeRead, Buffer, 65536, BytesRead, nil);
M.Write(Buffer, bytesRead);
until not WasOKread or (BytesRead = 0);
no peek needed, and the more, when i only use it
if ProcessOK then
repeat
if PeekNamedPipe(StdOutPipeRead,nil,0,nil,@BytesRead,nil) and (BytesRead>0) then
begin
WasOKread:=ReadFile(StdOutPipeRead, Buffer, 65536, BytesRead, nil);
M.Write(Buffer, bytesRead);
end
else break;
until not WasOKread or (BytesRead = 0);
file is NOT read. from all i know, this function with these parameters ONLY checks if something is waiting in a pipe, so teoretically it is even more proper. but this is just what i found trying to make working the 'full' version above.
i hope you are smarter than me and will tell me whats wrong...!
//edit:
i noticed ~98% on two (all i tried, big~22MB and small~0,5MB one) FLAC files. when i tried same thing with lame and 52KB MP3 file, i got only 53KB output, instead of 517KB. i also noticed, that small changes in code, like now
while WaitForSingleObject(PI.hProcess, 20)=WAIT_OBJECT_0 do Application.ProcessMessages;
becomes->
while WaitForSingleObject(PI.hProcess, 10)=WAIT_OBJECT_0 do Application.ProcessMessages;
i got less output, so if before i got from 437KB FLAC file always 448KB output, now i have 432KB. but no matter how much time i give, its never more than that previous 448KB.
hmm...am i dumb or something ?
//edit2:
one more thing : PeekNamedPipe does not return any error.