问题
I'm trying to figure out how to read the standard out/err from the process I've created with CreateProcessW. I looked at the docs, googled and searched this list but I didn't find good pointers/samples yet :)
Here's what I came up with so far (it's working fine on windows, it's a relevant snippet from my java code):
Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
Kernel32.StartupInfo startupInfo = new Kernel32.StartupInfo();
Kernel32.ProcessInfo processInformation = new Kernel32.ProcessInfo();
if (!kernel32.CreateProcessW(null, new WString(command), null, null, false,
DETACHED_PROCESS, null, new WString(dir.getAbsolutePath()), startupInfo,
processInformation)) {
throw new IOException("Could not start process. Errno: " +
kernel32.GetLastError());
}
kernel32.CloseHandle(processInformation.hProcess);
kernel32.CloseHandle(processInformation.hThread);
So... how can I grab the output from that process? Anyone has done that already and care sharing a sample?
Thanks guys for any help in advance.
回答1:
To write to a console for a process created with CreateProcess
function, MSDN suggests to create a child process and use anonymous pipes to redirect child process's standard input and output handles.
Creating a Child Process with Redirected Input and Output
Since JNA 3.3.0 Platform hasn't include all the Kernel32 functions we need, we need to provide the required JNA interfaces as follows: (NOTE JNA 4.0 provides Kernel32 for you)
Kernel32.java:
import java.util.HashMap;
import java.util.Map;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;
import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES;
import com.sun.jna.platform.win32.WinBase.STARTUPINFO;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinBase.PROCESS_INFORMATION;
import com.sun.jna.platform.win32.WinNT.HANDLE;
public interface Kernel32 extends StdCallLibrary {
final static Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
}
};
public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32", Kernel32.class, WIN32API_OPTIONS);
/*
BOOL WINAPI CreateProcess(
__in_opt LPCTSTR lpApplicationName,
__inout_opt LPTSTR lpCommandLine,
__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in BOOL bInheritHandles,
__in DWORD dwCreationFlags,
__in_opt LPVOID lpEnvironment,
__in_opt LPCTSTR lpCurrentDirectory,
__in LPSTARTUPINFO lpStartupInfo,
__out LPPROCESS_INFORMATION lpProcessInformation
);
*/
public boolean CreateProcess(
String lpApplicationName,
String lpCommandLine,
SECURITY_ATTRIBUTES lpProcessAttributes,
SECURITY_ATTRIBUTES lpThreadAttributes,
boolean bInheritHandles,
DWORD dwCreationFlags,
Pointer lpEnvironment,
String lpCurrentDirectory,
STARTUPINFO lpStartupInfo,
PROCESS_INFORMATION lpProcessInformation
);
public HANDLE GetStdHandle(DWORD nStdHandle);
public int GetLastError();
}
Then, the main part:
RunTest.java:
import java.nio.ByteBuffer;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinBase.PROCESS_INFORMATION;
import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES;
import com.sun.jna.platform.win32.WinBase.STARTUPINFO;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.ptr.IntByReference;
public class RunTest {
static HANDLEByReference childStdInRead = new HANDLEByReference();
static HANDLEByReference childStdInWrite = new HANDLEByReference();
static HANDLEByReference childStdOutRead = new HANDLEByReference();
static HANDLEByReference childStdOutWrite = new HANDLEByReference();
static final int HANDLE_FLAG_INHERIT = 0x00000001;
static final int HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
static final int BUFSIZE = 4096;
static final int GENERIC_READ = 0x80000000;
static final int FILE_ATTRIBUTE_READONLY = 1;
private static final int OPEN_EXISTING = 3;
private static final DWORD STD_OUTPUT_HANDLE = new DWORD(-11);
private static final int STARTF_USESTDHANDLES = 0x00000100;
static HANDLE inputFile = null;
static void createChildProcess(String cmd){
String szCmdline = cmd;
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = new DWORD(processInformation.size());
startupInfo.hStdError = childStdOutWrite.getValue();
startupInfo.hStdOutput = childStdOutWrite.getValue();
startupInfo.hStdInput = childStdInRead.getValue();
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
if (!Kernel32.INSTANCE.CreateProcess(
null,
szCmdline,
null,
null,
true,
new DWORD(0x00000020),
null,
null,
startupInfo,
processInformation)){
System.err.println(Kernel32.INSTANCE.GetLastError());
}
else {
com.sun.jna.platform.win32.Kernel32.INSTANCE.WaitForSingleObject(processInformation.hProcess, 0xFFFFFFFF);
com.sun.jna.platform.win32.Kernel32.INSTANCE.CloseHandle(processInformation.hProcess);
com.sun.jna.platform.win32.Kernel32.INSTANCE.CloseHandle(processInformation.hThread);
}
}
static void WriteToPipe()
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
IntByReference dwRead = new IntByReference();
IntByReference dwWritten = new IntByReference();
ByteBuffer buf = ByteBuffer.allocateDirect(BUFSIZE);
Pointer data = Native.getDirectBufferPointer(buf);
boolean bSuccess = true;
for (;;)
{
bSuccess = com.sun.jna.platform.win32.Kernel32.INSTANCE.ReadFile(inputFile, buf, BUFSIZE, dwRead, null);
if ( ! bSuccess || dwRead.getValue() == 0 ) break;
bSuccess = com.sun.jna.platform.win32.Kernel32.INSTANCE.WriteFile(childStdInWrite.getValue(), data.getByteArray(0, BUFSIZE), dwRead.getValue(), dwWritten, null);
if ( ! bSuccess ) break;
}
// Close the pipe handle so the child process stops reading.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.CloseHandle(childStdInWrite.getValue())){
System.err.println(Kernel32.INSTANCE.GetLastError());
}
}
static void ReadFromPipe()
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
IntByReference dwRead = new IntByReference();
IntByReference dwWritten = new IntByReference();
ByteBuffer buf = ByteBuffer.allocateDirect(BUFSIZE);
Pointer data = Native.getDirectBufferPointer(buf);
boolean bSuccess = true;
HANDLE hParentStdOut = Kernel32.INSTANCE.GetStdHandle(STD_OUTPUT_HANDLE);
// Close the write end of the pipe before reading from the
// read end of the pipe, to control child process execution.
// The pipe is assumed to have enough buffer space to hold the
// data the child process has already written to it.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.CloseHandle(childStdOutWrite.getValue())){
System.err.println(Kernel32.INSTANCE.GetLastError());
}
for (;;)
{
bSuccess = com.sun.jna.platform.win32.Kernel32.INSTANCE.ReadFile( childStdOutRead.getValue(), buf, BUFSIZE, dwRead, null);
if( ! bSuccess || dwRead.getValue() == 0 ) break;
bSuccess = com.sun.jna.platform.win32.Kernel32.INSTANCE.WriteFile(hParentStdOut, data.getByteArray(0, BUFSIZE), dwRead.getValue(), dwWritten, null);
if (! bSuccess ) break;
}
}
/**
* {@link http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx}
*/
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Please specify a command.\n");
System.exit(1);
}
if (args.length < 2) {
System.err.println("Please specify an input file.\n");
System.exit(1);
}
SECURITY_ATTRIBUTES saAttr = new SECURITY_ATTRIBUTES();
saAttr.dwLength = new DWORD(saAttr.size());
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = null;
// Create a pipe for the child process's STDOUT.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.CreatePipe(childStdOutRead, childStdOutWrite, saAttr, 0)){
System.err.println(Kernel32.INSTANCE.GetLastError());
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.SetHandleInformation(childStdOutRead.getValue(), HANDLE_FLAG_INHERIT, 0)){
System.err.println(Kernel32.INSTANCE.GetLastError());;
}
// Create a pipe for the child process's STDIN.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.CreatePipe(childStdInRead, childStdInWrite, saAttr, 0)){
System.err.println(Kernel32.INSTANCE.GetLastError());
}
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!com.sun.jna.platform.win32.Kernel32.INSTANCE.SetHandleInformation(childStdInWrite.getValue(), HANDLE_FLAG_INHERIT, 0)){
System.err.println(Kernel32.INSTANCE.GetLastError());;
}
createChildProcess(args[0]);
inputFile = com.sun.jna.platform.win32.Kernel32.INSTANCE.CreateFile(
args[1],
GENERIC_READ,
0,
null,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
null);
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
WriteToPipe();
System.out.println( "\n->Contents of \""+args[1]+"\" written to child STDIN pipe.\n");
// Read from pipe that is the standard output for child process.
System.out.println( "\n->Contents of child process STDOUT:\n\n" + args[1]);
ReadFromPipe();
System.out.println("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
}
}
The original MSDN program only asks for one argument. But, the modified Runtest java program will need two arguments: (1) the command line; (2) the input file.
Example usage:
java -jar RunTest.jar "C:\\Program Files\\Java\\jre6\\bin\\java.exe -version" "C:\\Documents and Settings\\Administrator\\Desktop\\test.txt"
Example output of the program:
->Contents of "C:\\Documents and Settings\\Administrator\\Desktop\\test.txt" written to child STDIN pipe.
->Contents of child process STDOUT:
C:\\Documents and Settings\\Administrator\\Desktop\\test.txt
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing)
->End of parent execution.
If you want to see an elaborate version... WindowsXPProcess.java
来源:https://stackoverflow.com/questions/8903510/how-to-get-the-process-output-when-using-jna-and-createprocessw