/
WinProcess.class.st
333 lines (271 loc) · 10.6 KB
/
WinProcess.class.st
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
"
Instances of this class represent native windows OS processes
"
Class {
#name : 'WinProcess',
#superclass : 'WinHandle',
#pools : [
'WinBaseConstants',
'WinTypes'
],
#category : 'OS-Windows-Environment-Processing',
#package : 'OS-Windows-Environment',
#tag : 'Processing'
}
{ #category : 'accessing' }
WinProcess class >> allProcessIdentifiers [
"Return a list of all process identifiers"
<script: 'self allProcessIdentifiers inspect'>
|processIds bytesReturned ids sizeOfDWORD cProcesses|
ids := OrderedCollection new.
sizeOfDWORD := (FFIExternalType sizeOf: DWORD).
processIds := ExternalAddress allocate: 2024 * sizeOfDWORD.
bytesReturned := ExternalAddress allocate: (FFIExternalType sizeOf: DWORD).
(self enumProcesses: processIds size: 2024 bytesReturned: bytesReturned)
ifTrue: [
cProcesses := (bytesReturned unsignedLongAt: 1) / sizeOfDWORD.
0 to: cProcesses do: [:index |
ids add: (processIds unsignedLongAt: index * sizeOfDWORD + 1)
]
].
^ids sorted
]
{ #category : 'process creation - console' }
WinProcess class >> createAndWaitForHeadlessConsoleProcess: szExe [
"
self createAndWaitForHeadlessConsoleProcess: 'cmd.exe /c TIMEOUT /T 5'
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe startupInfo: si processInformation: pi creationFlags: CREATE_NO_WINDOW.
self waitForSingleObject: pi hProcess timeout: INFINITE.
pi hProcess closeHandle.
pi hThread closeHandle
]
{ #category : 'process creation' }
WinProcess class >> createAndWaitForProcess: szExe [
"
self createAndWaitForProcess: 'cmd.exe'
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe startupInfo: si processInformation: pi.
self waitForSingleObject: pi hProcess timeout: INFINITE.
pi hProcess closeHandle.
pi hThread closeHandle
]
{ #category : 'process creation' }
WinProcess class >> createAndWaitForProcess: szExe withCurrentDirectory: szCurrentDirectory [
"
self createAndWaitForProcess: 'cmd.exe' withCurrentDirectory: FileSystem disk workingDirectory fullName
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe currentDirectory: szCurrentDirectory startupInfo: si processInformation: pi.
self waitForSingleObject: pi hProcess timeout: INFINITE.
pi hProcess closeHandle.
pi hThread closeHandle
]
{ #category : 'process creation - console' }
WinProcess class >> createHeadlessConsoleProcess: szExe [
"
Create a console process without any window.
self createHeadlessConsoleProcess: 'cmd.exe /c dir > directory.txt'.
FileSystem workingDirectory openInOSFileBrowser
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe startupInfo: si processInformation: pi creationFlags: CREATE_NO_WINDOW.
^pi
]
{ #category : 'process creation' }
WinProcess class >> createHeadlessProcess: szExe [
"
self createHeadlessProcess: 'cmd.exe'
"
| si pi |
si := WinStartupInfo new.
si dwFlags: STARTF_USESHOWWINDOW.
pi := WinProcessInformation new.
self createProcess: szExe startupInfo: si processInformation: pi.
^pi
]
{ #category : 'process creation' }
WinProcess class >> createProcess: szExe [
"
self createProcess: 'cmd.exe'
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe startupInfo: si processInformation: pi.
^pi
]
{ #category : 'primitives' }
WinProcess class >> createProcess: szExe currentDirectory: szCurrentDirectory startupInfo: lpStartupInfo processInformation: pi [
"Retrieves the contents of the STARTUPINFO structure that was specified when the calling process was created."
^self ffiCall: #(BOOL CreateProcessA(0, LPCTSTR szExe, 0, 0, false, 0, 0, LPCTSTR szCurrentDirectory, WinStartupInfo* lpStartupInfo, WinProcessInformation* pi)) module: #kernel32
]
{ #category : 'primitives' }
WinProcess class >> createProcess: szExe startupInfo: lpStartupInfo processInformation: pi [
"Retrieves the contents of the STARTUPINFO structure that was specified when the calling process was created."
^ self ffiCall: #(BOOL CreateProcessA(int 0, LPCTSTR szExe, int 0, int 0, bool false, int 0, int 0, int 0, WinStartupInfo* lpStartupInfo, WinProcessInformation* pi)) module: #kernel32
]
{ #category : 'primitives' }
WinProcess class >> createProcess: szExe startupInfo: lpStartupInfo processInformation: pi creationFlags: dwCreationFlags [
"Retrieves the contents of the STARTUPINFO structure that was specified when the calling process was created."
^ self ffiCall: #(BOOL CreateProcessA(0, LPCTSTR szExe, 0, 0, false, DWORD dwCreationFlags, 0, 0, WinStartupInfo* lpStartupInfo, WinProcessInformation* pi)) module: #kernel32
]
{ #category : 'process creation' }
WinProcess class >> createProcess: szExe withCurrentDirectory: szCurrentDirectory [
"
self createProcess: 'cmd.exe' withCurrentDirectory: FileSystem disk workingDirectory fullName
"
| si pi |
si := WinStartupInfo new.
pi := WinProcessInformation new.
self createProcess: szExe currentDirectory: szCurrentDirectory startupInfo: si processInformation: pi.
^pi
]
{ #category : 'accessing' }
WinProcess class >> currentProcess [
"Return an instance with a pseudohandle for the current process."
<script: 'self currentProcess inspect'>
^ self ffiCall: #(WinProcess GetCurrentProcess()) module: #kernel32
]
{ #category : 'accessing' }
WinProcess class >> currentProcessId [
"Returns the process identifier (PID) of the calling process."
^self ffiCall: #( DWORD GetCurrentProcessId()) module: #kernel32
]
{ #category : 'primitives' }
WinProcess class >> enumProcesses: pProcessIds size: cb bytesReturned: pBytesReturned [
"Retrieves the process identifier for each process object in the system.."
^ self ffiCall: #(BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned)) module: #Psapi
]
{ #category : 'private' }
WinProcess class >> fgetc: stream [
"Initiate pipe streams to or from a process "
^self ffiCall: #(int* fgetc(ExternalAddress* stream))
module: LibC
]
{ #category : 'accessing' }
WinProcess class >> getProcessId: hProcess [
"Return the PID of the process represented by the given process handle."
^ self ffiCall: #(DWORD GetProcessId(HANDLE hProcess)) module: #kernel32
]
{ #category : 'primitives' }
WinProcess class >> getStartupInfo: info [
"Retrieves the contents of the STARTUPINFO structure that was specified when the calling process was created."
^ self ffiCall: #(VOID GetStartupInfoA(WinStartupInfo* info)) module: #kernel32
]
{ #category : 'accessing module' }
WinProcess class >> getVMModuleHandle [
"The GetModuleHandle function retrieves a module handle for the specified module if the file has been mapped into the address space of the calling process."
^ self ffiCall: #( HMODULE GetModuleHandleA (int 0)) module: #Kernel32
]
{ #category : 'private' }
WinProcess class >> pipe: command mode: mode [
"Initiate pipe streams to or from a process "
^self ffiCall: #(ExternalAddress* _popen(char* command, char* mode))
module: LibC
]
{ #category : 'private' }
WinProcess class >> pipeClose: stream [
"Close pipe"
^self ffiCall: #(int* _pclose(ExternalAddress* stream))
module: LibC
]
{ #category : 'piping' }
WinProcess class >> resultOfCommand: cmd [
|file last s |
file := self pipe: cmd mode: 'r'.
s := String new writeStream.
[ last := (self fgetc: file) value.
last = 16rFFFFFFFF ] whileFalse: [
s nextPut: (Character value: last)
].
self pipeClose: file.
^s contents
]
{ #category : 'accessing' }
WinProcess class >> startupInfo [
"Returns the STARTUPINFO of the current process"
<script: 'self startupInfo inspect'>
|info|
info := WinStartupInfo new.
self getStartupInfo: info.
^info
]
{ #category : 'primitives' }
WinProcess class >> waitForSingleObject: hHandle timeout: dwMilliseconds [
"Waits until the specified object is in the signaled state or the time-out interval elapses."
^self ffiCall: #(DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)) module: #kernel32
]
{ #category : 'accessing' }
WinProcess >> getExitCode [
| buffer |
buffer := ByteArray new: 4.
self getExitCodeProcess: buffer.
^ buffer unsignedLongAt: 1
]
{ #category : 'private - primitives' }
WinProcess >> getExitCodeProcess: lpExitCode [
"Retrieves the termination status of the specified process."
^ self
ffiCall: #(BOOL GetExitCodeProcess #(HANDLE self , LPDWORD lpExitCode))
module: #kernel32
]
{ #category : 'accessing' }
WinProcess >> getPriorityClass [
"Return the priority class for the specified process"
^self ffiCall: #(DWORD GetPriorityClass(HANDLE self)) module: #kernel32
]
{ #category : 'testing' }
WinProcess >> isHighPriorityClass [
"Indicates a process that performs time-critical tasks that must be executed immediately for it to run correctly."
^self getPriorityClass == HIGH_PRIORITY_CLASS
]
{ #category : 'testing' }
WinProcess >> isIdlePriorityClass [
"Indicates a process whose threads run only when the system is idle and are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle priority class is inherited by child processes."
^self getPriorityClass == IDLE_PRIORITY_CLASS
]
{ #category : 'testing' }
WinProcess >> isNormalPriorityClass [
"Indicates a normal process with no special scheduling needs"
^self getPriorityClass == NORMAL_PRIORITY_CLASS
]
{ #category : 'testing' }
WinProcess >> isRealtimePriorityClass [
"Indicates a process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks."
^self getPriorityClass == REALTIME_PRIORITY_CLASS
]
{ #category : 'testing' }
WinProcess >> isRunning [
"A return value of STILL_ACTIVE (259) could be interpreted to mean that the thread is
is still running."
^ self getExitCode = STILL_ACTIVE
]
{ #category : 'testing' }
WinProcess >> isWow64Process [
"Determines whether the specified process is running under WOW64 (the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows)."
|result|
result := ExternalAddress allocate: 4.
self isWow64Process: result.
^(result value) ~= 0
]
{ #category : 'private - primitives' }
WinProcess >> isWow64Process: wow64Process [
"Determines whether the specified process is running under WOW64."
^self ffiCall: #(BOOL IsWow64Process(HANDLE self, ExternalAddress wow64Process)) module: #kernel32
]
{ #category : 'lifetime' }
WinProcess >> terminateProcessWithExitCode: uExitCode [
"Return the priority class for the specified process"
^self ffiCall: #(BOOL TerminateProcess(HANDLE self, UINT uExitCode)) module: #kernel32
]