-
Notifications
You must be signed in to change notification settings - Fork 3
/
ReadOutput.ctl
executable file
·401 lines (345 loc) · 15.7 KB
/
ReadOutput.ctl
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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
VERSION 5.00
Begin VB.UserControl ReadOutput
BackColor = &H80000007&
ClientHeight = 765
ClientLeft = 0
ClientTop = 0
ClientWidth = 975
InvisibleAtRuntime= -1 'True
ScaleHeight = 765
ScaleWidth = 975
Begin VB.Image imgIcon
Height = 735
Left = 0
Picture = "ReadOutput.ctx":0000
Stretch = -1 'True
Top = 0
Width = 975
End
End
Attribute VB_Name = "ReadOutput"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'VERSION 2.0 OF READ OUTPUT [CONTROL]
'You may use this code in your project as long as you dont claim its yours ;)
'This program reads the output of CLI (Command Line Interface) Applications.
'Examples of CLI Applications are:
' -PING.EXE
' -NETSTAT
' -TRACERT
'This program will grab the output and call events so that you can process the commands.
'NOTE: I got about 50% of this code from some site about 2 years ago, just found it and fixed some bugs
' and transformed it into a user-friendly control.
'
'Please vote if you like, complaint about bugs if you find any, but I also appreciate comments ;)
'Thanks again
'-Endra
'ADDONS:
' -The Terminate Process ID was originaly coded by Nick Campbeln. Thanks a bunch ;)
'-His code is surrounded by POUND (##) signs.
Option Explicit 'force declarations of variables
'#####################################################################################
'#####################################################################################
'#####################################################################################
'#### Functions/Consts used for CloseProcess()
'Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Sub CloseHandle Lib "kernel32" (ByVal hPass As Long)
Private Const WM_CLOSE As Long = &H10
Private Const WM_DESTROY As Long = &H2
'Private Const WM_QUERYENDSESSION = &H11
Private Const WM_ENDSESSION = &H16
Private Const PROCESS_TERMINATE As Long = &H1
'#### Functions/Consts/Types used for GetVersionEx() API
Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long
Private Type OSVERSIONINFO
OSVSize As Long
dwVerMajor As Long
dwVerMinor As Long
dwBuildNumber As Long '#### NT: Build Number, 9x: High-Order has Major/Minor ver, Low-Order has build
PlatformID As Long
szCSDVersion As String * 128 '#### NT: ie- "Service Pack 3", 9x: 'arbitrary additional information'
End Type
'Private Const VER_PLATFORM_WIN32s = 0
Private Const VER_PLATFORM_WIN32_WINDOWS = 1
Private Const VER_PLATFORM_WIN32_NT = 2
'#### Functions/Consts used for CloseAll()
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Const GW_HWNDNEXT = 2
Private Const GW_CHILD = 5
'#### Required local vars
Private g_bIsInit As Boolean
Private g_bIs9x As Boolean
'#####################################################################################
'#####################################################################################
'#####################################################################################
'private variables
Dim sCommand As String
Dim bProcessing As Boolean
Dim bCancelProcess As Boolean
'Public Events
Public Event Error(ByVal Error As String, LastDLLError As Long) 'Errors go here
Public Event GotChunk(ByVal sChunk As String, ByVal LastChunk As Boolean) 'Chunk Output detected, launch this event
Public Event Complete() 'Raise event when its done reading output
Public Event Starting() 'Raised when you can start the reading
Public Event Canceled() 'Raised when you canceled.
'The following are all API Calls and Types.
'You could probably find more information on them if you google them so I wont comment them at all.
Private Declare Function CreatePipe Lib "kernel32" ( _
phReadPipe As Long, _
phWritePipe As Long, _
lpPipeAttributes As Any, _
ByVal nSize As Long) As Long
Private Declare Function ReadFile Lib "kernel32" ( _
ByVal hFile As Long, _
ByVal lpBuffer As String, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
ByVal lpOverlapped As Any) As Long
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadID As Long
End Type
Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, _
lpProcessAttributes As Any, lpThreadAttributes As Any, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As Any, lpProcessInformation As Any) As Long
Private Declare Function CloseHandle2 Lib "kernel32" (ByVal hObject As Long) As Long
'The following are simply constants that dont need changing during the program.
'DO NOT EDIT THESE!
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const STARTF_USESTDHANDLES = &H100&
Private Const STARTF_USESHOWWINDOW = &H1
Private Const SW_HIDE = 0
Private Sub UserControl_Initialize()
On Error Resume Next
'doesnt error out of stack space
UserControl.Height = imgIcon.Height
UserControl.Width = imgIcon.Width
bProcessing = False
bCancelProcess = False
End Sub
Private Sub UserControl_Resize()
On Error Resume Next
'doesnt error out of stack space
UserControl.Height = imgIcon.Height
UserControl.Width = imgIcon.Width
End Sub
'The following function executes the command line and returns the output via events
Private Function ExecuteApp(sCmdline As String) As String
Dim proc As PROCESS_INFORMATION, ret As Long
Dim start As STARTUPINFO
Dim sa As SECURITY_ATTRIBUTES
Dim hReadPipe As Long 'The handle used to read from the pipe.
Dim hWritePipe As Long 'The pipe where StdOutput and StdErr will be redirected to.
Dim sOutput As String
Dim lngBytesRead As Long, sBuffer As String * 256
bProcessing = True
sa.nLength = Len(sa)
sa.bInheritHandle = True
ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
If ret = 0 Then
bProcessing = False
RaiseEvent Error("CreatePipe failed.", Err.LastDLLError)
Exit Function
End If
start.cb = Len(start)
start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
' Redirect the standard output and standard error to the same pipe
start.hStdOutput = hWritePipe
start.hStdError = hWritePipe
start.wShowWindow = SW_HIDE
' Start the shelled application:
' if you program has to work only on NT you don't need the "conspawn "
'ret = CreateProcessA(0&, "conspawn " & sCmdline, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
ret = CreateProcessA(0&, Environ("ComSpec") & " /c " & sCmdline, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
If ret = 0 Then
bProcessing = False
RaiseEvent Error("CreateProcess failed.", Err.LastDLLError)
Exit Function
End If
' The handle wWritePipe has been inherited by the shelled application
' so we can close it now
CloseHandle hWritePipe
' Read the characters that the shelled application
' has outputed 256 characters at a time
RaiseEvent Starting
bCancelProcess = False
Do
DoEvents
If bCancelProcess = True Then
Exit Do
End If
ret = ReadFile(hReadPipe, sBuffer, 256, lngBytesRead, 0&)
sOutput = Left$(sBuffer, lngBytesRead)
If ret = 0 Then
RaiseEvent GotChunk(sOutput, True) 'no more chunks to read
RaiseEvent Complete
Exit Do
Else
RaiseEvent GotChunk(sOutput, False) 'more chunks to read.
End If
Loop While ret <> 0 ' if ret = 0 then there is no more characters to read
If bCancelProcess = True Then
If CloseProcess(proc.dwProcessId) = True Then
RaiseEvent Canceled
Else
RaiseEvent Error("Cannot close process id: " & proc.dwProcessId, 1203)
End If
End If
CloseHandle proc.hProcess
CloseHandle proc.hThread
CloseHandle hReadPipe
bProcessing = False
bCancelProcess = False
End Function
Public Property Let SetCommand(ByVal sCommandVal As String)
sCommand = sCommandVal
End Property
Public Property Get SetCommand() As String
SetCommand = sCommand
End Property
Public Sub CancelProcess()
If bProcessing = True Then
bCancelProcess = True
Else
RaiseEvent Error("Not currently processing a command!", 1202)
End If
End Sub
Public Sub ProcessCommand()
If Len(sCommand) = 0 Then
RaiseEvent Error("Invalid Command.", 1200)
Exit Sub
End If
If bProcessing = True Then
RaiseEvent Error("Currently processing a command!", 1201)
Exit Sub
End If
ExecuteApp """" & sCommand & """"
End Sub
'#####################################################################################
'#####################################################################################
'#####################################################################################
'#####################################################################################
'# Public Functions
'#####################################################################################
'#########################################################
'# Ends a process according to the passed eMode
'#########################################################
Public Function CloseProcess(ByVal lProcessID As Long, Optional ByVal uExitCode As Long = 0) As Boolean
Dim lTemp As Long
'#### If we have not yet been initilized, call InitCloseProcess()
If (Not g_bIsInit) Then Call InitCloseProcess
'#### If we're running under Win95 or Win98 (WinME seems to process the other method correctly)
If (g_bIs9x) Then
'#### If we successfully send the 'Windows is closing' message to the lProcessID
If (CloseAll(lProcessID, WM_ENDSESSION, True)) Then
'#### Since the window has accepted the 'Windows is closing' message, we can now safely terminate the process
'#### Collect a process handle in lTemp for lProcessID
lTemp = OpenProcess(PROCESS_TERMINATE, False, lProcessID)
'#### If lTemp is invalid, return false
If (lTemp = 0) Then
CloseProcess = False
'#### Else the collected process handle is valid
Else
'#### TerminateProcess() returns non-zero (true) on success and zero (false) on failure
CloseProcess = CBool(TerminateProcess(lTemp, uExitCode))
'#### Close the open lTemp
Call CloseHandle2(lTemp)
End If
'#### Else we could not communicate with the process
Else
CloseProcess = False
End If
'#### Else we're under a system that correctly handles the WM_CLOSE message
Else
CloseProcess = CloseAll(lProcessID, WM_CLOSE)
End If
End Function
'#####################################################################################
'# Private Functions
'#####################################################################################
'#########################################################
'# Initilizes the module variables
'#########################################################
Private Sub InitCloseProcess()
Dim uOSInfo As OSVERSIONINFO
'#### Setup the uOSInfo UDT to determine the value of g_bIsNT4
With uOSInfo
.OSVSize = Len(uOSInfo)
.szCSDVersion = Space(128)
'#### Get the OS info, setting g_bIs9x accordingly
Call GetVersionEx(uOSInfo)
g_bIs9x = (.PlatformID = VER_PLATFORM_WIN32_WINDOWS) And (.dwVerMajor > 4) Or (.dwVerMajor = 4 And .dwVerMinor > 0) Or _
(.PlatformID = VER_PLATFORM_WIN32_WINDOWS And .dwVerMajor = 4 And .dwVerMinor = 0) 'Or _
'!' WinME (.PlatformID = VER_PLATFORM_WIN32_WINDOWS And .dwVerMajor = 4 And .dwVerMinor = 90)
End With
'#### Set g_bIsInit to true
g_bIsInit = True
End Sub
'#########################################################
'# Posts the eMessage to all of the windows with the matching lProcessID
'#########################################################
Private Function CloseAll(ByVal lProcessID As Long, Optional ByVal eMessage As Long = WM_CLOSE, Optional ByVal wParam As Long = 0) As Boolean
Dim hWndChild As Long
Dim lThreadProcessID As Long
'#### Get the Desktop handle while getting the first child under the Desktop and default the return value
hWndChild = GetWindow(GetDesktopWindow(), GW_CHILD)
CloseAll = False
'#### While we still have hWndChild(en) to look at
Do While (hWndChild <> 0)
'#### If this is a parent window
If (GetParent(hWndChild) = 0) Then
'#### Get the lThreadProcessID of the window
Call GetWindowThreadProcessId(hWndChild, lThreadProcessID)
'#### If we have a match with the ProcessIDs
If (lProcessID = lThreadProcessID) Then
'#### Post the message to the process and set the return value to true
Call PostMessage(hWndChild, eMessage, wParam, 0&)
CloseAll = True
End If
End If
'#### Move onto the next hWndChild
hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)
Loop
End Function
'#####################################################################################
'#####################################################################################
'#####################################################################################
'#####################################################################################