Это очень распространенная проблема, с которой мы сталкиваемся, борясь с «таинствами» JavaScript.
Давайте начнем с простой функции JavaScript:
function foo(){
// do something
return 'wohoo';
}
let bar = foo(); // bar is 'wohoo' here
Это простой синхронный вызов функции (где каждая строка кода выполняется одна за другой в последовательность), и результат будет таким же, как ожидалось.
Теперь добавим немного завихрения, введя небольшую задержку в нашей функции, чтобы все строки кода не выполнялись последовательно. Таким образом, он будет эмулировать асинхронное поведение функции:
function foo(){
setTimeout( ()=>{
return 'wohoo';
}, 1000 )
}
let bar = foo() // bar is undefined here
Итак, вы идете, эта задержка просто сломала функциональность, которую мы ожидали! Но что именно произошло? Ну, на самом деле это довольно логично, если вы посмотрите на код. функция foo()
после выполнения ничего не возвращает (таким образом, возвращаемое значение равно undefined
), но оно запускает таймер, который выполняет функцию после 1s, чтобы вернуть «wohoo». Но, как вы можете видеть, значение, присвоенное бару, является немедленно возвращенным материалом из foo (), а не что-либо еще, что приходит позже.
Итак, как мы решаем эту проблему?
Давайте попросим нашу функцию для ОБЕЩАНИЯ. Обещание действительно о том, что это означает: это означает, что функция гарантирует, что вы предоставите любой результат, который он получит в будущем. поэтому давайте посмотрим на это в нашей маленькой проблеме выше:
function foo(){
return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
setTimeout ( function(){
// promise is RESOLVED , when exececution reaches this line of code
resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
}, 1000 )
})
}
let bar ;
foo().then( res => {
bar = res;
console.log(bar) // will print 'wohoo'
});
Таким образом, резюме - для решения асинхронных функций, таких как вызовы на основе ajax и т. д., вы можете использовать обещание resolve
значение (которое вы намерены вернуть). Таким образом, короче говоря, вы разрешаете значение вместо возврата в асинхронных функциях.
Самое близкое, что я пришел к решению, это SendSignal стороннее приложение. Автор перечисляет исходный код и исполняемый файл. Я проверил, что он работает под 64-битными окнами (работает как 32-разрядная программа, убивая еще одну 32-разрядную программу), но я не понял, как вставлять код в программу Windows (либо 32-разрядную или 64-бит).
Как это работает:
После многократного поиска в отладчике я обнаружил, что точка входа, которая фактически выполняет поведение, связанное с сигналом, как ctrl-break - это kernel32! CtrlRoutine. Функция имела тот же прототип, что и ThreadProc, поэтому его можно использовать непосредственно с CreateRemoteThread, без необходимости вводить код. Однако это не экспортированный символ! Он находится на разных адресах (и даже имеет разные имена) в разных версиях Windows. Что делать?
Вот решение, которое я, наконец, придумал. Я устанавливаю консольный обработчик ctrl для своего приложения, а затем генерирую сигнал ctrl-break для моего приложения. Когда мой обработчик вызван, я оглядываюсь наверху стека, чтобы узнать параметры, переданные в kernel32! BaseThreadStart. Я беру первый параметр, который является желаемым начальным адресом потока, который является адресом kernel32! CtrlRoutine. Затем я возвращаюсь из своего обработчика, указывая, что я обработал сигнал, и мое приложение не должно быть прекращено. Вернемся в основной поток, я жду, пока не будет найден адрес ядра32! CtrlRoutine. Как только у меня получится, я создаю удаленный поток в целевом процессе с открытым начальным адресом. Это приводит к тому, что обработчики ctrl в целевом процессе оцениваются так, как если бы был нажат ctrl-break!
Приятно, что затрагивается только целевой процесс, и любой процесс (даже оконный процесс) может быть целенаправленным. Один недостаток заключается в том, что мое маленькое приложение не может использоваться в пакетном файле, так как оно убьет его, когда он отправит событие ctrl-break, чтобы обнаружить адрес kernel32! CtrlRoutine.
blockquote>(запустите его с помощью
start
, если он запущен в пакетном файле.)
Вот код, который я использую в своем приложении на C ++.
Положительные точки:
Отрицательные точки:
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
{
bool success = false;
DWORD thisConsoleId = GetCurrentProcessId();
// Leave current console if it exists
// (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
bool consoleDetached = (FreeConsole() != FALSE);
if (AttachConsole(dwProcessId) != FALSE)
{
// Add a fake Ctrl-C handler for avoid instant kill is this console
// WARNING: do not revert it or current program will be also killed
SetConsoleCtrlHandler(nullptr, true);
success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
FreeConsole();
}
if (consoleDetached)
{
// Create a new console if previous was deleted by OS
if (AttachConsole(thisConsoleId) == FALSE)
{
int errorCode = GetLastError();
if (errorCode == 31) // 31=ERROR_GEN_FAILURE
{
AllocConsole();
}
}
}
return success;
}
Пример использования:
DWORD dwProcessId = ...;
if (signalCtrl(dwProcessId, CTRL_C_EVENT))
{
cout << "Signal sent" << endl;
}
На основе идентификатора процесса мы можем отправить сигнал для обработки для принудительного или грациозного прекращения или любого другого сигнала.
Список всех процессов:
C:\>tasklist
Чтобы убить процесс :
C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F
Подробности:
http://tweaks.com/windows/39559/kill-processes-from-command-prompt/
В Java, используя JNA с библиотекой Kernel32.dll, похож на C ++-решение. Запускает основной метод CtrlCSender как процесс, который просто получает консоль процесса для отправки события Ctrl + C и генерирует событие. Когда он запускается отдельно без консоли, событие Ctrl + C не нужно отключать и снова активировать.
CtrlCSender.java - На основе Nemo1024's и KindDragon's .
Учитывая известный идентификатор процесса, это консольное приложение будет прикреплять консоль целевого процесса и генерировать на нем событие CTRL + C.
import com.sun.jna.platform.win32.Kernel32;
public class CtrlCSender {
public static void main(String args[]) {
int processId = Integer.parseInt(args[0]);
Kernel32.INSTANCE.AttachConsole(processId);
Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);
}
}
Основное приложение - Выполняет CtrlCSender как отдельный процесс утешения
ProcessBuilder pb = new ProcessBuilder();
pb.command("javaw", "-cp", System.getProperty("java.class.path", "."), CtrlCSender.class.getName(), processId);
pb.redirectErrorStream();
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Process ctrlCProcess = pb.start();
ctrlCProcess.waitFor();
Как-то GenerateConsoleCtrlEvent()
возвращает ошибку, если вы вызываете ее для другого процесса, но можете подключиться к другому консольному приложению и отправить событие ко всем дочерним процессам.
void SendControlC(int pid)
{
AttachConsole(pid); // attach to process console
SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event
}
Я нашел все это слишком сложным и использовал SendKeys для отправки нажатия CTRL-C в окно командной строки (то есть окно cmd.exe) в качестве обходного пути.
Изменить:
Для приложения GUI «нормальный» способ справиться с этим при разработке Windows - это отправить сообщение WM_CLOSE в главное окно процесса.
Для console app, вам нужно использовать SetConsoleCtrlHandler , чтобы добавить CTRL_C_EVENT
.
Если приложение не соблюдает это, вы можете вызвать TerminateProcess .
Это должно быть прозрачным, потому что в данный момент это не так. Существует модифицированная и скомпилированная версия SendSignal для отправки Ctrl-C (по умолчанию она отправляет Ctrl + Break). Вот несколько бинарных файлов:
(2014-3-7): я построил 32-битную и 64-битную версию с помощью Ctrl-C, это называется SendSignalCtrlC.exe, и вы можете скачать ее на : https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86/SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/ SendSignalCtrlC.exe - Juraj Michalak
blockquote>Я также отразил эти файлы на всякий случай: 32-разрядная версия: https://www.dropbox.com/ s / r96jxglhkm4sjz2 / SendSignalCtrlC.exe? dl = 0 64-разрядная версия: https://www.dropbox.com/s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0
Отказ от ответственности: я не создал эти файлы. Скомпилированные исходные файлы не были внесены. Единственной протестированной платформой является 64-разрядная Windows 7. Рекомендуется адаптировать исходный код, доступный в http://www.latenighthacking.com/projects/2003/sendSignal/ , и скомпилировать его самостоятельно.
Друг мой предложил совершенно другой способ решения проблемы, и это сработало для меня. Используйте vbscript, как показано ниже. Он запускается и запускается, запускается в течение 7 секунд и закрывает его с помощью ctrl + c.
'Пример VBScript
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "notepad.exe"
WshShell.AppActivate "notepad"
WScript.Sleep 7000
WshShell.SendKeys "^C"
void SendSIGINT( HANDLE hProcess )
{
DWORD pid = GetProcessId(hProcess);
FreeConsole();
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(NULL, true);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // SIGINT
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(NULL, false);
WaitForSingleObject(hProcess, 10000);
}
}