使用jna和CreateProcessW时如何获取进程输出

Posted

技术标签:

【中文标题】使用jna和CreateProcessW时如何获取进程输出【英文标题】:how to get the process output when using jna and CreateProcessW 【发布时间】:2012-01-18 00:13:46 【问题描述】:

我试图弄清楚如何从 我用 CreateProcessW 创建的流程。我看了看文档, 谷歌搜索并搜索了这个列表,但我没有找到好的指针/样本 然而:)

这是我到目前为止的想法(它在 Windows 上运行良好,这是我的 java 代码中的相关 sn-p):

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

那么...我怎样才能从该过程中获取输出?有人已经这样做并愿意分享样本吗?

提前感谢大家的帮助。

【问题讨论】:

我不认为CreateProcess 会返回一个进程信息对象,因为规范说它返回一个 BOOL 值。您应该在调用此函数后立即调查其参数中引用的ProcessInformation 结构的状态。在规范中,它说lpProcessInformation [out]a pointer to a PROCESS_INFORMATION structure that receives identification information about the new process。或者,您可以通过示例使用 Kernel32 的 GetProcessInformation 函数。 非常感谢您的回复!是的,我收集了方法本身不会给出输出。 StartupInfo 包含(除其他外): public HANDLE hStdInput;公共句柄 hStdOutput;公共句柄 hStdError;但是,我不确定如何将 HANDLE 映射到可以在 java 端使用的东西。我希望这将是我可以在 java 端阅读的一些流。此外,我并不严格知道如何正确分配这些字段...我将深入研究您对 GetProcessInformation 的建议。如果您有任何其他指示,请回复,因为我在该地区很绿。再次感谢! 抱歉,我们似乎无法通过ProcessInformationstructure 或GetProcessInformation 函数实现任何目标(获取输出)。请参阅我在下面给出的解决方案 【参考方案1】:

要为使用CreateProcess 函数创建的进程写入控制台,MSDN 建议创建一个子进程并使用匿名管道来重定向子进程的标准输入和输出句柄。

Creating a Child Process with Redirected Input and Output

由于 JNA 3.3.0 平台还没有包含我们需要的所有 Kernel32 功能,我们需要提供所需的 JNA 接口如下:(注意 JNA 4.0 为您提供了Kernel32)

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();

然后,主要部分:

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. 


    


最初的 MSDN 程序只要求一个参数。但是,修改后的 Runtest java 程序需要两个参数:(1)命令行; (2) 输入文件。

示例用法:

java -jar RunTest.jar "C:\\Program Files\\Java\\jre6\\bin\\java.exe -version" "C:\\Documents and Settings\\Administrator\\Desktop\\test.txt"

程序的示例输出:

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

如果你想看一个精致的版本...WindowsXPProcess.java

【讨论】:

嘿。我非常感谢彻底的回答!这是我的原始问题供参考:***.com/questions/9012639/… 我认为你不需要实现Kernel32。我刚刚下载了最新版本的 JNA (4.0),它已经有一个名为 Kernel32.CreateProcess 的方法。见twall.github.io/jna/3.3.0/javadoc/com/sun/jna/platform/win32/… WindowsXPProcess.java 的链接失效了。

以上是关于使用jna和CreateProcessW时如何获取进程输出的主要内容,如果未能解决你的问题,请参考以下文章

使用jna在java中获取鼠标类型

使用 JNA 在 Clojure 中按值获取和传递结构

JNA 访问 NTFS USN (win32)。如何从内存对象中获取数据?

使用 JNA 将本机 C 函数映射到 Java 接口时的指针问题

为啥 CreateProcessW() 不执行提供的命令?

JNA ByteBuffer statvfs