diff --git a/core/org.eclipse.cdt.core.win32/ChangeLog b/core/org.eclipse.cdt.core.win32/ChangeLog index fffce6ca5d4..8ab4304bdc3 100644 --- a/core/org.eclipse.cdt.core.win32/ChangeLog +++ b/core/org.eclipse.cdt.core.win32/ChangeLog @@ -1,3 +1,8 @@ +2004-02-12 Alex Chapiro + + Update starter and spawner to use named pipes. + Update the binaries. + 2003-08-29 Alex Chapiro This patch just increase command line buffer up to OS limit (2K). It also diff --git a/core/org.eclipse.cdt.core.win32/library/Spawner.h b/core/org.eclipse.cdt.core.win32/library/Spawner.h index 3641bd88ca6..400452b4d5b 100644 --- a/core/org.eclipse.cdt.core.win32/library/Spawner.h +++ b/core/org.eclipse.cdt.core.win32/library/Spawner.h @@ -1,3 +1,17 @@ +/********************************************************************** + * Copyright (c) 2002-2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * + * Spawner.h + * + * This is a part of JNI implementation of spawner +***********************************************************************/ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_eclipse_cdt_utils_spawner_Spawner */ @@ -40,9 +54,15 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor (JNIEnv *, jobject, jint); +// #define DEBUG_MONITOR + int interruptProcess(int pid); + #ifdef __cplusplus } #endif + +// #define DEBUG_MONITOR + #endif diff --git a/core/org.eclipse.cdt.core.win32/library/SpawnerInputStream.h b/core/org.eclipse.cdt.core.win32/library/SpawnerInputStream.h index 7ab967353eb..f0c484a8427 100644 --- a/core/org.eclipse.cdt.core.win32/library/SpawnerInputStream.h +++ b/core/org.eclipse.cdt.core.win32/library/SpawnerInputStream.h @@ -1,3 +1,18 @@ +/********************************************************************** + * Copyright (c) 2002-2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * + * SpawnerInputStream.h + * + * This is a part of JNI implementation of spawner +***********************************************************************/ + /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */ diff --git a/core/org.eclipse.cdt.core.win32/library/SpawnerOutputStream.h b/core/org.eclipse.cdt.core.win32/library/SpawnerOutputStream.h index 7470e533c9a..161e1b81236 100644 --- a/core/org.eclipse.cdt.core.win32/library/SpawnerOutputStream.h +++ b/core/org.eclipse.cdt.core.win32/library/SpawnerOutputStream.h @@ -1,3 +1,17 @@ +/********************************************************************** + * Copyright (c) 2002-2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * + * SpawnerOutputStream.h + * + * This is a part of JNI implementation of spawner +***********************************************************************/ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */ diff --git a/core/org.eclipse.cdt.core.win32/library/StdAfx.c b/core/org.eclipse.cdt.core.win32/library/StdAfx.c index dc7550edf6d..318bc9214a6 100644 --- a/core/org.eclipse.cdt.core.win32/library/StdAfx.c +++ b/core/org.eclipse.cdt.core.win32/library/StdAfx.c @@ -1,12 +1,16 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation + * + * StdAfx.c + * + * This is a part of JNI implementation of spawner ***********************************************************************/ // stdafx.cpp : source file that includes just the standard includes // spawner.pch will be the pre-compiled header diff --git a/core/org.eclipse.cdt.core.win32/library/StdAfx.h b/core/org.eclipse.cdt.core.win32/library/StdAfx.h index 3ec5e93c719..4b427db00f4 100644 --- a/core/org.eclipse.cdt.core.win32/library/StdAfx.h +++ b/core/org.eclipse.cdt.core.win32/library/StdAfx.h @@ -1,13 +1,18 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation + * + * StdAfx.h + * + * This is a part of JNI implementation of spawner ***********************************************************************/ + // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently diff --git a/core/org.eclipse.cdt.core.win32/library/Win32ProcessEx.c b/core/org.eclipse.cdt.core.win32/library/Win32ProcessEx.c index a78d83b2766..4379580730b 100644 --- a/core/org.eclipse.cdt.core.win32/library/Win32ProcessEx.c +++ b/core/org.eclipse.cdt.core.win32/library/Win32ProcessEx.c @@ -1,59 +1,76 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation -***********************************************************************/ -/* + * * Win32ProcessEx.c * * This is a JNI implementation of spawner - */ +***********************************************************************/ + #include "stdafx.h" #include #include #include #include "Spawner.h" + + +#include "jni.h" +#include "io.h" + +#define PIPE_SIZE 512 // Size of pipe buffer +#define MAX_CMD_SIZE 2049 // Maximum size of command line +#define MAX_ENV_SIZE 4096 // Initial size of environment block +#define PIPE_NAME_LENGTH 100 // Size of pipe name buffer +#define PIPE_TIMEOUT 10000 // Default time-out value, in milliseconds. -#include "jni.h" -#include "io.h" - -// #define DEBUG_MONITOR - -#define PIPE_SIZE 512 -#define MAX_CMD_SIZE 2049 -#define MAX_ENV_SIZE 4096 - -#define MAX_PROCS (100) +#define MAX_PROCS (100) // Maximum number of simultaneiously runnig processes +// Theses are VM helpers typedef JNIEXPORT void * (JNICALL * JVM_GetThreadInterruptEvent)(); typedef JNIEXPORT char * (JNICALL * JVM_NativePath)(const char *); +// Process description block. Should be created for each launched process typedef struct _procInfo { int pid; // Process ID - int uid; // quasi-unique process ID + int uid; // quasi-unique process ID; we have to create it to avoid duplicated pid + // (actually this impossible from OS point of view but it is still possible + // a clash of new created and already finished process with one and the same PID. + // 3 events connected to this process (see starter) HANDLE eventBreak; HANDLE eventWait; HANDLE eventTerminate; } procInfo_t, * pProcInfo_t; -static int procCounter = 0; +static int procCounter = 0; // Number of running processes +// This is a VM helper JNIEXPORT void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg); -pProcInfo_t createProcInfo(); -pProcInfo_t findProcInfo(int pid); + +// Creates _procInfo block for every launched procss +pProcInfo_t createProcInfo(); + +// Find process description for this pid +pProcInfo_t findProcInfo(int pid); + +// We launch separate thread for each project to trap it termination unsigned int _stdcall waitProcTermination(void* pv) ; + +// This is a helper function to prevent losing of quotatin marks static int copyTo(char * target, const char * source, int cpyLenght, int availSpace); + +// Use this function to clean project descriptor and return it to the pool of available blocks. static void cleanUpProcBlock(pProcInfo_t pCurProcInfo); - +// Signal codes typedef enum { SIG_NOOP, SIG_HUP, @@ -65,20 +82,27 @@ typedef enum { extern CRITICAL_SECTION cs; -extern TCHAR path[MAX_PATH]; +extern TCHAR path[MAX_PATH]; // Directory where spawner.dll is located -static HMODULE hVM = NULL; +static HMODULE hVM = NULL; // VM handler static pProcInfo_t pInfo = NULL; +static int nCounter = 0; // We use it to build unique synchronisation object names +///////////////////////////////////////////////////////////////////////////////////// +// Launcher; launchess process and traps its termination +// Arguments: (see Spawner.java) +// [in] cmdarray - array of command line elements +// [in] envp - array of environment variables +// [in] dir - working directory +// [out] channels - streams handlers +///////////////////////////////////////////////////////////////////////////////////// JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 (JNIEnv * env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir, jintArray channels) { - - HANDLE hread[3], hwrite[3]; - SECURITY_ATTRIBUTES sa; + HANDLE stdHandles[3]; PROCESS_INFORMATION pi = {0}; STARTUPINFO si; DWORD flags = 0; @@ -91,6 +115,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 jsize nCmdTokens = 0; jsize nEnvVars = 0; int i; + DWORD pid = GetCurrentProcessId(); int nPos; pProcInfo_t pCurProcInfo; DWORD dwThreadId; @@ -100,6 +125,10 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 #ifdef DEBUG_MONITOR char buffer[1000]; #endif + int nLocalCounter; + char inPipeName[PIPE_NAME_LENGTH]; + char outPipeName[PIPE_NAME_LENGTH]; + char errPipeName[PIPE_NAME_LENGTH]; if((HIBYTE(LOWORD(GetVersion()))) & 0x80) { @@ -113,25 +142,39 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 return 0; } - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = 0; - sa.bInheritHandle = TRUE; + ZeroMemory(stdHandles, sizeof(stdHandles)); - memset(hread, 0, sizeof(hread)); - memset(hwrite, 0, sizeof(hwrite)); - if (!(CreatePipe(&hread[0], &hwrite[0], &sa, PIPE_SIZE) && - CreatePipe(&hread[1], &hwrite[1], &sa, PIPE_SIZE) && - CreatePipe(&hread[2], &hwrite[2], &sa, PIPE_SIZE))) - { - CloseHandle(hread[0]); - CloseHandle(hread[1]); - CloseHandle(hread[2]); - CloseHandle(hwrite[0]); - CloseHandle(hwrite[1]); - CloseHandle(hwrite[2]); + // Create pipe names + EnterCriticalSection(&cs); + sprintf(inPipeName, "\\\\.\\pipe\\stdin%08i%010i", pid, nCounter); + sprintf(outPipeName, "\\\\.\\pipe\\stdout%08i%010i", pid, nCounter); + sprintf(errPipeName, "\\\\.\\pipe\\stderr%08i%010i", pid, nCounter); + nLocalCounter = nCounter; + ++nCounter; + LeaveCriticalSection(&cs); + + + if ((INVALID_HANDLE_VALUE == (stdHandles[0] = CreateNamedPipe(inPipeName, PIPE_ACCESS_OUTBOUND, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || + (INVALID_HANDLE_VALUE == (stdHandles[1] = CreateNamedPipe(outPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || + (INVALID_HANDLE_VALUE == (stdHandles[2] = CreateNamedPipe(errPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL)))) { + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); ThrowByName(env, "java/io/IOException", "CreatePipe"); return 0; - } + } + +#ifdef DEBUG_MONITOR + sprintf(buffer, "Opened pipes: %s, %s, %s\n", inPipeName, outPipeName, errPipeName); + OutputDebugString(buffer); +#endif + nCmdTokens = (*env) -> GetArrayLength(env, cmdarray); nEnvVars = (*env) -> GetArrayLength(env, envp); @@ -144,17 +187,18 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 return 0; } - + // Construct starter's command line sprintf(eventBreakName, "SABreak%p", pCurProcInfo); sprintf(eventWaitName, "SAWait%p", pCurProcInfo); sprintf(eventTerminateName, "SATerm%p", pCurProcInfo); pCurProcInfo -> eventBreak = CreateEvent(NULL, TRUE, FALSE, eventBreakName); ResetEvent(pCurProcInfo -> eventBreak); pCurProcInfo -> eventWait = CreateEvent(NULL, TRUE, FALSE, eventWaitName); + ResetEvent(pCurProcInfo -> eventWait); pCurProcInfo -> eventTerminate = CreateEvent(NULL, TRUE, FALSE, eventTerminateName); ResetEvent(pCurProcInfo -> eventTerminate); - nPos = sprintf(szCmdLine, "%sstarter.exe %s %s %s ", path, eventBreakName, eventWaitName, eventTerminateName); + nPos = sprintf(szCmdLine, "%sstarter.exe %i %i %s %s %s ", path, pid, nLocalCounter, eventBreakName, eventWaitName, eventTerminateName); // Prepare command line for(i = 0; i < nCmdTokens; ++i) @@ -229,21 +273,16 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 } - memset(&si, 0, sizeof(si)); + ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); - si.dwFlags |= STARTF_USESTDHANDLES; si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // Processes in the Process Group are hidden - si.hStdInput = hread[0]; - si.hStdOutput = hwrite[1]; - si.hStdError = hwrite[2]; - - SetHandleInformation(hwrite[0], HANDLE_FLAG_INHERIT, FALSE); - SetHandleInformation(hread[1], HANDLE_FLAG_INHERIT, FALSE); - SetHandleInformation(hread[2], HANDLE_FLAG_INHERIT, FALSE); + SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, FALSE); + SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, FALSE); + SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, FALSE); flags = CREATE_NEW_CONSOLE; flags |= CREATE_NO_WINDOW; @@ -251,14 +290,15 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 #ifdef DEBUG_MONITOR OutputDebugString(szCmdLine); #endif - + // launches starter; we need it to create another console group to correctly process + // emulation of SYSint signal (Ctrl-C) ret = CreateProcess(0, /* executable name */ - szCmdLine, /* command line */ + szCmdLine, /* command line */ 0, /* process security attribute */ 0, /* thread security attribute */ - TRUE, /* inherits system handles */ + FALSE, /* inherits system handles */ flags, /* normal attached process */ - envBlk, /* environment block */ + envBlk, /* environment block */ cwd, /* change to the new current directory */ &si, /* (in) startup information */ &pi); /* (out) process information */ @@ -270,18 +310,13 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 free(szEnvBlock); - CloseHandle(hread[0]); - CloseHandle(hwrite[1]); - CloseHandle(hwrite[2]); - - - if (!ret) + if (!ret) // Launching error { LPTSTR lpMsgBuf; - CloseHandle(hwrite[0]); - CloseHandle(hread[1]); - CloseHandle(hread[2]); + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | @@ -327,18 +362,19 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 } else { -#ifdef DEBUG_MONITOR - sprintf(buffer, "Process %i created\n", pi.dwProcessId); - OutputDebugString(buffer); -#endif ret = (long)(pCurProcInfo -> uid); - file_handles[0] = (int)hwrite[0]; - file_handles[1] = (int)hread[1]; - file_handles[2] = (int)hread[2]; + + // Prepare stream handlers to return to java program + file_handles[0] = (int)stdHandles[0]; + file_handles[1] = (int)stdHandles[1]; + file_handles[2] = (int)stdHandles[2]; (*env) -> SetIntArrayRegion(env, channels, 0, 3, file_handles); } CloseHandle(h[1]); LeaveCriticalSection(&cs); +#ifdef DEBUG_MONITOR + OutputDebugString("Process started\n"); +#endif } @@ -348,6 +384,13 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0 } +///////////////////////////////////////////////////////////////////////////////////// +// Launcher; just launches process and don't care about it any more +// Arguments: (see Spawner.java) +// [in] cmdarray - array of command line elements +// [in] envp - array of environment variables +// [in] dir - working directory +///////////////////////////////////////////////////////////////////////////////////// JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 (JNIEnv * env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir) { @@ -389,7 +432,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 { if(0 > (nCpyLen = copyTo(szCmdLine + nPos, str, len, MAX_CMD_SIZE - nPos))) { - ThrowByName(env, "java/io/IOException", "Too long command line"); + ThrowByName(env, "java/io/Exception", "Too long command line"); return 0; } nPos += nCpyLen; @@ -418,7 +461,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 szEnvBlock = (char *)realloc(szEnvBlock, nBlkSize); if(NULL == szEnvBlock) { - ThrowByName(env, "java/io/IOException", "Not enough memory"); + ThrowByName(env, "java/io/Exception", "Not enough memory"); return 0; } } @@ -446,7 +489,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 } - memset(&si, 0, sizeof(si)); + ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); @@ -472,7 +515,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 free(cwd); free(szEnvBlock); - if (!ret) + if (!ret) // error { LPTSTR lpMsgBuf; @@ -494,6 +537,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 } else { + // Clean-up CloseHandle(pi.hThread); CloseHandle(pi.hProcess); ret = (long)pi.dwProcessId; //hProcess; @@ -505,6 +549,12 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1 } +///////////////////////////////////////////////////////////////////////////////////// +// Emulation of the signal raising +// Arguments: (see Spawner.java) +// [in] uid - unique process ID +// [in] signal - signal to raise +///////////////////////////////////////////////////////////////////////////////////// JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise (JNIEnv * env, jobject process, jint uid, jint signal) { @@ -570,13 +620,18 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise } - - + + +///////////////////////////////////////////////////////////////////////////////////// +// Wait for process termination +// Arguments: (see Spawner.java) +// [in] uid - unique process ID +///////////////////////////////////////////////////////////////////////////////////// JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor (JNIEnv * env, jobject process, jint uid) -{ - long exit_code; - int what=0; +{ + long exit_code; + int what=0; HANDLE hProc; pProcInfo_t pCurProcInfo = findProcInfo(uid); @@ -590,25 +645,31 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor what = WaitForSingleObject(hProc, INFINITE); - + if (what == WAIT_OBJECT_0) - { - GetExitCodeProcess(hProc, &exit_code); + { + GetExitCodeProcess(hProc, &exit_code); } if(hProc) CloseHandle(hProc); - return exit_code; -} - - - + return exit_code; +} + + + // Utilities +///////////////////////////////////////////////////////////////////////////////////// +// Throws Java exception (will be trapped by VM). +// Arguments: +// [in] name - name of exception class +// [in] message to assign thi event +///////////////////////////////////////////////////////////////////////////////////// JNIEXPORT void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg) { @@ -624,6 +685,11 @@ ThrowByName(JNIEnv *env, const char *name, const char *msg) +///////////////////////////////////////////////////////////////////////////////////// +// Create process description block. +// Arguments: no +// Return : pointer to the process descriptor +///////////////////////////////////////////////////////////////////////////////////// pProcInfo_t createProcInfo() { int i; @@ -634,7 +700,7 @@ pProcInfo_t createProcInfo() if(NULL == pInfo) { pInfo = malloc(sizeof(procInfo_t) * MAX_PROCS); - memset(pInfo, 0, sizeof(procInfo_t) * MAX_PROCS); + ZeroMemory(pInfo, sizeof(procInfo_t) * MAX_PROCS); } for(i = 0; i < MAX_PROCS; ++i) @@ -653,6 +719,11 @@ pProcInfo_t createProcInfo() return p; } +///////////////////////////////////////////////////////////////////////////////////// +// Using unique process ID finds process descriptor +// Arguments: no +// Return : pointer to the process descriptor +///////////////////////////////////////////////////////////////////////////////////// pProcInfo_t findProcInfo(int uid) { int i; @@ -672,6 +743,11 @@ pProcInfo_t findProcInfo(int uid) return p; } +///////////////////////////////////////////////////////////////////////////////////// +// Cleans up vacant process descriptor +// Arguments: +// pCurProcInfo - pointer to descriptor to clean up +// Return : no void cleanUpProcBlock(pProcInfo_t pCurProcInfo) { if(0 != pCurProcInfo -> eventBreak) @@ -693,11 +769,16 @@ void cleanUpProcBlock(pProcInfo_t pCurProcInfo) pCurProcInfo -> pid = 0; } +///////////////////////////////////////////////////////////////////////////////////// +// Running in separae thread and waiting for the process termination +// Arguments: +// pv - (int)pv is a pid +// Return : always 0 +///////////////////////////////////////////////////////////////////////////////////// unsigned int _stdcall waitProcTermination(void* pv) { int i; int pid = (int)pv; - DWORD rc = 0; #ifdef DEBUG_MONITOR char buffer[1000]; #endif @@ -742,7 +823,15 @@ unsigned int _stdcall waitProcTermination(void* pv) return 0; } -// Return number of bytes in target or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// +// Use this utility program to process correctly quotation marks in the command line +// Arguments: +// target - string to copy to +// source - string to copy from +// cpyLength - copy length +// availSpace - size of the target buffer +// Return :number of bytes used in target, or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// int copyTo(char * target, const char * source, int cpyLength, int availSpace) { BOOL bSlash = FALSE; diff --git a/core/org.eclipse.cdt.core.win32/library/iostream.c b/core/org.eclipse.cdt.core.win32/library/iostream.c index faae3c11120..2c091176775 100644 --- a/core/org.eclipse.cdt.core.win32/library/iostream.c +++ b/core/org.eclipse.cdt.core.win32/library/iostream.c @@ -1,23 +1,25 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation + * + * raise.c + * + * This is a part of JNI implementation of spawner + * Includes implementation of JNI methods (see Spawner.java) ***********************************************************************/ -/* - * This is a JNI implementation of access to standard i/o streams - */ #include "stdafx.h" #include #include +#include "spawner.h" #include "SpawnerInputStream.h" #include "SpawnerOutputStream.h" - #include "jni.h" #include "io.h" @@ -37,35 +39,87 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_rea { BYTE tmpBuf[BUFF_SIZE]; int nBuffOffset = 0; +#ifdef DEBUG_MONITOR + char buffer[1000]; +#endif + OVERLAPPED overlapped; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + overlapped.hEvent = CreateEvent(NULL, // no security attribute + TRUE, // manual-reset event + TRUE, // initial state = signaled + NULL); // unnamed event object + + if(NULL == overlapped.hEvent) { + LPTSTR lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + ThrowByName(env, "java/io/IOException", lpMsgBuf); + } + +#ifdef DEBUG_MONITOR + sprintf(buffer, "Start read %i\n", fd); + OutputDebugString(buffer); +#endif while(len > nBuffOffset) { int nNumberOfBytesToRead = min(len - nBuffOffset, BUFF_SIZE); int nNumberOfBytesRead; - if(0 == ReadFile((HANDLE)fd, tmpBuf, nNumberOfBytesToRead, &nNumberOfBytesRead, NULL )) + if(0 == ReadFile((HANDLE)fd, tmpBuf, nNumberOfBytesToRead, &nNumberOfBytesRead, &overlapped )) { - LPTSTR lpMsgBuf; int err = GetLastError(); + if(err == ERROR_IO_PENDING) + { + // asynchronous i/o is still in progress + // check on the results of the asynchronous read + if(GetOverlappedResult((HANDLE)fd, &overlapped, + &nNumberOfBytesRead, TRUE)) + err = 0; + // if there was a problem ... + else + err = GetLastError(); + } if(err == ERROR_BROKEN_PIPE) // Pipe was closed - return 0; - if(err != ERROR_MORE_DATA) // Otherwise error means just that there are more data - { // than buffer can accept - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); + break; + if(err != 0) + { + LPTSTR lpMsgBuf; +#ifdef DEBUG_MONITOR + char buffer[200]; + sprintf(buffer, "Read failed - %i, error %i\n", fd, err); + OutputDebugString(buffer); +#endif + if(err != ERROR_MORE_DATA) // Otherwise error means just that there are more data + { // than buffer can accept + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); - ThrowByName(env, "java/io/IOException", lpMsgBuf); - LocalFree( lpMsgBuf ); - return 0; + ThrowByName(env, "java/io/IOException", lpMsgBuf); + LocalFree( lpMsgBuf ); + nBuffOffset = 0; + break; + } } } if(nNumberOfBytesRead > 0) @@ -76,6 +130,11 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_rea if(nNumberOfBytesRead != nNumberOfBytesToRead) break; } + CloseHandle(overlapped.hEvent); +#ifdef DEBUG_MONITOR + sprintf(buffer, "End read %i\n", fd); + OutputDebugString(buffer); +#endif return nBuffOffset; // This is a real full readed length } @@ -88,7 +147,19 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_rea JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0 (JNIEnv * env, jobject proc, jint fd) { - return (CloseHandle((HANDLE)fd) ? 0 : -1); + int rc; +#ifdef DEBUG_MONITOR + char buffer[1000]; + sprintf(buffer, "Close %i\n", fd); + OutputDebugString(buffer); +#endif + DisconnectNamedPipe((HANDLE)fd); + rc = (CloseHandle((HANDLE)fd) ? 0 : -1); +#ifdef DEBUG_MONITOR + sprintf(buffer, "Closed %i\n", fd); + OutputDebugString(buffer); +#endif + return (rc ? GetLastError() : 0); } /* @@ -102,6 +173,7 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_wr BYTE tmpBuf[BUFF_SIZE]; int nBuffOffset = 0; + while(len > nBuffOffset) { int nNumberOfBytesToWrite = min(len - nBuffOffset, BUFF_SIZE); @@ -139,5 +211,17 @@ JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_wr JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0 (JNIEnv * env, jobject proc, jint fd) { - return (CloseHandle((HANDLE)fd) ? 0 : -1); + int rc; +#ifdef DEBUG_MONITOR + char buffer[1000]; + sprintf(buffer, "Close %i\n", fd); + OutputDebugString(buffer); +#endif + DisconnectNamedPipe((HANDLE)fd); + rc = (CloseHandle((HANDLE)fd) ? 0 : -1); +#ifdef DEBUG_MONITOR + sprintf(buffer, "Closed %i\n", fd); + OutputDebugString(buffer); +#endif + return (rc ? GetLastError() : 0); } diff --git a/core/org.eclipse.cdt.core.win32/library/raise.c b/core/org.eclipse.cdt.core.win32/library/raise.c index ebe0172f585..b4305e222f4 100644 --- a/core/org.eclipse.cdt.core.win32/library/raise.c +++ b/core/org.eclipse.cdt.core.win32/library/raise.c @@ -1,16 +1,17 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation + * + * raise.c + * + * This is a part of JNI implementation of spawner ***********************************************************************/ -/* - * This is a JNI implementation of spawner - */ #include "stdafx.h" #include "Spawner.h" @@ -19,10 +20,17 @@ extern void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg); -// #define DEBUG_MONITOR static HWND consoleHWND; + +///////////////////////////////////////////////////////////////////////////////////// +// Check if window is a console of process with pid +// Arguments: +// hwnd - window handler +// arg - process PID +// Return : TRUE if yes +///////////////////////////////////////////////////////////////////////////////////// static BOOL CALLBACK find_child_console (HWND hwnd, LPARAM arg) { @@ -46,35 +54,13 @@ find_child_console (HWND hwnd, LPARAM arg) return TRUE; } -/* -JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise__Ljava_lang_Object_2 - (JNIEnv * env, jobject process, jobject jpid) -{ - jint pid; - jclass integerClass = (*env) -> FindClass(env, "java/lang/Integer"); - jmethodID intValue; - if(NULL == integerClass) { - ThrowByName(env, "java/lang/IOException", "Cannot find Integer class"); - return -1; - } - if(!((*env) -> IsInstanceOf(env, jpid, integerClass))) { - ThrowByName(env, "java/lang/IOException", "Wrong argument"); - return -1; - } - - intValue = (*env) -> GetMethodID(env, integerClass, "intValue", "()I"); - if(NULL == intValue) { - ThrowByName(env, "java/lang/IOException", "Cannot find intValue method in Integer class"); - return -1; - } - - pid = (*env) -> CallIntMethod(env, jpid, intValue); - - return interruptProcess(pid); - -} -*/ +///////////////////////////////////////////////////////////////////////////////////// +// Function implements interrupt process (Ctrl-C emulation) +// Arguments: +// pid - process' pid +// Return : 0 if OK or error code +///////////////////////////////////////////////////////////////////////////////////// int interruptProcess(int pid) { #ifdef DEBUG_MONITOR @@ -89,10 +75,13 @@ int interruptProcess(int pid) sprintf(buffer, "Try to interrupt process %i\n", pid); OutputDebugString(buffer); #endif + // Find console EnumWindows (find_child_console, (LPARAM) pid); - if(NULL != consoleHWND) + if(NULL != consoleHWND) // Yes, we found out it { + // We are going to switch focus to console, + // send Ctrl-C and then restore focus BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0); /* Fake Ctrl-C for SIGINT, and Ctrl-Break for SIGQUIT. */ BYTE vk_c_code = 'C'; @@ -100,7 +89,7 @@ int interruptProcess(int pid) BYTE c_scan_code = (BYTE) MapVirtualKey (vk_c_code, 0); BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0); HWND foreground_window; - + foreground_window = GetForegroundWindow (); if (foreground_window) @@ -128,17 +117,6 @@ int interruptProcess(int pid) /* Set the foreground window to the child. */ if (SetForegroundWindow (consoleHWND)) { - /* - if(0 != c_scan_code) { - // Generate keystrokes as if user had typed Ctrl-C. - keybd_event (VK_CONTROL, control_scan_code, 0, 0); - keybd_event (vk_c_code, c_scan_code, 0, 0); - keybd_event (vk_c_code, c_scan_code, KEYEVENTF_KEYUP, 0); - keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0); - } - */ - /* Sleep for a bit to give time for respond */ - Sleep (100); if(0 != break_scan_code) { /* Generate keystrokes as if user had typed Ctrl-Break */ keybd_event (VK_CONTROL, control_scan_code, 0, 0); diff --git a/core/org.eclipse.cdt.core.win32/library/spawner.c b/core/org.eclipse.cdt.core.win32/library/spawner.c index 42217ae985a..b32679980b3 100644 --- a/core/org.eclipse.cdt.core.win32/library/spawner.c +++ b/core/org.eclipse.cdt.core.win32/library/spawner.c @@ -1,22 +1,26 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation + * + * spawner.c + * + * This is a part of JNI implementation of spawner ***********************************************************************/ -// spawner.cpp : Defines the entry point for the DLL application. -// #include "stdafx.h" +#include "spawner.h" CRITICAL_SECTION cs; -TCHAR path[MAX_PATH + 1] = {_T('\0') }; + +TCHAR path[MAX_PATH + 1] = {_T('\0') }; // Directory where spawner.dll is located BOOL APIENTRY DllMain( HANDLE hModule, @@ -48,4 +52,3 @@ BOOL APIENTRY DllMain( HANDLE hModule, return TRUE; } - diff --git a/core/org.eclipse.cdt.core.win32/library/starter/starter.cpp b/core/org.eclipse.cdt.core.win32/library/starter/starter.cpp index 8b41e40a30c..7939182bf54 100644 --- a/core/org.eclipse.cdt.core.win32/library/starter/starter.cpp +++ b/core/org.eclipse.cdt.core.win32/library/starter/starter.cpp @@ -1,22 +1,19 @@ /********************************************************************** - * Copyright (c) 2002,2003 QNX Software Systems and others. + * Copyright (c) 2002-2004 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Common Public License v0.5 + * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation -***********************************************************************/ -/* - * starter.c + * + * starter.cpp * * This is a small utility for windows spawner - */ +***********************************************************************/ -//#define UNICODE -//#define _UNICODE #define STRICT #include @@ -24,8 +21,9 @@ #include #include -// #define DEBUG_MONITOR +//#define DEBUG_MONITOR #define MAX_CMD_LINE_LENGTH (2049) +#define PIPE_NAME_LENGTH 100 int copyTo(char * target, const char * source, int cpyLength, int availSpace); @@ -58,8 +56,8 @@ BOOL WINAPI HandlerRoutine( DWORD dwCtrlType) // control signal type extern "C" int _tmain(int argc, TCHAR* argv[]) { - // Make sure that we've been passed the right number of arguments - if (argc < 5) { + // Make sure that we've been passed the right number of arguments + if (argc < 7) { _tprintf(__TEXT("Usage: %s (Three InheritableEventHandles) (CommandLineToSpawn)\n"), argv[0]); return(0); @@ -69,7 +67,7 @@ extern "C" int _tmain(int argc, TCHAR* argv[]) { TCHAR szCmdLine[MAX_CMD_LINE_LENGTH] = { 0 }; int nPos = 0; - for(int i = 4; i < argc; ++i) + for(int i = 6; i < argc; ++i) { int nCpyLen; if(0 > (nCpyLen = copyTo(szCmdLine + nPos, argv[i], _tcslen(argv[i]), MAX_CMD_LINE_LENGTH - nPos))) @@ -94,28 +92,81 @@ extern "C" int _tmain(int argc, TCHAR* argv[]) { #endif BOOL exitProc = FALSE; - HANDLE waitEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[2]); + HANDLE waitEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[4]); HANDLE h[3]; - h[0] = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[1]); - h[2] = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[3]); // This is a terminate event + h[0] = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[3]); + h[2] = OpenEvent(EVENT_ALL_ACCESS, TRUE, argv[5]); // This is a terminate event SetConsoleCtrlHandler(HandlerRoutine, TRUE); + + int parentPid = strtol(argv[1], NULL, 10); + int nCounter = strtol(argv[2], NULL, 10); + char inPipeName[PIPE_NAME_LENGTH]; + char outPipeName[PIPE_NAME_LENGTH]; + char errPipeName[PIPE_NAME_LENGTH]; + + sprintf(inPipeName, "\\\\.\\pipe\\stdin%08i%010i", parentPid, nCounter); + sprintf(outPipeName, "\\\\.\\pipe\\stdout%08i%010i", parentPid, nCounter); + sprintf(errPipeName, "\\\\.\\pipe\\stderr%08i%010i", parentPid, nCounter); #ifdef DEBUG_MONITOR - sprintf(buffer, "starter start command: %s\n", szCmdLine); + sprintf(buffer, "Pipes: %s, %s, %s\n", inPipeName, outPipeName, errPipeName); OutputDebugString(buffer); #endif + + HANDLE stdHandles[3]; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if((INVALID_HANDLE_VALUE == (stdHandles[0] = CreateFile(inPipeName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, &sa))) || + (INVALID_HANDLE_VALUE == (stdHandles[1] = CreateFile(outPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa))) || + (INVALID_HANDLE_VALUE == (stdHandles[2] = CreateFile(errPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa)))) + { +#ifdef DEBUG_MONITOR + sprintf(buffer, "Failed to open pipe %i, %i, %i: %i\n", stdHandles[0], stdHandles[1], stdHandles[2], GetLastError()); + OutputDebugString(buffer); +#endif + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + return -1;; + } + SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, TRUE); + SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, TRUE); + SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, TRUE); + + if(!SetStdHandle(STD_INPUT_HANDLE, stdHandles[0]) || + !SetStdHandle(STD_OUTPUT_HANDLE, stdHandles[1]) || + !SetStdHandle(STD_ERROR_HANDLE, stdHandles[2])) { +#ifdef DEBUG_MONITOR + sprintf(buffer, "Failed to reassign standard streams: %i\n", GetLastError()); + OutputDebugString(buffer); +#endif + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); + return -1;; + } + -// OutputDebugString(szCmdLine); // Spawn the other processes as part of this Process Group BOOL f = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); - + // We don't need them any more + CloseHandle(stdHandles[0]); + CloseHandle(stdHandles[1]); + CloseHandle(stdHandles[2]); if (f) { +#ifdef DEBUG_MONITOR + sprintf(buffer, "Process %i started\n", pi.dwProcessId); + OutputDebugString(buffer); +#endif SetEvent(waitEvent); // Means thar process has been spawned CloseHandle(pi.hThread); h[1] = pi.hProcess; - while(!exitProc) { // Wait for the spawned-process to die or for the event @@ -151,6 +202,7 @@ extern "C" int _tmain(int argc, TCHAR* argv[]) { break; default: // Unexpected code +#ifdef DEBUG_MONITOR LPTSTR lpMsgBuf; FormatMessage( @@ -167,6 +219,7 @@ extern "C" int _tmain(int argc, TCHAR* argv[]) { OutputDebugString(lpMsgBuf); // Free the buffer. LocalFree( lpMsgBuf ); +#endif exitProc = TRUE; break; } @@ -177,12 +230,21 @@ extern "C" int _tmain(int argc, TCHAR* argv[]) { CloseHandle(waitEvent); CloseHandle(h[0]); + CloseHandle(h[1]); CloseHandle(h[2]); return(dwExitCode); } -// Return number of bytes in target or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// +// Use this utility program to process correctly quotation marks in the command line +// Arguments: +// target - string to copy to +// source - string to copy from +// cpyLength - copy length +// availSpace - size of the target buffer +// Return :number of bytes used in target, or -1 in case of error +///////////////////////////////////////////////////////////////////////////////////// int copyTo(LPTSTR target, LPCTSTR source, int cpyLength, int availSpace) { BOOL bSlash = FALSE; diff --git a/core/org.eclipse.cdt.core.win32/os/win32/x86/spawner.dll b/core/org.eclipse.cdt.core.win32/os/win32/x86/spawner.dll index 771fd664dad..07fa17c03ed 100644 Binary files a/core/org.eclipse.cdt.core.win32/os/win32/x86/spawner.dll and b/core/org.eclipse.cdt.core.win32/os/win32/x86/spawner.dll differ diff --git a/core/org.eclipse.cdt.core.win32/os/win32/x86/starter.exe b/core/org.eclipse.cdt.core.win32/os/win32/x86/starter.exe index 38dd37c649d..a51e76dc846 100644 Binary files a/core/org.eclipse.cdt.core.win32/os/win32/x86/starter.exe and b/core/org.eclipse.cdt.core.win32/os/win32/x86/starter.exe differ