-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSendCtrlC.cpp
More file actions
161 lines (136 loc) · 3.35 KB
/
SendCtrlC.cpp
File metadata and controls
161 lines (136 loc) · 3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// SendCtrlC.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// Helper class for making sure a HANDLE is closed properly.
class ScopedHandle
{
public:
// Construct from a HANDLE.
ScopedHandle(HANDLE inHandle)
: handle(inHandle)
{}
// Destructor: close the handle when going out of scope, if valid.
~ScopedHandle()
{
if (IsValid())
{
CloseHandle(handle);
}
}
// Do we wrap a valid handle.
bool IsValid() const
{
return handle != NULL;
}
// Implicit conversion to HANDLE to allow passing direct to Win32 functions.
operator HANDLE()
{
return handle;
}
private:
HANDLE handle;
// Non-copyable.
ScopedHandle(const ScopedHandle&);
ScopedHandle& operator=(const ScopedHandle&);
};
bool IsCmdExe(DWORD procId);
void TerminateBatchFile();
// Return codes:
// 0: Success
// 1: No process id specified
// 2: Invalid process id (zero or non-numeric)
// 3: Specified process id does not exist
// 4: Specified process is not a console app
// 5: Unknown AttachConsole error
// 6: Failed to send CTRL+C signal to process
int _tmain(int argc, _TCHAR* argv[])
{
if (argc <= 1)
{
// Need process id.
return 1;
}
DWORD procId = _ttol(argv[1]);
if (procId == 0)
{
// Invalid id
return 2;
}
// Ignore the CTRL+C event we send to ourselves.
SetConsoleCtrlHandler(NULL, TRUE);
// Detach from current console (can only be attached to one at a time).
// Ignore error -- it just means we weren't already attached.
FreeConsole();
// Attach to console of given proc id
if (!AttachConsole(procId))
{
auto error = GetLastError();
if (error == ERROR_GEN_FAILURE)
{
// Process does not exist
return 3;
}
else if (error == ERROR_INVALID_HANDLE)
{
// Process does not have a console.
return 4;
}
return 5;
}
// Send CTRL+C to target process (and outselves).
if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
{
return 6;
}
// If the process is cmd.exe (and is therefore probably a batch file)
// attempt to dismiss the "Terminate batch job?" prompt.
if (IsCmdExe(procId))
{
TerminateBatchFile();
}
return 0;
}
// Attempt to dismiss a "Terminate batch job?" prompt.
void TerminateBatchFile()
{
INPUT_RECORD input[2];
ZeroMemory(input, sizeof(input));
// Y key.
input[0].EventType = KEY_EVENT;
input[0].Event.KeyEvent.bKeyDown = TRUE;
input[0].Event.KeyEvent.uChar.UnicodeChar = L'y';
input[0].Event.KeyEvent.wRepeatCount = 1;
// Enter
input[1].EventType = KEY_EVENT;
input[1].Event.KeyEvent.bKeyDown = TRUE;
input[1].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
input[1].Event.KeyEvent.wRepeatCount = 1;
// Slight hack: wait a bit for the prompt to appear.
Sleep(1);
DWORD numWritten;
auto stdInHandle = GetStdHandle(STD_INPUT_HANDLE);
WriteConsoleInput(stdInHandle, input, 2, &numWritten);
}
// Is the process with the given ID an instance of cmd.exe (and is therefore probably a batch file)?
bool IsCmdExe(DWORD procId)
{
// Open the process.
ScopedHandle process(OpenProcess(
PROCESS_QUERY_INFORMATION,
FALSE, // bInheritHandle
procId));
if (!process.IsValid())
{
return false;
}
TCHAR filename[MAX_PATH];
DWORD size = MAX_PATH;
if (!QueryFullProcessImageName(process, 0, filename, &size))
{
return false;
}
// Convert to lowercase.
_tcslwr_s(filename);
// Does the filename include "cmd.exe"?
return _tcsstr(filename, TEXT("cmd.exe")) != NULL;
}