`

CreateProcess Usage

 
阅读更多
(
ORIGINALLY ADAPTED FORM:
http://blog.tianya.cn/blogger/post_read.asp?BlogID=1509063&PostID=17562664
)

管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。匿名管道(Anonymous Pipes)是在父进程和子进程间单向传输数据的一种未命名的管道,只能在本地计算机中使用,而不可用于网络间的通信。
  
    匿名管道实施细则
  
    匿名管道由CreatePipe()函数创建,该函数在创建匿名管道的同时返回两个句柄:管道读句柄和管道写句柄。CreatePipe()的函数原型为:
  
  BOOL CreatePipe(PHANDLE hReadPipe, // 指向读句柄的指针
   PHANDLE hWritePipe, // 指向写句柄的指针
   LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全属性的指针
   DWORD nSize // 管道大小
  );
  
    通过hReadPipe和hWritePipe所指向的句柄可分别以只读、只写的方式去访问管道。在使用匿名管道通信时,服务器进程必须将其中的一个句柄传送给客户机进程。句柄的传递多通过继承来完成,服务器进程也允许这些句柄为子进程所继承。除此之外,进程也可以通过诸如DDE或共享内存等形式的进程间通信将句柄发送给与其不相关联的进程。
  
    在调用CreatePipe()函数时,如果管道服务器将lpPipeAttributes 指向的SECURITY_ATTRIBUTES数据结构的数据成员bInheritHandle设置为TRUE,那么CreatePipe()创建的管道读、写句柄将会被继承。管道服务器可调用DuplicateHandle()函数改变管道句柄的继承。管道服务器可以为一个可继承的管道句柄创建一个不可继承的副本或是为一个不可继承的管道句柄创建一个可继承的副本。CreateProcess()函数还可以使管道服务器有能力决定子进程对其可继承句柄是全部继承还是不继承。
  
    在生成子进程之前,父进程首先调用Win32 API SetStdHandle()使子进程、父进程可共用标准输入、标准输出和标准错误句柄。当父进程向子进程发送数据时,用SetStdHandle()将管道的读句柄赋予标准输入句柄;在从子进程接收数据时,则用SetStdHandle()将管道的写句柄赋予标准输出(或标准错误)句柄。然后,父进程可以调用进程创建函数CreateProcess()生成子进程。如果父进程要发送数据到子进程,父进程可调用WriteFile()将数据写入到管道(传递管道写句柄给函数),子进程则调用GetStdHandle()取得管道的读句柄,将该句柄传入ReadFile()后从管道读取数据。
  
    如果是父进程从子进程读取数据,那么由子进程调用GetStdHandle()取得管道的写入句柄,并调用WriteFile()将数据写入到管道。然后,父进程调用ReadFile()从管道读取出数据(传递管道读句柄给函数)。
  
    在用WriteFile()函数向管道写入数据时,只有在向管道写完指定字节的数据后或是在有错误发生时函数才会返回。如管道缓冲已满而数据还没有写完,WriteFile()将要等到另一进程对管道中数据读取以释放出更多可用空间后才能够返回。管道服务器在调用CreatePipe()创建管道时以参数nSize对管道的缓冲大小作了设定。
  
    匿名管道并不支持异步读、写操作,这也就意味着不能在匿名管道中使用ReadFileEx()和WriteFileEx(),而且ReadFile()和WriteFile()中的lpOverLapped参数也将被忽略。匿名管道将在读、写句柄都被关闭后退出,也可以在进程中调用CloseHandle()函数来关闭此句柄
  
  
  
  /////////////////////////////////////////////////////////
  匿名管道程序示例
  
    总的来说,匿名管道程序是比较简单的。在下面将要给出的程序示例中,将由父进程(管道服务器)创建一个子进程(管道客户机),子进程回见个其全部的标准输出发送到匿名管道中,父进程再从管道读取数据,一直到子进程关闭管道的写句柄。其中,匿名管道服务器程序的实现清单如下:
  
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  char ReadBuf[100];
  DWORD ReadNum;
  HANDLE hRead; // 管道读句柄
  HANDLE hWrite; // 管道写句柄
  BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 创建匿名管道
  if (bRet == TRUE)
   printf("成功创建匿名管道!\n");
  else
   printf("创建匿名管道失败,错误代码:%d\n", GetLastError());
   // 得到本进程的当前标准输出
   HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE);
   // 设置标准输出到匿名管道
   SetStdHandle(STD_OUTPUT_HANDLE, hWrite);
   GetStartupInfo(&si); // 获取本进程的STARTUPINFO结构信息
   bRet = CreateProcess(NULL, "Client.exe", NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi); // 创建子进程
   SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢复本进程的标准输出
   if (bRet == TRUE) // 输入信息
    printf("成功创建子进程!\n");
   else
    printf("创建子进程失败,错误代码:%d\n", GetLastError());
    CloseHandle(hWrite); // 关闭写句柄
    // 读管道直至管道关闭
    while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))
    {
     ReadBuf[ReadNum] = '\0';
     printf("从管道[%s]读取%d字节数据\n", ReadBuf, ReadNum);
    }
    if (GetLastError() == ERROR_BROKEN_PIPE) // 输出信息
     printf("管道被子进程关闭\n");
    else
     printf("读数据错误,错误代码:%d\n", GetLastError());
  
    在本示例中,将当前进程的标准输出设置为使用匿名管道,再创建子进程,子进程将继承父进程的标准输出,然后再将父进程的标准输出恢复为其初始状态。于是父进程便可从管道读取数据,直到有错误发生或关闭管道写入端的所有句柄。创建的子进程只是向标准输出和标准错误发送一些文本信息,其中发送给标准输出的文本将重定向输出到管道,发送给标准错误的文本将不改变输出。下面给出子进程的实现代码:
  
  int main(int argc, char* argv[])
  {
   for (int i = 0; i < 100; i++) // 发送一些数据到标准输出和标准错误
   {
    printf("i = %d\n", i); // 打印提示
    cout << "标准输出:" << i << endl; // 打印到标准输出
    cerr << "标准错误:" << i << endl; // 打印到标准错误
   }
   return 0;
  }

-----------------------------------------------------------------

NOW,LET'S GO TO SEE TIPS FROM MSDN ABOUT :
(ORIGINALLY ADAPTED FORM:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365152%28v=vs.85%29.aspx)
CREATEPIPE

Creates an anonymous pipe, and returns handles to the read and write ends of the pipe.
Syntax

BOOL WINAPI CreatePipe(
  __out     PHANDLE hReadPipe,
  __out     PHANDLE hWritePipe,
  __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
  __in      DWORD nSize
);

Parameters

hReadPipe [out]

    A pointer to a variable that receives the read handle for the pipe.
hWritePipe [out]

    A pointer to a variable that receives the write handle for the pipe.
lpPipeAttributes [in, optional]

    A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpPipeAttributes is NULL, the handle cannot be inherited.

    The lpSecurityDescriptor member of the structure specifies a security descriptor for the new pipe. If lpPipeAttributes is NULL, the pipe gets a default security descriptor. The ACLs in the default security descriptor for a pipe come from the primary or impersonation token of the creator.
nSize [in]

    The size of the buffer for the pipe, in bytes. The size is only a suggestion; the system uses the value to calculate an appropriate buffering mechanism. If this parameter is zero, the system uses the default buffer size.

Return value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.
Remarks

CreatePipe creates the pipe, assigning the specified pipe size to the storage buffer. CreatePipe also creates handles that the process uses to read from and write to the buffer in subsequent calls to the ReadFile and WriteFile functions.

To read from the pipe, a process uses the read handle in a call to the ReadFile function. ReadFile returns when one of the following is true: a write operation completes on the write end of the pipe, the number of bytes requested has been read, or an error occurs.

When a process uses WriteFile to write to an anonymous pipe, the write operation is not completed until all bytes are written. If the pipe buffer is full before all bytes are written, WriteFile does not return until another process or thread uses ReadFile to make more buffer space available.

Anonymous pipes are implemented using a named pipe with a unique name. Therefore, you can often pass a handle to an anonymous pipe to a function that requires a handle to a named pipe.

If CreatePipe fails, the contents of the output parameters are indeterminate. No assumptions should be made about their contents in this event.

To free resources used by a pipe, the application should always close handles when they are no longer needed, which is accomplished either by calling the CloseHandle function or when the process associated with the instance handles ends. Note that an instance of a pipe may have more than one handle associated with it. An instance of a pipe is always deleted when the last handle to the instance of the named pipe is closed.

-----------------------------------------------------------------
FINALLY,LET'S GO TO SEE ONE EXAMPLE FROM MSDN...
(ORIGINALLY ADAPTED FROM:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx)
The example in this topic demonstrates how to create a child process using the CreateProcess function from a console process. It also demonstrates a technique for using anonymous pipes to redirect the child process's standard input and output handles. Note that named pipes can also be used to redirect process I/O.

The CreatePipe function uses the SECURITY_ATTRIBUTES structure to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the standard output for the child process. These pipe handles are specified in the STARTUPINFO structure, which makes them the standard handles inherited by the child process.

The parent process uses the opposite ends of these two pipes to write to the child process's input and read from the child process's output. As specified in the STARTUPINFO structure, these handles are also inheritable. However, these handles must not be inherited. Therefore, before creating the child process, the parent process uses the SetHandleInformation function to ensure that the write handle for the child process's standard input and the read handle for the child process's standard input cannot be inherited. For more information, see Pipes.

The following is the code for the parent process. It takes a single command-line argument: the name of a text file.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

#define BUFSIZE 4096

HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

HANDLE g_hInputFile = NULL;

void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);

int _tmain(int argc, TCHAR *argv[])
{
   SECURITY_ATTRIBUTES saAttr;

   printf("\n->Start of parent execution.\n");

// Set the bInheritHandle flag so pipe handles are inherited.

   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = NULL;

// Create a pipe for the child process's STDOUT.

   if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
      ErrorExit(TEXT("StdoutRd CreatePipe"));

// Ensure the read handle to the pipe for STDOUT is not inherited.

   if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdout SetHandleInformation"));

// Create a pipe for the child process's STDIN.

   if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
      ErrorExit(TEXT("Stdin CreatePipe"));

// Ensure the write handle to the pipe for STDIN is not inherited.

   if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdin SetHandleInformation"));

// Create the child process.
  
   CreateChildProcess();

// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.

   if (argc == 1)
      ErrorExit(TEXT("Please specify an input file.\n"));

   g_hInputFile = CreateFile(
       argv[1],
       GENERIC_READ,
       0,
       NULL,
       OPEN_EXISTING,
       FILE_ATTRIBUTE_READONLY,
       NULL);

   if ( g_hInputFile == INVALID_HANDLE_VALUE )
      ErrorExit(TEXT("CreateFile"));

// 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();
   printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);

// Read from pipe that is the standard output for child process.

   printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
   ReadFromPipe();

   printf("\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.

   return 0;
}

void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
   TCHAR szCmdline[]=TEXT("child");
   PROCESS_INFORMATION piProcInfo;
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE;

// Set up members of the PROCESS_INFORMATION structure.

   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.

   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO);
   siStartInfo.hStdError = g_hChildStd_OUT_Wr;
   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
   siStartInfo.hStdInput = g_hChildStd_IN_Rd;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.
   
   bSuccess = CreateProcess(NULL,
      szCmdline,     // command line
      NULL,          // process security attributes
      NULL,          // primary thread security attributes
      TRUE,          // handles are inherited
      0,             // creation flags
      NULL,          // use parent's environment
      NULL,          // use parent's current directory
      &siStartInfo,  // STARTUPINFO pointer
      &piProcInfo);  // receives PROCESS_INFORMATION
  
   // If an error occurs, exit the application.
   if ( ! bSuccess )
      ErrorExit(TEXT("CreateProcess"));
   else
   {
      // Close handles to the child process and its primary thread.
      // Some applications might keep these handles to monitor the status
      // of the child process, for example.

      CloseHandle(piProcInfo.hProcess);
      CloseHandle(piProcInfo.hThread);
   }
}

void WriteToPipe(void)

// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
   DWORD dwRead, dwWritten;
   CHAR chBuf[BUFSIZE];
   BOOL bSuccess = FALSE;

   for (;;)
   {
      bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
      if ( ! bSuccess || dwRead == 0 ) break;
     
      bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
      if ( ! bSuccess ) break;
   }

// Close the pipe handle so the child process stops reading.

   if ( ! CloseHandle(g_hChildStd_IN_Wr) )
      ErrorExit(TEXT("StdInWr CloseHandle"));
}

void ReadFromPipe(void)

// 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.
{
   DWORD dwRead, dwWritten;
   CHAR chBuf[BUFSIZE];
   BOOL bSuccess = FALSE;
   HANDLE hParentStdOut = 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 (!CloseHandle(g_hChildStd_OUT_Wr))
      ErrorExit(TEXT("StdOutWr CloseHandle"));

   for (;;)
   {
      bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
      if( ! bSuccess || dwRead == 0 ) break;

      bSuccess = WriteFile(hParentStdOut, chBuf,
                           dwRead, &dwWritten, NULL);
      if (! bSuccess ) break;
   }
}

void ErrorExit(PTSTR lpszFunction)

// Format a readable error message, display a message box,
// and exit from the application.
{
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}



The following is the code for the child process. It uses the inherited handles for STDIN and STDOUT to access the pipe created by the parent. The parent process reads from its input file and writes the information to a pipe. The child receives text through the pipe using STDIN and writes to the pipe using STDOUT. The parent reads from the read end of the pipe and displays the information to its STDOUT.

#include <windows.h>
#include <stdio.h>

#define BUFSIZE 4096

int main(void)
{
   CHAR chBuf[BUFSIZE];
   DWORD dwRead, dwWritten;
   HANDLE hStdin, hStdout;
   BOOL bSuccess;

   hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
   hStdin = GetStdHandle(STD_INPUT_HANDLE);
   if (
       (hStdout == INVALID_HANDLE_VALUE) ||
       (hStdin == INVALID_HANDLE_VALUE)
      )
      ExitProcess(1);

   // Send something to this process's stdout using printf.
   printf("\n ** This is a message from the child process. ** \n");

   // This simple algorithm uses the existence of the pipes to control execution.
   // It relies on the pipe buffers to ensure that no data is lost.
   // Larger applications would use more advanced process control.

   for (;;)
   {
   // Read from standard input and stop on error or no data.
      bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
     
      if (! bSuccess || dwRead == 0)
         break;

   // Write to standard output and stop on error.
      bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);
     
      if (! bSuccess)
         break;
   }
   return 0;
}




分享到:
评论

相关推荐

    CreateProcess

    通过进程打开另一个进程的三种方法:CreateProcess,WinExec,ShellExecute ,给出了调用的实现代码,源码中有CreateProcess,ShellExecute的函数说明,包含一个可执行的演示程序和源码 用VC2008编写的

    一个CREATEPROCESS()的实例

    创建进程实例,使用CreateProcess()函数打开一个程序的实例 CreateProcess()函数是Windows操作系统中用于创建新进程的API函数。该函数可以用来创建一个新的进程实例,并指定其执行的命令行参数、安全性设置、...

    以CreateProcess为例运行系统程序并输出结果

    在Windows操作系统中,创建一个新的进程并执行一个系统程序通常是通过API函数`CreateProcess`来实现的。`CreateProcess`是Windows API提供的一种低级进程创建方法,它允许开发者具有较高的控制权,例如设置进程和...

    CreateProcess调用后,获得主窗口句柄

    用CreateProcess创建进程后,得到主窗口句柄。

    新建程序进程CreateProcess

    标题中的“新建程序进程CreateProcess”指的是在编程中创建新的操作系统进程的过程,这通常涉及到操作系统级别的操作,如在Windows系统中使用API函数`CreateProcess`。`CreateProcess`是Windows API提供的一种机制,...

    解决createprocess error code 740

    标题 "解决createprocess error code 740" 指的是在尝试运行一个应用程序时遇到的一个特定错误,其中 `CreateProcess` 是 Windows API 中用于创建新进程的函数。错误代码 740 表示 "请求的操作需要提升的权限",意味...

    用CreateProcess创建进程

    在Windows操作系统中,`CreateProcess`函数是用于创建新进程和其主线程的关键API。它提供了灵活的方式来启动和控制一个程序,包括指定命令行参数、环境变量、工作目录以及进程和线程的安全属性。本篇文章将深入探讨`...

    CreateProcess函数 详细使用说明

    **CreateProcess函数详解** 在Windows操作系统中,`CreateProcess`函数是用于创建新进程和其初始主线程的关键API。这个函数允许程序员启动新的应用程序,并控制它们的执行环境。`CreateProcess`不仅创建新进程,还...

    createprocess创建进程并获取进程窗口HWND

    createprocess创建进程并获取进程窗口HWND,不管窗口是否在显示状态都可以获取。

    CreateProcess拦截exe程序

    在这个场景中,"CreateProcess拦截exe程序"指的是通过API Hook来拦截`CreateProcess`函数,这是一种Windows API,用于创建新的进程和其初始线程。下面我们将深入探讨`CreateProcess`、API Hook的基本概念以及如何...

    一个简单的可以Debug用CreateProcess创建的进程例子

    本篇文章将探讨如何使用`CreateProcess`函数在Windows环境中创建进程,并通过Visual Studio(VS)的调试器对其进行附加调试。`CreateProcess`是Windows API提供的一种功能,用于启动新的进程或线程。这个例子特别...

    CreateProcess创建暂停进程进行DLL注入-[VC.Dll+VB.Code]

    本教程将深入讲解如何使用`CreateProcess`函数在VC++中创建一个新的进程,并在VB环境下编写代码进行DLL注入。 `CreateProcess`是Windows API中的一个关键函数,它负责启动新的进程并可选地创建新的线程。这个函数...

    父子进程,createprocess

    在IT领域,特别是操作系统设计和进程管理中,"父子进程,createprocess" 是一个关键的概念。这个主题涉及到操作系统如何创建新的进程以及它们如何并发执行。本文将深入探讨这个主题,结合给定的描述,我们将专注于...

    vs2010下hook_createprocess

    "hook_createprocess"这个主题涉及的是在Visual Studio 2010环境下,通过编写DLL动态链接库来实现对`CreateProcess`函数的钩子,以便在创建新进程时进行干预。`CreateProcess`是Windows API中用于启动新进程的关键...

    API_HOOK_CreateProcess进程监视:知道你运行了哪些程序.zip

    在这个特定的案例中,"API_HOOK_CreateProcess进程监视"指的是利用API钩子来监控系统的CreateProcess函数。CreateProcess是Windows操作系统中的一个关键API,它负责创建新的进程和执行新的可执行文件。 当我们谈论...

    ShellExecute和CreateProcess打开指定程序

    本文将深入探讨两种常见的方法:ShellExecute和CreateProcess。这两种方法都是Windows API提供的功能,用于执行应用程序,但它们在使用场景和特性上有所不同。 **ShellExecute** ShellExecute是Windows Shell服务...

    pipe-CMD.rar_CMD回显_CreateProcess_c++ cmd pipe_windows管道cmd_管道 c

    利用管道执行cmd,显示回显内容,管道加createprocess实现该功能

    windows下eclipse跑junit报错:CreateProcess error=206

    windows下eclipse跑junit报错:CreateProcess error=206后面跟着乱码。 把这个jar替换掉就好了,放在:eclipse/plugins/下面。 参考:https://bugs.eclipse.org/bugs/show_bug.cgi?id=327193

Global site tag (gtag.js) - Google Analytics