background:
I once answered this question
that was about flushing two input strings from a Java process to a batch script. Since I found a workaround solution I am still
very interested to solve the remaining mystery and find out why the obvious solution is not working.
problem description
See this very simple batch script:
@ECHO OFF
SET /P input1=1st Input:
SET /P input2=2nd Input:
ECHO 1st Input: %input1% and 2nd Input: %input2%
If you run this batch script with Java using ProcessBuilder
and flush two input strings into it you will notice that only the
first input string will be consumed while the second will be ignored.
I found out that SET /P
command consumes input from pipes when
- CRLF token is found
- by timeout
- by full buffer(1024 Bytes)
My accepted workaround was based on the last two options by using a Thread.sleep(100)
statement between the inputs or using a 1024 Byte
Buffer for each input.
It always works for single input or in this case the first input because closing the stream has the effect
that the batch script reads one input and empty returns all following SET /P
statements.
the question
Why is the first option by using the CRLF token "input\r\n"
not working?
research
I already tried to workaround the String.getBytes()
method by creating a Byte Buffer myself using \x0d
and \x0a
as last
bytes for CRLF token but it has no effect.
And I tried all other OutputStream
wrappers like PrintWriter
to check if there is
a problem with the flush()
implementation without any success.
I created a C++ program that basically does the same as the java programm by using CreateProcess
and stangely it works like a charm.
testing code
Not working Java code:
ProcessBuilder builder = new ProcessBuilder("test.bat");
Process process = builder.start();
OutputStream out = process.getOutputStream();
out.write("foo\r\n".getBytes());
out.flush();
out.write("bar\r\n".getBytes());
out.flush();
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null)
System.out.println(line);
in.close();
Full working C++ code:
DWORD dwWritten;
char cmdline[] = "test.bat";
CHAR Input1[] = "foo\r\n";
CHAR Input2[] = "bar\r\n";
HANDLE hStdInRd = NULL;
HANDLE hStdInWr = NULL;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Create Pipe
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&hStdInRd, &hStdInWr, &saAttr, 0);
SetHandleInformation(hStdInWr, HANDLE_FLAG_INHERIT, 0);
// Create Process
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = hStdInRd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
// Write to Pipe
WriteFile(hStdInWr, Input1, (DWORD)strlen(Input1), &dwWritten, NULL);
FlushFileBuffers(hStdInWr);
WriteFile(hStdInWr, Input2, (DWORD)strlen(Input2), &dwWritten, NULL);
FlushFileBuffers(hStdInWr);
CloseHandle(hStdInWr);
the question again
The problem does not make any sense to me and is bugging me a lot. Why does sending the CRLF token from Java does not have any
effect on batch file inputs while it does when sending from C++ program?