当前位置: 首页 > 面试题库 >

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

梁丘经艺
2023-03-14
问题内容

我试图弄清楚如何从用CreateProcessW创建的过程中读取标准输出/错误。我看了看文档,用谷歌搜索了这个列表,但是我还没有找到好的指针/样本:)

到目前为止,这是我想到的(在Windows上运行良好,这是我的Java代码中的相关代码段):

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功能创建的进程写入控制台,MSDN建议创建一个子进程并使用匿名管道来重定向该子进程的标准输入和输出句柄。

创建具有重定向输入和输出的子进程

由于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



 类似资料:
  • 问题内容: 我正在尝试获取Windows机器上所有当前正在运行的进程的列表。 我正在尝试通过JNA的winapi调用EnumProcesses-> OpenProcess-> GetModuleBaseNameW-> CloseHandle尝试OpenProcess调用时失败。GetLastError返回5(ERROR_ACCESS_DENIED)。 这是我的代码: 问题答案: 调用with 表示

  • 我想在Java使用JNA调用Linux mount命令,并从调用结果填充一个装入点列表,但无法理解接口方法的实际返回类型应该是什么。 如果我使用int,那么它将没有任何错误地打印-1。我认为这是某种错误的迹象。 } 我尝试使用基于以下文档的不同返回类型,但没有任何工作。 默认类型映射 我想我的问题是基于错误的签名 我的库有时会导致VM崩溃:仔细检查导致崩溃的方法的签名,以确保所有参数的大小和类型都

  • 问题内容: 我正在尝试运行可执行文件并使用;捕获其输出;但是,我似乎并没有获得全部输出。 手动打开时,它比原始exe文件少打印两行。 我尝试了一种具有相同结果的替代方法: 任何人都可以帮助我获取exe的完整数据吗? 正如塞巴斯蒂安的要求: 原始exe文件最后几行o / p: -Gdd:通用计数(1-1000) -Cdd:切割起始于(0-99)-Edd:切割终止于(1-100) 请在下面选择流文件编

  • 库加载良好,但似乎找不到静态变量。为什么没有加载静态变量?

  • 问题内容: 我遇到了一个问题。有时,当我的JUnit测试正在运行时,请命令webDriver.quit();。不会杀死chromedriver进程,因此下一个测试无法开始。在那种情况下,我想添加一些可以在Linux上手动终止进程的方法,但是我不知道如何获取chromedriver的PID,因此可以执行以下操作:Runtime.getRuntime()。exec(KILL + PID); 问题答案:

  • 问题内容: 我知道我可以像这样获得流程的使用子流程: 但是我只想知道要写入进程的pid,我该 怎么做? 问题答案: 只需写信给: