From 483cf03d1d6974c73bc8f0c509199324da6ea7b3 Mon Sep 17 00:00:00 2001 From: Stephan Reiter Date: Fri, 25 May 2018 11:03:54 +0200 Subject: [PATCH 1/4] Add sendCtrlC method --- native/java-interface.cpp | 4 +++ native/java-interface.h | 8 +++++ native/runtime.cpp | 31 ++++++++++++++++ native/stdafx.h | 1 + native/winp.cpp | 35 +++++++++++++++++++ native/winp.h | 4 +++ src/main/java/org/jvnet/winp/Native.java | 1 + src/main/java/org/jvnet/winp/WinProcess.java | 6 ++++ .../java/org/jvnet/winp/NativeAPITest.java | 15 +++++++- 9 files changed, 104 insertions(+), 1 deletion(-) diff --git a/native/java-interface.cpp b/native/java-interface.cpp index a9aa34c..e3f5121 100644 --- a/native/java-interface.cpp +++ b/native/java-interface.cpp @@ -7,6 +7,10 @@ JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_kill(JNIEnv* env, jclass c return KillProcessEx(pid, recursive); } +JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_sendCtrlC(JNIEnv* env, jclass clazz, jint pid) { + return SendCtrlC(pid); +} + JNIEXPORT jint JNICALL Java_org_jvnet_winp_Native_setPriority(JNIEnv* env, jclass clazz, jint pid, jint priority) { auto_handle hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid); if(hProcess && SetPriorityClass(hProcess, priority)) { diff --git a/native/java-interface.h b/native/java-interface.h index aebbaf0..2a7beb4 100644 --- a/native/java-interface.h +++ b/native/java-interface.h @@ -15,6 +15,14 @@ extern "C" { JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_kill (JNIEnv *, jclass, jint, jboolean); +/* + * Class: org_jvnet_winp_Native + * Method: sendCtrlC + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_sendCtrlC + (JNIEnv *, jclass, jint); + /* * Class: org_jvnet_winp_Native * Method: isCriticalProcess diff --git a/native/runtime.cpp b/native/runtime.cpp index f316f28..89ff599 100644 --- a/native/runtime.cpp +++ b/native/runtime.cpp @@ -1,15 +1,46 @@ #include "stdafx.h" #include "winp.h" +#include +HANDLE hDllInst; LPFN_ISWOW64PROCESS fnIsWow64Process; extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG dwReason, LPVOID lpReserved) { + hDllInst = hInst; fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); return TRUE; } +extern "C" __declspec(dllexport) void CALLBACK SendCtrlCMain(HWND hwnd, HINSTANCE hinst, LPCSTR pszCmdLine, int nCmdShow) { + int pid = -1; + int count = sscanf_s(pszCmdLine, "%i", &pid); + if (count != 1) { + return; + } + + FreeConsole(); + if (!AttachConsole(pid)) { + return; + } + + SetConsoleCtrlHandler(NULL, TRUE); + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); +} + +std::wstring GetDllFilename() { + std::vector pathBuf; + DWORD copied = 0; + do { + pathBuf.resize(pathBuf.size() + MAX_PATH); + copied = GetModuleFileNameW(static_cast(hDllInst), &pathBuf[0], static_cast(pathBuf.size())); + } + while (copied >= pathBuf.size()); + + return std::wstring(pathBuf.begin(), pathBuf.begin() + copied); +} + void error( JNIEnv* env, const char* file, int line, const char* msg ) { DWORD errorCode = GetLastError(); diff --git a/native/stdafx.h b/native/stdafx.h index 07b04a9..80091f8 100644 --- a/native/stdafx.h +++ b/native/stdafx.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #pragma comment(lib, "psapi.lib") // if you don't have ntdll.lib, download Windows DDK diff --git a/native/winp.cpp b/native/winp.cpp index b4eac97..d083aed 100644 --- a/native/winp.cpp +++ b/native/winp.cpp @@ -5,6 +5,41 @@ #include "winp.h" #include "auto_handle.h" #include "java-interface.h" +#include +#include +//--------------------------------------------------------------------------- +// SendCtrlC +// +// Sends CTRL+C to the specified process. +// +// Parameters: +// dwProcessId - identifier of the process to terminate +// +// Returns: +// TRUE, if successful, FALSE - otherwise. +// +BOOL WINAPI SendCtrlC(IN DWORD dwProcessId) { + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + std::wstring cmd = L"rundll32.exe " + GetDllFilename() + L",SendCtrlCMain " + + std::to_wstring(dwProcessId); + std::vector cmd_buffer(cmd.begin(), cmd.end()); // with C++17, could just use cmd.data() + + BOOL started = CreateProcessW(NULL, &cmd_buffer[0], NULL, NULL, + FALSE, 0, NULL, NULL, &si, &pi); + if (started) { + // wait for termination if the process started + WaitForSingleObject(pi.hProcess, INFINITE); + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return started; +} //--------------------------------------------------------------------------- // KillProcess diff --git a/native/winp.h b/native/winp.h index 1c197c5..0293f25 100644 --- a/native/winp.h +++ b/native/winp.h @@ -6,6 +6,10 @@ #define reportError(env,msg) error(env,__FILE__,__LINE__,msg); void error(JNIEnv* env, const char* file, int line, const char* msg); +std::wstring GetDllFilename(); + +BOOL WINAPI SendCtrlC(IN DWORD dwProcessId); + // // Kernel32.dll // diff --git a/src/main/java/org/jvnet/winp/Native.java b/src/main/java/org/jvnet/winp/Native.java index 95ae60a..229aa10 100755 --- a/src/main/java/org/jvnet/winp/Native.java +++ b/src/main/java/org/jvnet/winp/Native.java @@ -21,6 +21,7 @@ class Native { public static final String DLL_NAME = "64".equals(System.getProperty("sun.arch.data.model")) ? "winp.x64" : "winp"; native static boolean kill(int pid, boolean recursive); + native static boolean sendCtrlC(int pid); native static boolean isCriticalProcess(int pid); native static int setPriority(int pid, int value); native static int getProcessId(int handle); diff --git a/src/main/java/org/jvnet/winp/WinProcess.java b/src/main/java/org/jvnet/winp/WinProcess.java index 7b68a18..d36de0f 100755 --- a/src/main/java/org/jvnet/winp/WinProcess.java +++ b/src/main/java/org/jvnet/winp/WinProcess.java @@ -78,6 +78,12 @@ public void kill() { Native.kill(pid,false); } + public boolean sendCtrlC() { + if (LOGGER.isLoggable(FINE)) + LOGGER.fine(String.format("Attempting to send CTRL+C to pid=%d (%s)",pid,getCommandLine())); + return Native.sendCtrlC(pid); + } + public boolean isCriticalProcess() { return Native.isCriticalProcess(pid); } diff --git a/src/test/java/org/jvnet/winp/NativeAPITest.java b/src/test/java/org/jvnet/winp/NativeAPITest.java index f697970..3745233 100644 --- a/src/test/java/org/jvnet/winp/NativeAPITest.java +++ b/src/test/java/org/jvnet/winp/NativeAPITest.java @@ -119,7 +119,20 @@ public void testKill() throws Exception { Thread.sleep(100); wp.killRecursively(); } - + + @Test + public void testSendCtrlC() throws Exception { + Process p = spawnProcess("cmd"); + + WinProcess wp = new WinProcess(p); + Thread.sleep(100); + + wp.sendCtrlC(); + Thread.sleep(4000); + + wp.killRecursively(); + } + @Test public void shouldFailForNonExistentProcess() { int nonExistentPid = Integer.MAX_VALUE; From 706d75e710719a9f352c9bfe2242487fb41944da Mon Sep 17 00:00:00 2001 From: Stephan Reiter Date: Fri, 25 May 2018 13:01:52 +0200 Subject: [PATCH 2/4] Add separate executable for sending Ctrl+C --- build.cmd | 12 ++ native/runtime.cpp | 16 -- native/sendctrlc/main.cpp | 24 +++ native/sendctrlc/sendctrlc.vcxproj | 160 ++++++++++++++++++ native/winp.cpp | 31 +++- native/winp.sln | 9 + src/main/java/org/jvnet/winp/WinProcess.java | 9 + .../java/org/jvnet/winp/NativeAPITest.java | 31 +++- 8 files changed, 265 insertions(+), 27 deletions(-) create mode 100644 native/sendctrlc/main.cpp create mode 100644 native/sendctrlc/sendctrlc.vcxproj diff --git a/build.cmd b/build.cmd index 13fd676..4d62f48 100644 --- a/build.cmd +++ b/build.cmd @@ -34,6 +34,10 @@ msbuild winp.vcxproj /t:Clean /p:Configuration=%configuration% /verbosity:minima if %errorlevel% neq 0 exit /b %errorlevel% msbuild winp.vcxproj /t:Clean /p:Configuration=%configuration% /verbosity:minimal /nologo /p:Platform="x64" if %errorlevel% neq 0 exit /b %errorlevel% +msbuild sendctrlc\sendctrlc.vcxproj /t:Clean /p:Configuration=Release /verbosity:minimal /nologo /p:Platform="Win32" +if %errorlevel% neq 0 exit /b %errorlevel% +msbuild sendctrlc\sendctrlc.vcxproj /t:Clean /p:Configuration=Release /verbosity:minimal /nologo /p:Platform="x64" +if %errorlevel% neq 0 exit /b %errorlevel% msbuild ..\native_test\testapp\testapp.vcxproj /t:Clean /p:Configuration=Release /verbosity:minimal /nologo /p:Platform="Win32" if %errorlevel% neq 0 exit /b %errorlevel% msbuild ..\native_test\testapp\testapp.vcxproj /t:Clean /p:Configuration=Release /verbosity:minimal /nologo /p:Platform="x64" @@ -48,6 +52,10 @@ msbuild winp.vcxproj /p:Configuration=%configuration% /nologo /p:Platform="Win32 if %errorlevel% neq 0 exit /b %errorlevel% msbuild winp.vcxproj /p:Configuration=%configuration% /nologo /p:Platform="x64" if %errorlevel% neq 0 exit /b %errorlevel% +msbuild sendctrlc\sendctrlc.vcxproj /p:Configuration=%configuration% /nologo /p:Platform="Win32" +if %errorlevel% neq 0 exit /b %errorlevel% +msbuild sendctrlc\sendctrlc.vcxproj /p:Configuration=%configuration% /nologo /p:Platform="x64" +if %errorlevel% neq 0 exit /b %errorlevel% echo ### Building test applications msbuild ..\native_test\testapp\testapp.vcxproj /verbosity:minimal /p:Configuration=Release /nologo /p:Platform="Win32" @@ -61,6 +69,10 @@ COPY native\%configuration%\winp.dll src\main\resources\winp.dll if %errorlevel% neq 0 exit /b %errorlevel% COPY native\x64\%configuration%\winp.dll src\main\resources\winp.x64.dll if %errorlevel% neq 0 exit /b %errorlevel% +COPY native\sendctrlc\Win32\%configuration%\sendctrlc.exe src\main\resources\sendctrlc.exe +if %errorlevel% neq 0 exit /b %errorlevel% +COPY native\sendctrlc\x64\%configuration%\sendctrlc.exe src\main\resources\sendctrlc.x64.exe +if %errorlevel% neq 0 exit /b %errorlevel% echo ### Build and Test winp.jar for %version% cd %BUIDROOT% diff --git a/native/runtime.cpp b/native/runtime.cpp index 89ff599..6cbebf3 100644 --- a/native/runtime.cpp +++ b/native/runtime.cpp @@ -13,22 +13,6 @@ BOOL WINAPI DllMain(HANDLE hInst, ULONG dwReason, LPVOID lpReserved) { return TRUE; } -extern "C" __declspec(dllexport) void CALLBACK SendCtrlCMain(HWND hwnd, HINSTANCE hinst, LPCSTR pszCmdLine, int nCmdShow) { - int pid = -1; - int count = sscanf_s(pszCmdLine, "%i", &pid); - if (count != 1) { - return; - } - - FreeConsole(); - if (!AttachConsole(pid)) { - return; - } - - SetConsoleCtrlHandler(NULL, TRUE); - GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); -} - std::wstring GetDllFilename() { std::vector pathBuf; DWORD copied = 0; diff --git a/native/sendctrlc/main.cpp b/native/sendctrlc/main.cpp new file mode 100644 index 0000000..964f82e --- /dev/null +++ b/native/sendctrlc/main.cpp @@ -0,0 +1,24 @@ + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include + +int main(int argc, char** argv) { + if (argc < 2) { + return 2; + } + + int pid = atoi(argv[1]); + + FreeConsole(); + if (!AttachConsole(pid)) { + return 1; + } + + SetConsoleCtrlHandler(NULL, TRUE); + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + return 0; +} diff --git a/native/sendctrlc/sendctrlc.vcxproj b/native/sendctrlc/sendctrlc.vcxproj new file mode 100644 index 0000000..396222b --- /dev/null +++ b/native/sendctrlc/sendctrlc.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1} + Win32Proj + sendctrlc32 + sendctrlc + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(MSBuildProjectDirectory)\$(Platform)\$(Configuration)\ + + + true + $(MSBuildProjectDirectory)\$(Platform)\$(Configuration)\ + + + false + $(MSBuildProjectDirectory)\$(Platform)\$(Configuration)\ + + + false + $(MSBuildProjectDirectory)\$(Platform)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + \ No newline at end of file diff --git a/native/winp.cpp b/native/winp.cpp index d083aed..1186645 100644 --- a/native/winp.cpp +++ b/native/winp.cpp @@ -6,7 +6,14 @@ #include "auto_handle.h" #include "java-interface.h" #include -#include + +static void str_replace_last(std::wstring& exepath, const wchar_t* from, const wchar_t* to) { + std::wstring::size_type pos = exepath.rfind(from); + if (pos != std::wstring::npos) { + exepath.replace(pos, wcslen(from), to); + } +} + //--------------------------------------------------------------------------- // SendCtrlC // @@ -25,20 +32,32 @@ BOOL WINAPI SendCtrlC(IN DWORD dwProcessId) { si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - std::wstring cmd = L"rundll32.exe " + GetDllFilename() + L",SendCtrlCMain " + - std::to_wstring(dwProcessId); + std::wstring exepath = GetDllFilename(); + str_replace_last(exepath, L"\\winp.dll", L"\\sendctrlc.exe"); + str_replace_last(exepath, L"\\winp.x64.dll", L"\\sendctrlc.x64.exe"); + + std::wstring cmd = exepath + L' ' + std::to_wstring(dwProcessId); std::vector cmd_buffer(cmd.begin(), cmd.end()); // with C++17, could just use cmd.data() BOOL started = CreateProcessW(NULL, &cmd_buffer[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + + BOOL success = FALSE; if (started) { - // wait for termination if the process started - WaitForSingleObject(pi.hProcess, INFINITE); + // wait for termination if the process started, max. 5 secs + WaitForSingleObject(pi.hProcess, 5000); + + // then set success flag if the exit code was 0 + DWORD exit_code; + if (GetExitCodeProcess(pi.hProcess, &exit_code) != FALSE) { + success = (exit_code == 0); + } } + CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - return started; + return success; } //--------------------------------------------------------------------------- diff --git a/native/winp.sln b/native/winp.sln index 4e5c46e..5b9f63d 100644 --- a/native/winp.sln +++ b/native/winp.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winp", "winp.vcxproj", "{6B623E61-2427-4C6A-B028-35E9A397AC3A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sendctrlc", "sendctrlc\sendctrlc.vcxproj", "{AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "native_test", "native_test", "{3F7718C9-97FF-4839-8197-63A69FAD6E54}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testapp", "..\native_test\testapp\testapp.vcxproj", "{AF1488B6-BF5B-4F3F-8AB7-5114D5BF93A1}" @@ -25,6 +27,13 @@ Global {6B623E61-2427-4C6A-B028-35E9A397AC3A}.Release|Win32.Build.0 = Release|Win32 {6B623E61-2427-4C6A-B028-35E9A397AC3A}.Release|x64.ActiveCfg = Release|x64 {6B623E61-2427-4C6A-B028-35E9A397AC3A}.Release|x64.Build.0 = Release|x64 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Debug|Win32.ActiveCfg = Debug|Win32 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Debug|Win32.Build.0 = Debug|Win32 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Debug|x64.ActiveCfg = Debug|Win32 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Release|Win32.ActiveCfg = Release|Win32 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Release|Win32.Build.0 = Release|Win32 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Release|x64.ActiveCfg = Release|x64 + {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93B1}.Release|x64.Build.0 = Release|x64 {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93A1}.Debug|Win32.ActiveCfg = Debug|Win32 {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93A1}.Debug|Win32.Build.0 = Debug|Win32 {AF1488B6-BF5B-4F3F-8AB7-5114D5BF93A1}.Debug|x64.ActiveCfg = Debug|Win32 diff --git a/src/main/java/org/jvnet/winp/WinProcess.java b/src/main/java/org/jvnet/winp/WinProcess.java index d36de0f..0fe7231 100755 --- a/src/main/java/org/jvnet/winp/WinProcess.java +++ b/src/main/java/org/jvnet/winp/WinProcess.java @@ -84,6 +84,15 @@ public boolean sendCtrlC() { return Native.sendCtrlC(pid); } + public boolean isRunning() { + try { + Native.getCmdLine(pid); + return true; + } catch (WinpException e) { + return false; + } + } + public boolean isCriticalProcess() { return Native.isCriticalProcess(pid); } diff --git a/src/test/java/org/jvnet/winp/NativeAPITest.java b/src/test/java/org/jvnet/winp/NativeAPITest.java index 3745233..ab95225 100644 --- a/src/test/java/org/jvnet/winp/NativeAPITest.java +++ b/src/test/java/org/jvnet/winp/NativeAPITest.java @@ -121,15 +121,36 @@ public void testKill() throws Exception { } @Test - public void testSendCtrlC() throws Exception { - Process p = spawnProcess("cmd"); + public void testPingAsDelay() throws Exception { + Process p = spawnProcess("PING", "-n", "10", "127.0.0.1"); // run for 10 secs WinProcess wp = new WinProcess(p); - Thread.sleep(100); + assertTrue(wp.isRunning()); - wp.sendCtrlC(); - Thread.sleep(4000); + Thread.sleep(4000); // just wait, don't send Ctrl+C + + assertTrue(wp.isRunning()); + wp.killRecursively(); + } + + @Test + public void testSendCtrlC() throws Exception { + Process p = spawnProcess("PING", "-n", "10", "127.0.0.1"); // run for 10 secs + + WinProcess wp = new WinProcess(p); + assertTrue(wp.isRunning()); + // send Ctrl+C, then wait for a max of 4 secs + boolean sent = wp.sendCtrlC(); + assertTrue(sent); + for (int i = 0; i < 40; ++i) { + if (!wp.isRunning()) { + break; + } + Thread.sleep(100); + } + + assertTrue(!wp.isRunning()); wp.killRecursively(); } From 8ed8f31922f9e6a4e089fafd53355c828a22c784 Mon Sep 17 00:00:00 2001 From: Stephan Reiter Date: Wed, 6 Jun 2018 11:16:10 +0200 Subject: [PATCH 3/4] Extract EXE from JAR and tell DLL where to find it --- native/java-interface.cpp | 5 +- native/java-interface.h | 4 +- native/winp.cpp | 18 ++----- native/winp.h | 4 +- src/main/java/org/jvnet/winp/Native.java | 61 +++++++++++++++++------- 5 files changed, 56 insertions(+), 36 deletions(-) diff --git a/native/java-interface.cpp b/native/java-interface.cpp index e3f5121..7e72cdc 100644 --- a/native/java-interface.cpp +++ b/native/java-interface.cpp @@ -7,8 +7,9 @@ JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_kill(JNIEnv* env, jclass c return KillProcessEx(pid, recursive); } -JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_sendCtrlC(JNIEnv* env, jclass clazz, jint pid) { - return SendCtrlC(pid); +JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_sendCtrlC(JNIEnv* env, jclass clazz, jint pid, jstring sendctrlcExePath) { + const wchar_t* exePath = (wchar_t*)env->GetStringChars(sendctrlcExePath, NULL); + return SendCtrlC(pid, exePath); } JNIEXPORT jint JNICALL Java_org_jvnet_winp_Native_setPriority(JNIEnv* env, jclass clazz, jint pid, jint priority) { diff --git a/native/java-interface.h b/native/java-interface.h index 2a7beb4..b85bfd1 100644 --- a/native/java-interface.h +++ b/native/java-interface.h @@ -18,10 +18,10 @@ JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_kill /* * Class: org_jvnet_winp_Native * Method: sendCtrlC - * Signature: (I)Z + * Signature: (ILjava/lang/String)Z */ JNIEXPORT jboolean JNICALL Java_org_jvnet_winp_Native_sendCtrlC - (JNIEnv *, jclass, jint); + (JNIEnv *, jclass, jint, jstring); /* * Class: org_jvnet_winp_Native diff --git a/native/winp.cpp b/native/winp.cpp index 1186645..185345d 100644 --- a/native/winp.cpp +++ b/native/winp.cpp @@ -7,13 +7,6 @@ #include "java-interface.h" #include -static void str_replace_last(std::wstring& exepath, const wchar_t* from, const wchar_t* to) { - std::wstring::size_type pos = exepath.rfind(from); - if (pos != std::wstring::npos) { - exepath.replace(pos, wcslen(from), to); - } -} - //--------------------------------------------------------------------------- // SendCtrlC // @@ -25,18 +18,15 @@ static void str_replace_last(std::wstring& exepath, const wchar_t* from, const w // Returns: // TRUE, if successful, FALSE - otherwise. // -BOOL WINAPI SendCtrlC(IN DWORD dwProcessId) { +BOOL WINAPI SendCtrlC(IN DWORD dwProcessId, const wchar_t* pExePath) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - std::wstring exepath = GetDllFilename(); - str_replace_last(exepath, L"\\winp.dll", L"\\sendctrlc.exe"); - str_replace_last(exepath, L"\\winp.x64.dll", L"\\sendctrlc.x64.exe"); - - std::wstring cmd = exepath + L' ' + std::to_wstring(dwProcessId); + std::wstring exepath(pExePath); + std::wstring cmd = L'"' + exepath + L"\" " + std::to_wstring(dwProcessId); std::vector cmd_buffer(cmd.begin(), cmd.end()); // with C++17, could just use cmd.data() BOOL started = CreateProcessW(NULL, &cmd_buffer[0], NULL, NULL, @@ -46,7 +36,7 @@ BOOL WINAPI SendCtrlC(IN DWORD dwProcessId) { if (started) { // wait for termination if the process started, max. 5 secs WaitForSingleObject(pi.hProcess, 5000); - + // then set success flag if the exit code was 0 DWORD exit_code; if (GetExitCodeProcess(pi.hProcess, &exit_code) != FALSE) { diff --git a/native/winp.h b/native/winp.h index 0293f25..a28fd70 100644 --- a/native/winp.h +++ b/native/winp.h @@ -8,7 +8,7 @@ void error(JNIEnv* env, const char* file, int line, const char* msg); std::wstring GetDllFilename(); -BOOL WINAPI SendCtrlC(IN DWORD dwProcessId); +BOOL WINAPI SendCtrlC(IN DWORD dwProcessId, const wchar_t* pExePath); // // Kernel32.dll @@ -62,7 +62,7 @@ enum MBI_REGION_TYPE : DWORD { Private = MEM_PRIVATE }; -extern "C" NTSTATUS NTAPI ZwQueryInformationProcess(HANDLE hProcess, PROCESSINFOCLASS infoType, /*out*/ PVOID pBuf, /*sizeof pBuf*/ ULONG lenBuf, SIZE_T* /*PULONG*/ returnLength); +extern "C" NTSTATUS NTAPI ZwQueryInformationProcess(HANDLE hProcess, PROCESSINFOCLASS infoType, /*out*/ PVOID pBuf, /*sizeof pBuf*/ ULONG lenBuf, SIZE_T* /*PULONG*/ returnLength); #define SystemProcessesAndThreadsInformation 5 diff --git a/src/main/java/org/jvnet/winp/Native.java b/src/main/java/org/jvnet/winp/Native.java index 229aa10..4d696cc 100755 --- a/src/main/java/org/jvnet/winp/Native.java +++ b/src/main/java/org/jvnet/winp/Native.java @@ -19,9 +19,10 @@ class Native { public static final String DLL_NAME = "64".equals(System.getProperty("sun.arch.data.model")) ? "winp.x64" : "winp"; + public static final String CTRLCEXE_NAME = "64".equals(System.getProperty("sun.arch.data.model")) ? "sendctrlc.x64" : "sendctrlc"; native static boolean kill(int pid, boolean recursive); - native static boolean sendCtrlC(int pid); + native static boolean sendCtrlC(int pid, String sendctrlcExePath); native static boolean isCriticalProcess(int pid); native static int setPriority(int pid, int value); native static int getProcessId(int handle); @@ -67,8 +68,18 @@ class Native { private static final String DLL_TARGET = "winp.folder.preferred"; private static final String UNPACK_DLL_TO_PARENT_DIR = "winp.unpack.dll.to.parent.dir"; + private static String ctrlCExePath; + + public static boolean sendCtrlC(int pid) { + if (ctrlCExePath == null) { + return false; + } + return sendCtrlC(pid, ctrlCExePath); + } + static { - load(); + File exeFile = load(); + ctrlCExePath = (exeFile == null) ? null : exeFile.getPath(); } private static String md5(URL res) { @@ -91,16 +102,18 @@ private static String md5(URL res) { } } - private static void load() { + private static File load() { - final URL res = Native.class.getClassLoader().getResource(DLL_NAME + ".dll"); + final URL dllRes = Native.class.getClassLoader().getResource(DLL_NAME + ".dll"); try { - if (res != null) { - loadByUrl(res); + if (dllRes != null) { + final URL exeRes = Native.class.getClassLoader().getResource(CTRLCEXE_NAME + ".exe"); + return loadByUrl(dllRes, exeRes); } else { // we don't know where winp.dll is, so let's just hope the user put it somewhere System.loadLibrary(DLL_NAME); + return null; } } catch (Throwable cause) { @@ -110,32 +123,40 @@ private static void load() { } } - private static void loadByUrl(URL res) throws IOException { + private static File loadByUrl(URL dllRes, URL exeRes) throws IOException { - String url = res.toExternalForm(); + String dllUrl = dllRes.toExternalForm(); + if (dllUrl.startsWith("file:")) { + // during debug the files are on disk and not in a jar + if (!exeRes.toExternalForm().startsWith("file:")) { + LOGGER.log(Level.WARNING, "DLL and EXE are inconsistenly present on disk"); + } - if (url.startsWith("file:")) { - // during debug File f; try { - f = new File(res.toURI()); + f = new File(dllRes.toURI()); } catch (URISyntaxException e) { - f = new File(res.getPath()); + f = new File(dllRes.getPath()); } loadDll(f); - return; + + File exeFile = new File(f.getParentFile(), CTRLCEXE_NAME + ".exe"); + return exeFile; } try { - File dllFile = extractToStaticLocation(res); + File dllFile = extractToStaticLocation(dllRes); + File exeFile = extractExe(exeRes, dllFile.getParentFile()); loadDll(dllFile); - return; + return exeFile; } catch (Throwable e) { LOGGER.log(Level.WARNING, "Failed to load DLL from static location", e); } - File dllFile = extractToTmpLocation(res); + File dllFile = extractToTmpLocation(dllRes); + File exeFile = extractExe(exeRes, dllFile.getParentFile()); loadDll(dllFile); + return exeFile; } private static File extractToStaticLocation(URL url) throws IOException { @@ -161,6 +182,14 @@ private static File extractToTmpLocation(URL res) throws IOException { return tmpFile; } + private static File extractExe(URL res, File dir) throws IOException { + File destFile = new File(dir, CTRLCEXE_NAME + '.' + md5(res) + ".exe"); + if (!destFile.exists()) { + copyStream(res.openStream(), new FileOutputStream(destFile)); + } + return destFile; + } + private static File getJarFile(URL res) { String url = res.toExternalForm(); From 9d2607d346f02ab146c62ef8476fa69713ff4a08 Mon Sep 17 00:00:00 2001 From: Stephan Reiter Date: Wed, 6 Jun 2018 12:39:46 +0200 Subject: [PATCH 4/4] Remove unused GetDllFilename --- native/runtime.cpp | 12 ------------ native/stdafx.h | 1 - native/winp.cpp | 2 ++ native/winp.h | 2 -- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/native/runtime.cpp b/native/runtime.cpp index 6cbebf3..39a37ca 100644 --- a/native/runtime.cpp +++ b/native/runtime.cpp @@ -13,18 +13,6 @@ BOOL WINAPI DllMain(HANDLE hInst, ULONG dwReason, LPVOID lpReserved) { return TRUE; } -std::wstring GetDllFilename() { - std::vector pathBuf; - DWORD copied = 0; - do { - pathBuf.resize(pathBuf.size() + MAX_PATH); - copied = GetModuleFileNameW(static_cast(hDllInst), &pathBuf[0], static_cast(pathBuf.size())); - } - while (copied >= pathBuf.size()); - - return std::wstring(pathBuf.begin(), pathBuf.begin() + copied); -} - void error( JNIEnv* env, const char* file, int line, const char* msg ) { DWORD errorCode = GetLastError(); diff --git a/native/stdafx.h b/native/stdafx.h index 80091f8..07b04a9 100644 --- a/native/stdafx.h +++ b/native/stdafx.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #pragma comment(lib, "psapi.lib") // if you don't have ntdll.lib, download Windows DDK diff --git a/native/winp.cpp b/native/winp.cpp index 185345d..bec3b67 100644 --- a/native/winp.cpp +++ b/native/winp.cpp @@ -5,6 +5,8 @@ #include "winp.h" #include "auto_handle.h" #include "java-interface.h" + +#include #include //--------------------------------------------------------------------------- diff --git a/native/winp.h b/native/winp.h index a28fd70..1bd5e80 100644 --- a/native/winp.h +++ b/native/winp.h @@ -6,8 +6,6 @@ #define reportError(env,msg) error(env,__FILE__,__LINE__,msg); void error(JNIEnv* env, const char* file, int line, const char* msg); -std::wstring GetDllFilename(); - BOOL WINAPI SendCtrlC(IN DWORD dwProcessId, const wchar_t* pExePath); //