I have a small program which takes input and does some preprocessing on it before continuing its business. (The classic case of "make us a program; oh, we don't know the exact data format yet.") For this step being more scriptable, I've decided to use AWK for the preprocessing step (because the data will be tabular in nature).
Enough of the background. What this boils down to, is that I'm trying to invoke GAWK with CreateProcess.
- I'm fairly certain my GAWK expects UTF-8 (that is, no Windows "Unicode"/UTF-16, and I'm fairly certain I'm feeding it that).
- It's GnuWin32-AWK, so it should know Window Styles' paths.
- I'm also fairly certain that the anonymous pipes that are used for the communication are set correctly; I made a test.bat script that worked correctly. Also, I can read AWK error messages from the input pipe (child output) if there's a type in the command line.
- The only output I've managed to get was 0A 0D, that is, \n\r.
- If the command line is OK,
WaitForSingleObject()
returns error 259 indicating deadlock.
EDIT 1: This is a simplified version of the function. I'm keeping the main loop for now. It does two turns in the loop in both cases.
wstring path = L"C:\\MODBUSSINATOR\\awk\\awk.exe";
wstring args = L"C:\\MODBUSSINATOR\\awk\\awk.exe --file=.\\awk\\recipe.awk";
string source = "PERSE PERSE PERSE";
wchar_t argbuff[args.size()+1]; // MSDN says the arg-buff may be modified by client
wcscpy(argbuff,args.c_str()); // so copy args into array
istringstream inpus(source);
ostringstream resus;
DWORD written=0,retcode=0,read=0,readable=0,inpusize=0,resusize=0;
char inpu_buff[1024],resu[1024],*inpu;
SECURITY_ATTRIBUTES cia = {sizeof(SECURITY_ATTRIBUTES),nullptr,true}, coa = cia;
HANDLE oh,ih,cih,coh;
if(!CreatePipe(&cih,&oh,&cia,BUFF_SZ)) //BUFF_SZ = 1024
abort();
if(!CreatePipe(&ih,&coh,&cia,BUFF_SZ))
abort();
PROCESS_INFORMATION procinfo;
STARTUPINFO startinfo;
memset(&procinfo,0,sizeof(PROCESS_INFORMATION)); // The documentation says to do this.
memset(&startinfo,0,sizeof(STARTUPINFO)); // Though I set all the fields exclusively.
startinfo = {
sizeof(STARTUPINFO),nullptr,nullptr,nullptr,0,0,0,0,0,0,0,
STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,SW_HIDE,0,nullptr,
cih,coh,coh
};
if(!CreateProcess(
path.c_str(),argbuff,nullptr,nullptr,true, 0 // true, handles are inherited
,nullptr,nullptr,&startinfo,&procinfo
))
abort();
while(true){
if(inpusize <= 0){
inpu = inpu_buff;
inpus.get(inpu,1024);
inpusize = inpus.gcount();
if(inpusize){
if(WriteFile(oh,inpu,inpusize,&written,nullptr)){
inpusize -= written;
inpu += written;
}else
abort();
}else
CloseHandle(oh);
}
if(!PeekNamedPipe(ih,nullptr,0,nullptr,&readable,nullptr))
abort();
if(readable > 0){
if(!ReadFile(ih,resu,readable,&read,nullptr))
abort();
resu[read] = '\0';
resus << resu;
}
if(!written && !read)
break;
read = 0,written = 0;
}
CloseHandle(coh);
CloseHandle(cih);
CloseHandle(ih);
if(WaitForSingleObject(procinfo.hProcess,conf.script_to) == WAIT_TIMEOUT)
abort();
cout << "AWK SAYS:" << endl <<
resus.str() << endl;
Before simplifications, I was reading the following error from the pipe:
"awk: fatal: can't open source file `.\aD'".
Now the test bat file will block at the WaitForSingleObject()
function until the timeout. But output comes through OK ("PERSE PERSE PERSE"), and AWK still outputs nothing. I'm investigating.
Please note that the hotspot around the code is probably around the CreateProcess()
function. The rest is your normal WinAPI verbosity.
My recipe.awk is just a pass-through at the moment:
BEGIN { print "paskaa" > "paska.txt" ; }
/.*/ { print $0; }
END { exit 999; }
Above, I use the generation of the paska.txt file to indicate that the script starts executing. It has never done so.
This was my equivalant.bat, which worked as expected:
@ECHO OFF
:LOOP
SET VAR=
SET /P VAR=
IF "%VAR%" NEQ "" (
ECHO %VAR%
GOTO :LOOP
)
Please bear with my incompetence. I would very much need your some fresh input.
EDIT 2: I managed to solve the problem, I was lacking these:
SetHandleInformation(ihh,HANDLE_FLAG_INHERIT,0)
SetHandleInformation(ohh,HANDLE_FLAG_INHERIT,0)
Meaning, don't let the child process inherit my-side communication handles. If it does, there would be two copies, so the pipe would never get closed.
It was very lame to have spent a workday and a half with this.
EDIT 3: And as the further help for the odd-googlers out there:
You want to close you instances of the child-side handles of the pipes once the child process has been created! If not, the pipe remains valid even if the Child closes its copy of the handle, thus your PeekNamedPipe and ReadFile will either return 0 readable or block indefinitely (depending on their arguments).