0

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).

VITTUIX-MAN
  • 346
  • 2
  • 8
  • Does running `C:\MODBUSSINATOR\awk\awk.exe --file=.\awk\recipe.awk` in a regular command prompt work? I'm going to wager that awk requires forward slashes in paths – nemetroid Aug 11 '15 at 10:46
  • Yes it does, I need to add echo "some data" | C:\MODBUSSINATOR\awk\awk.exe --file=.\awk\recipe.awk though, because the script is expected to run as it was in a middle of pipeline. Before this I used ShellExecute -API to call awk, but managing encodings and temporary files was a nightmare. – VITTUIX-MAN Aug 11 '15 at 10:54
  • Instead of telling us what to ignore and what you're sure is not contributing to the problem, strip it all out of your code and show the smallest possible example that still demonstrates the issue. I say this so that you'll have the best chance of getting help --- people tend to run away screaming when they see such dense code in a question like this. – jas Aug 11 '15 at 10:56
  • Wall of code. Too much to dig into for me. Please make an MCVE. – David Heffernan Aug 11 '15 at 11:21
  • Tried to simplify it a little, still keeping the loop though because it's kind of needed when you do this child process business single-threadedly. – VITTUIX-MAN Aug 11 '15 at 11:42

0 Answers0