mongrel_service 0.1 → 0.4.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +52 -0
- data/LICENSE.txt +55 -0
- data/Manifest.txt +32 -0
- data/README.txt +20 -0
- data/Rakefile +8 -42
- data/TODO.txt +19 -0
- data/lib/mongrel_service/init.rb +88 -107
- data/lib/mongrel_service/service_manager.rb +74 -0
- data/resources/mongrel_service.exe +0 -0
- data/src/ServiceFB/ServiceFB.bas +651 -0
- data/src/ServiceFB/ServiceFB.bi +109 -0
- data/src/ServiceFB/ServiceFB_Utils.bas +495 -0
- data/src/ServiceFB/ServiceFB_Utils.bi +70 -0
- data/src/ServiceFB/_internals.bi +50 -0
- data/src/ServiceFB/_utils_internals.bi +51 -0
- data/src/mongrel_service/_debug.bi +59 -0
- data/src/mongrel_service/boolean.bi +18 -0
- data/src/mongrel_service/console_process.bas +397 -0
- data/src/mongrel_service/console_process.bi +75 -0
- data/src/mongrel_service/mongrel_service.bas +198 -0
- data/src/mongrel_service/mongrel_service.bi +61 -0
- data/tasks/gem.rake +17 -0
- data/tasks/native_lib.rake +40 -0
- data/tasks/native_service.rake +42 -0
- data/tasks/tests.rake +55 -0
- data/tests/all_tests.bas +18 -0
- data/tests/fixtures/mock_process.bas +92 -0
- data/tests/test_console_process.bas +402 -0
- data/tests/test_helpers.bas +35 -0
- data/tests/test_helpers.bi +8 -0
- data/tools/freebasic.rb +358 -0
- metadata +102 -49
- data/COPYING +0 -20
- data/LICENSE +0 -20
- data/README +0 -13
- data/bin/mongrel_service +0 -153
- data/tools/rakehelp.rb +0 -106
@@ -0,0 +1,70 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#if __FB_VERSION__ < "0.17"
|
9
|
+
#error ServiceFB is designed to compile with FreeBASIC version "0.17"
|
10
|
+
#else
|
11
|
+
|
12
|
+
#ifndef __FB_WIN32__
|
13
|
+
#error Platform unsupported. Compiling ServiceFB requires Windows platform.
|
14
|
+
#else
|
15
|
+
|
16
|
+
#ifndef __ServiceFB_Utils_bi__
|
17
|
+
#define __ServiceFB_Utils_bi__
|
18
|
+
|
19
|
+
#include once "win/psapi.bi"
|
20
|
+
#include once "win/tlhelp32.bi"
|
21
|
+
|
22
|
+
namespace fb
|
23
|
+
namespace svc
|
24
|
+
namespace utils '# fb.svc.utils
|
25
|
+
'# use this to determine (using select case maybe?) the
|
26
|
+
'# mode which the service was invoked.
|
27
|
+
enum ServiceRunMode
|
28
|
+
RunAsUnknown = 0
|
29
|
+
RunAsService
|
30
|
+
RunAsManager
|
31
|
+
RunAsConsole
|
32
|
+
end enum
|
33
|
+
|
34
|
+
|
35
|
+
'# ServiceController type (object)
|
36
|
+
'# this is a helper object in case you want to implement
|
37
|
+
'# console mode (command line testing/debugging) and management (install/remove/control)
|
38
|
+
'# to your services, all from the same executable
|
39
|
+
type ServiceController
|
40
|
+
'# ctor/dtor()
|
41
|
+
declare constructor()
|
42
|
+
declare constructor(byref as string)
|
43
|
+
declare constructor(byref as string, byref as string)
|
44
|
+
declare constructor(byref as string, byref as string, byref as string)
|
45
|
+
declare destructor()
|
46
|
+
|
47
|
+
'# methods (public)
|
48
|
+
declare sub Banner()
|
49
|
+
declare function RunMode() as ServiceRunMode
|
50
|
+
declare sub Manage()
|
51
|
+
declare sub Manage(byref as ServiceProcess)
|
52
|
+
declare sub Console()
|
53
|
+
declare sub Console(byref as ServiceProcess)
|
54
|
+
|
55
|
+
'# properties (public)
|
56
|
+
'# use these properties for shwoing information on console/manager mode
|
57
|
+
'# as banner.
|
58
|
+
'# Product, version
|
59
|
+
'# copyright
|
60
|
+
product as string
|
61
|
+
version as string
|
62
|
+
copyright as string
|
63
|
+
end type
|
64
|
+
end namespace '# fb.svc.utils
|
65
|
+
end namespace '# fb.svc
|
66
|
+
end namespace '# fb
|
67
|
+
|
68
|
+
#endif '# __ServiceFB_bi__
|
69
|
+
#endif '# __FB_WIN32__
|
70
|
+
#endif '# __FB_VERSION__
|
@@ -0,0 +1,50 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
'##################################################################
|
9
|
+
'#
|
10
|
+
'# DO NOT INCLUDE THIS FILE DIRECTLY!
|
11
|
+
'# it is used internaly by ServiceFB
|
12
|
+
'# use ServiceFB.bi instead
|
13
|
+
'#
|
14
|
+
'##################################################################
|
15
|
+
|
16
|
+
namespace fb
|
17
|
+
namespace svc
|
18
|
+
'# now due references locking, I needed a constructor and destructor for
|
19
|
+
'# the namespace to garantee everything is cleaned up on termination of the process
|
20
|
+
declare sub _initialize() constructor
|
21
|
+
declare sub _terminate() destructor
|
22
|
+
|
23
|
+
'# global service procedures (private)
|
24
|
+
declare sub _main(byval as DWORD, byval as LPSTR ptr)
|
25
|
+
declare function _control_ex(byval as DWORD, byval as DWORD, byval as LPVOID, byval as LPVOID) as DWORD
|
26
|
+
declare sub _run()
|
27
|
+
|
28
|
+
'# global references helper
|
29
|
+
declare function _add_to_references(byref as ServiceProcess) as integer
|
30
|
+
declare function _find_in_references(byref as string) as ServiceProcess ptr
|
31
|
+
|
32
|
+
'# command line builder (helper)
|
33
|
+
'# this is used to gather information about:
|
34
|
+
'# mode (if present)
|
35
|
+
'# valid service name (after lookup in the table)
|
36
|
+
'# command line to be passed to service
|
37
|
+
declare sub _build_commandline(byref as string, byref as string, byref as string)
|
38
|
+
|
39
|
+
'# I started this as simple, unique service served from one process
|
40
|
+
'# but the idea of share the same process space (and reduce resources use) was good.
|
41
|
+
'# to do that, I needed a references table (similar to service_table, but we will
|
42
|
+
'# hold the ServiceProcess registered by ServiceHost (the multi services host).
|
43
|
+
'# also, I needed a locking mechanism to avoid problems of two calls changing the table
|
44
|
+
'# at the same time.
|
45
|
+
extern _svc_references as ServiceProcess ptr ptr
|
46
|
+
extern _svc_references_count as integer
|
47
|
+
extern _svc_references_lock as any ptr
|
48
|
+
end namespace '# fb.svc
|
49
|
+
end namespace '# fb
|
50
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
'##################################################################
|
9
|
+
'#
|
10
|
+
'# DO NOT INCLUDE THIS FILE DIRECTLY!
|
11
|
+
'# it is used internaly by ServiceFB
|
12
|
+
'# use ServiceFB_Utils.bi instead
|
13
|
+
'#
|
14
|
+
'##################################################################
|
15
|
+
|
16
|
+
namespace fb
|
17
|
+
namespace svc
|
18
|
+
namespace utils '# fb.svc.utils
|
19
|
+
'# console_handler is used to get feedback form keyboard and allow
|
20
|
+
'# shutdown of service using Ctrl+C / Ctrl+Break from keyboard
|
21
|
+
declare function _console_handler(byval as DWORD) as BOOL
|
22
|
+
|
23
|
+
'# helper private subs used to list the services and their descriptions
|
24
|
+
'# in _svc_references
|
25
|
+
declare sub _list_references()
|
26
|
+
|
27
|
+
'# internals functions used to get Parent PID and Process Name
|
28
|
+
'# using this we automatically determine if the service was started by SCM
|
29
|
+
'# or by the user, from commandline or from explorer
|
30
|
+
declare function _parent_pid(byval as uinteger) as uinteger
|
31
|
+
declare function _process_name(byval as uinteger) as string
|
32
|
+
declare function _process_name_dyn_psapi(byval as uinteger) as string
|
33
|
+
declare function _show_error() as string
|
34
|
+
|
35
|
+
'# InStrRev (authored by ikkejw @ freebasic forums)
|
36
|
+
'# http://www.freebasic.net/forum/viewtopic.php?p=49315#49315
|
37
|
+
declare function InStrRev(byval as uinteger = 0, byref as string, byref as string) as uinteger
|
38
|
+
|
39
|
+
'# use a signal (condition) in the console mode to know
|
40
|
+
'# when the service should be stopped.
|
41
|
+
'# the Console() main loop will wait for it, and the console_handler
|
42
|
+
'# will raise in case Ctrl+C / Ctrl+Break or other events are
|
43
|
+
'# received.
|
44
|
+
extern _svc_stop_signal as any ptr
|
45
|
+
extern _svc_stop_mutex as any ptr
|
46
|
+
extern _svc_stopped as BOOL
|
47
|
+
extern _svc_in_console as ServiceProcess ptr
|
48
|
+
extern _svc_in_console_stop_flag as BOOL
|
49
|
+
end namespace '# fb.svc.utils
|
50
|
+
end namespace '# fb.svc
|
51
|
+
end namespace '# fb
|
@@ -0,0 +1,59 @@
|
|
1
|
+
'##################################################################
|
2
|
+
'#
|
3
|
+
'# mongrel_service: Win32 native implementation for mongrel
|
4
|
+
'# (using ServiceFB and FreeBASIC)
|
5
|
+
'#
|
6
|
+
'# Copyright (c) 2006 Multimedia systems
|
7
|
+
'# (c) and code by Luis Lavena
|
8
|
+
'#
|
9
|
+
'# mongrel_service (native) and mongrel_service gem_pluing are licensed
|
10
|
+
'# in the same terms as mongrel, please review the mongrel license at
|
11
|
+
'# http://mongrel.rubyforge.org/license.html
|
12
|
+
'#
|
13
|
+
'##################################################################
|
14
|
+
|
15
|
+
'##################################################################
|
16
|
+
'# Requirements:
|
17
|
+
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
18
|
+
'#
|
19
|
+
'##################################################################
|
20
|
+
|
21
|
+
#ifndef __Debug_bi__
|
22
|
+
#define __Debug_bi__
|
23
|
+
|
24
|
+
#ifdef DEBUG_LOG
|
25
|
+
#include once "vbcompat.bi"
|
26
|
+
#ifndef DEBUG_LOG_FILE
|
27
|
+
#define DEBUG_LOG_FILE EXEPATH + "\debug.log"
|
28
|
+
#endif
|
29
|
+
|
30
|
+
'# this procedure is only used for debugging purposed, will be removed from
|
31
|
+
'# final compilation
|
32
|
+
private sub debug_to_file(byref message as string, byref file as string, byval linenumber as uinteger, byref func as string)
|
33
|
+
dim handle as integer
|
34
|
+
static first_time as integer
|
35
|
+
|
36
|
+
handle = freefile
|
37
|
+
open DEBUG_LOG_FILE for append as #handle
|
38
|
+
|
39
|
+
if (first_time = 0) then
|
40
|
+
print #handle, "# Logfile created on "; format(now(), "dd/mm/yyyy HH:mm:ss")
|
41
|
+
print #handle, ""
|
42
|
+
first_time = 1
|
43
|
+
end if
|
44
|
+
|
45
|
+
'# src/module.bas:123, namespace.function:
|
46
|
+
'# message
|
47
|
+
'#
|
48
|
+
print #handle, file; ":"; str(linenumber); ", "; lcase(func); ":"
|
49
|
+
print #handle, space(2); message
|
50
|
+
print #handle, ""
|
51
|
+
|
52
|
+
close #handle
|
53
|
+
end sub
|
54
|
+
#define debug(message) debug_to_file(message, __FILE__, __LINE__, __FUNCTION__)
|
55
|
+
#else
|
56
|
+
#define debug(message)
|
57
|
+
#endif '# DEBUG_LOG
|
58
|
+
|
59
|
+
#endif '# __Debug_bi__
|
@@ -0,0 +1,18 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#ifndef __BOOLEAN_BI__
|
9
|
+
#define __BOOLEAN_BI__
|
10
|
+
|
11
|
+
#undef BOOLEAN
|
12
|
+
type BOOLEAN as byte
|
13
|
+
#undef FALSE
|
14
|
+
const FALSE as byte = 0
|
15
|
+
#undef TRUE
|
16
|
+
const TRUE as byte = not FALSE
|
17
|
+
|
18
|
+
#endif ' __BOOLEAN_BI__
|
@@ -0,0 +1,397 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#include once "console_process.bi"
|
9
|
+
|
10
|
+
constructor ConsoleProcess(byref new_filename as string = "", byref new_arguments as string = "")
|
11
|
+
'# assign filename and arguments
|
12
|
+
|
13
|
+
'# if filename contains spaces, automatically quote it!
|
14
|
+
if (instr(new_filename, " ") > 0) then
|
15
|
+
_filename = !"\"" + new_filename + !"\""
|
16
|
+
else
|
17
|
+
_filename = new_filename
|
18
|
+
endif
|
19
|
+
|
20
|
+
_arguments = new_arguments
|
21
|
+
end constructor
|
22
|
+
|
23
|
+
destructor ConsoleProcess()
|
24
|
+
'# in case process still running
|
25
|
+
if (running = true) then
|
26
|
+
terminate(true)
|
27
|
+
|
28
|
+
'# close opened handles
|
29
|
+
'# ...
|
30
|
+
CloseHandle(_process_info.hProcess)
|
31
|
+
CloseHandle(_process_info.hThread)
|
32
|
+
end if
|
33
|
+
end destructor
|
34
|
+
|
35
|
+
property ConsoleProcess.filename() as string
|
36
|
+
return _filename
|
37
|
+
end property
|
38
|
+
|
39
|
+
property ConsoleProcess.filename(byref rhs as string)
|
40
|
+
if not (running = true) then
|
41
|
+
_filename = rhs
|
42
|
+
end if
|
43
|
+
end property
|
44
|
+
|
45
|
+
property ConsoleProcess.arguments() as string
|
46
|
+
return _arguments
|
47
|
+
end property
|
48
|
+
|
49
|
+
property ConsoleProcess.arguments(byref rhs as string)
|
50
|
+
if not (running = true) then
|
51
|
+
_arguments = rhs
|
52
|
+
end if
|
53
|
+
end property
|
54
|
+
|
55
|
+
property ConsoleProcess.redirected_stdout() as string
|
56
|
+
return _stdout_filename
|
57
|
+
end property
|
58
|
+
|
59
|
+
property ConsoleProcess.redirected_stderr() as string
|
60
|
+
return _stderr_filename
|
61
|
+
end property
|
62
|
+
|
63
|
+
'# running is a helper which evaluates _pid and exit_code
|
64
|
+
property ConsoleProcess.running() as boolean
|
65
|
+
dim result as boolean
|
66
|
+
|
67
|
+
'# presume not running
|
68
|
+
result = false
|
69
|
+
|
70
|
+
if not (_pid = 0) then
|
71
|
+
'# that means the process is/was running.
|
72
|
+
'# now evaluate if exit_code = STILL_ACTIVE
|
73
|
+
result = (exit_code = STILL_ACTIVE)
|
74
|
+
end if
|
75
|
+
|
76
|
+
return result
|
77
|
+
end property
|
78
|
+
|
79
|
+
property ConsoleProcess.pid() as uinteger
|
80
|
+
return _pid
|
81
|
+
end property
|
82
|
+
|
83
|
+
property ConsoleProcess.exit_code() as uinteger
|
84
|
+
static previous_code as uinteger
|
85
|
+
dim result as uinteger
|
86
|
+
|
87
|
+
result = 0
|
88
|
+
|
89
|
+
'# is _pid valid?
|
90
|
+
if not (_pid = 0) then
|
91
|
+
if not (_process_info.hProcess = NULL) then
|
92
|
+
'# the process reference is valid, get the exit_code
|
93
|
+
if not (GetExitCodeProcess(_process_info.hProcess, @result) = 0) then
|
94
|
+
previous_code = result
|
95
|
+
'# OK
|
96
|
+
'# no error in the query, get result
|
97
|
+
if not (result = STILL_ACTIVE) then
|
98
|
+
CloseHandle(_process_info.hProcess)
|
99
|
+
_process_info.hProcess = NULL
|
100
|
+
end if '# (result = STILL_ACTIVE)
|
101
|
+
end if '# not (GetExitCodeProcess() = 0)
|
102
|
+
else
|
103
|
+
result = previous_code
|
104
|
+
end if '# not (proc = NULL)
|
105
|
+
end if '# not (_pid = 0)
|
106
|
+
|
107
|
+
return result
|
108
|
+
end property
|
109
|
+
|
110
|
+
function ConsoleProcess.redirect(byval target as ProcessStdEnum, byref new_std_filename as string) as boolean
|
111
|
+
dim result as boolean
|
112
|
+
|
113
|
+
if not (running = true) then
|
114
|
+
select case target
|
115
|
+
case ProcessStdOut:
|
116
|
+
_stdout_filename = new_std_filename
|
117
|
+
result = true
|
118
|
+
|
119
|
+
case ProcessStdErr:
|
120
|
+
_stderr_filename = new_std_filename
|
121
|
+
result = true
|
122
|
+
|
123
|
+
case ProcessStdBoth:
|
124
|
+
_stdout_filename = new_std_filename
|
125
|
+
_stderr_filename = new_std_filename
|
126
|
+
result = true
|
127
|
+
|
128
|
+
end select
|
129
|
+
end if
|
130
|
+
|
131
|
+
return result
|
132
|
+
end function
|
133
|
+
|
134
|
+
function ConsoleProcess.start() as boolean
|
135
|
+
dim result as boolean
|
136
|
+
dim success as boolean
|
137
|
+
|
138
|
+
'# API
|
139
|
+
'# New Process resources
|
140
|
+
dim context as STARTUPINFO
|
141
|
+
dim proc_sa as SECURITY_ATTRIBUTES = type(sizeof(SECURITY_ATTRIBUTES), NULL, TRUE)
|
142
|
+
|
143
|
+
'# StdIn, StdOut, StdErr Read and Write Pipes.
|
144
|
+
dim as HANDLE StdInRd, StdOutRd, StdErrRd
|
145
|
+
dim as HANDLE StdInWr, StdOutWr, StdErrWr
|
146
|
+
dim merged as boolean
|
147
|
+
|
148
|
+
'# cmdline
|
149
|
+
dim cmdline as string
|
150
|
+
|
151
|
+
'# assume start will fail
|
152
|
+
result = false
|
153
|
+
|
154
|
+
if (running = false) then
|
155
|
+
'# we should create the std* for the new proc!
|
156
|
+
'# (like good parents, prepare everything!)
|
157
|
+
|
158
|
+
'# to ensure everything will work, we must allocate a console
|
159
|
+
'# using AllocConsole, even if it fails.
|
160
|
+
'# This solve the problems when running as service.
|
161
|
+
'# we discard result of AllocConsole since we ALWAYS will allocate it.
|
162
|
+
AllocConsole()
|
163
|
+
|
164
|
+
'# assume all the following steps succeed
|
165
|
+
success = true
|
166
|
+
|
167
|
+
'# StdIn is the only std that will be created using pipes always
|
168
|
+
'# StdIn
|
169
|
+
if (CreatePipe(@StdInRd, @StdInWr, @proc_sa, 0) = 0) then
|
170
|
+
success = false
|
171
|
+
end if
|
172
|
+
|
173
|
+
'# Ensure the handles to the pipe are not inherited.
|
174
|
+
if (SetHandleInformation(StdInWr, HANDLE_FLAG_INHERIT, 0) = 0) then
|
175
|
+
success = false
|
176
|
+
end if
|
177
|
+
|
178
|
+
'# StdOut and StdErr should be redirected?
|
179
|
+
if (not _stdout_filename = "") or _
|
180
|
+
(not _stderr_filename = "") then
|
181
|
+
|
182
|
+
'# out and err are the same? (merged)
|
183
|
+
if (_stdout_filename = _stderr_filename) then
|
184
|
+
merged = true
|
185
|
+
end if
|
186
|
+
end if
|
187
|
+
|
188
|
+
'# StdOut if stdout_filename
|
189
|
+
if not (_stdout_filename = "") then
|
190
|
+
StdOutWr = CreateFile(strptr(_stdout_filename), _
|
191
|
+
GENERIC_WRITE, _
|
192
|
+
FILE_SHARE_READ or FILE_SHARE_WRITE, _
|
193
|
+
@proc_sa, _
|
194
|
+
OPEN_ALWAYS, _
|
195
|
+
FILE_ATTRIBUTE_NORMAL, _
|
196
|
+
NULL)
|
197
|
+
|
198
|
+
if (StdOutWr = INVALID_HANDLE_VALUE) then
|
199
|
+
'# failed to open file
|
200
|
+
success = false
|
201
|
+
else
|
202
|
+
SetFilePointer(StdOutWr, 0, NULL, FILE_END)
|
203
|
+
end if
|
204
|
+
else
|
205
|
+
'# use pipes instead
|
206
|
+
'# StdOut
|
207
|
+
if (CreatePipe(@StdOutRd, @StdOutWr, @proc_sa, 0) = 0) then
|
208
|
+
success = false
|
209
|
+
end if
|
210
|
+
|
211
|
+
if (SetHandleInformation(StdOutRd, HANDLE_FLAG_INHERIT, 0) = 0) then
|
212
|
+
success = false
|
213
|
+
end if
|
214
|
+
end if 'not (_stdout_filename = "")
|
215
|
+
|
216
|
+
'# only create stderr if no merged.
|
217
|
+
if (merged = true) then
|
218
|
+
StdErrWr = StdOutWr
|
219
|
+
else
|
220
|
+
'# do the same for StdErr...
|
221
|
+
if not (_stderr_filename = "") then
|
222
|
+
StdErrWr = CreateFile(strptr(_stderr_filename), _
|
223
|
+
GENERIC_WRITE, _
|
224
|
+
FILE_SHARE_READ or FILE_SHARE_WRITE, _
|
225
|
+
@proc_sa, _
|
226
|
+
OPEN_ALWAYS, _
|
227
|
+
FILE_ATTRIBUTE_NORMAL, _
|
228
|
+
NULL)
|
229
|
+
|
230
|
+
if (StdErrWr = INVALID_HANDLE_VALUE) then
|
231
|
+
'# failed to open file
|
232
|
+
success = false
|
233
|
+
else
|
234
|
+
SetFilePointer(StdErrWr, 0, NULL, FILE_END)
|
235
|
+
end if
|
236
|
+
else
|
237
|
+
'# use pipes instead
|
238
|
+
'# StdOut
|
239
|
+
if (CreatePipe(@StdErrRd, @StdErrWr, @proc_sa, 0) = 0) then
|
240
|
+
success = false
|
241
|
+
end if
|
242
|
+
|
243
|
+
if (SetHandleInformation(StdErrRd, HANDLE_FLAG_INHERIT, 0) = 0) then
|
244
|
+
success = false
|
245
|
+
end if
|
246
|
+
|
247
|
+
end if 'not (_stderr_filename = "")
|
248
|
+
end if '(merged = true)
|
249
|
+
|
250
|
+
'# now we must proceed to create the process
|
251
|
+
'# without the pipes, we shouldn't continue!
|
252
|
+
if (success = true) then
|
253
|
+
'# Set the Std* handles ;-)
|
254
|
+
with context
|
255
|
+
.cb = sizeof( context )
|
256
|
+
.hStdError = StdErrWr
|
257
|
+
.hStdOutput = StdOutWr
|
258
|
+
.hStdInput = StdInRd
|
259
|
+
.dwFlags = STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW
|
260
|
+
'# FIXME: .wShowWindow = iif((_show_console = true), SW_SHOW, SW_HIDE)
|
261
|
+
.wShowWindow = SW_HIDE
|
262
|
+
end with
|
263
|
+
|
264
|
+
'# build the command line
|
265
|
+
cmdline = _filename + " " + _arguments
|
266
|
+
|
267
|
+
'# now creates the process
|
268
|
+
if (CreateProcess(NULL, _
|
269
|
+
strptr(cmdline), _
|
270
|
+
NULL, _
|
271
|
+
NULL, _
|
272
|
+
1, _ '# win32 TRUE (1)
|
273
|
+
0, _
|
274
|
+
NULL, _
|
275
|
+
NULL, _
|
276
|
+
@context, _
|
277
|
+
@_process_info) = 0) then
|
278
|
+
result = false
|
279
|
+
else
|
280
|
+
'# set the _pid
|
281
|
+
_pid = _process_info.dwProcessId
|
282
|
+
|
283
|
+
'# OK? yeah, I think so.
|
284
|
+
result = true
|
285
|
+
|
286
|
+
'# close the Std* handles
|
287
|
+
CloseHandle(StdInRd)
|
288
|
+
CloseHandle(StdInWr)
|
289
|
+
CloseHandle(StdOutRd)
|
290
|
+
CloseHandle(StdOutWr)
|
291
|
+
CloseHandle(StdErrRd)
|
292
|
+
CloseHandle(StdErrWr)
|
293
|
+
|
294
|
+
'# close children main Thread handle and
|
295
|
+
'# NULLify to avoid issues
|
296
|
+
CloseHandle(_process_info.hThread)
|
297
|
+
_process_info.hThread = NULL
|
298
|
+
end if '# (CreateProcess() = 0)
|
299
|
+
else
|
300
|
+
result = false
|
301
|
+
end if '# (success = TRUE)
|
302
|
+
end if
|
303
|
+
|
304
|
+
return result
|
305
|
+
end function
|
306
|
+
|
307
|
+
function ConsoleProcess.terminate(byval force as boolean = false) as boolean
|
308
|
+
dim result as boolean
|
309
|
+
dim success as boolean
|
310
|
+
|
311
|
+
dim proc as HANDLE
|
312
|
+
dim code as uinteger
|
313
|
+
dim wait_code as uinteger
|
314
|
+
|
315
|
+
'# is pid valid?
|
316
|
+
if (running = true) then
|
317
|
+
'# hook our custom console handler
|
318
|
+
if not (SetConsoleCtrlHandler(@_console_handler, 1) = 0) then
|
319
|
+
success = true
|
320
|
+
end if
|
321
|
+
|
322
|
+
if (success = true) then
|
323
|
+
'# get a handle to Process
|
324
|
+
proc = _process_info.hProcess
|
325
|
+
if not (proc = NULL) then
|
326
|
+
'# process is valid, perform actions
|
327
|
+
success = false
|
328
|
+
|
329
|
+
if not (force = true) then
|
330
|
+
'# send CTRL_C_EVENT and wait for result
|
331
|
+
if not (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0) = 0) then
|
332
|
+
'# it worked, wait 5 seconds terminates.
|
333
|
+
wait_code = WaitForSingleObject(proc, 10000)
|
334
|
+
if not (wait_code = WAIT_TIMEOUT) then
|
335
|
+
success = true
|
336
|
+
end if
|
337
|
+
else
|
338
|
+
success = false
|
339
|
+
end if
|
340
|
+
|
341
|
+
'# Ctrl-C didn't work, try Ctrl-Break
|
342
|
+
if (success = false) then
|
343
|
+
'# send CTRL_BREAK_EVENT and wait for result
|
344
|
+
if not (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0) = 0) then
|
345
|
+
'# it worked, wait 5 seconds terminates.
|
346
|
+
wait_code = WaitForSingleObject(proc, 10000)
|
347
|
+
if not (wait_code = WAIT_TIMEOUT) then
|
348
|
+
success = true
|
349
|
+
end if
|
350
|
+
else
|
351
|
+
success = false
|
352
|
+
end if
|
353
|
+
end if
|
354
|
+
|
355
|
+
'# only do termination if force was set.
|
356
|
+
elseif (force = true) and (success = false) then
|
357
|
+
'# still no luck? we should do a hard kill then
|
358
|
+
if (TerminateProcess(proc, 0) = 0) then
|
359
|
+
success = false
|
360
|
+
else
|
361
|
+
success = true
|
362
|
+
end if
|
363
|
+
end if
|
364
|
+
|
365
|
+
'# now get process exit code
|
366
|
+
if (success = true) then
|
367
|
+
result = true
|
368
|
+
else
|
369
|
+
result = false
|
370
|
+
end if
|
371
|
+
else
|
372
|
+
'# invalid process handler
|
373
|
+
result = false
|
374
|
+
end if
|
375
|
+
|
376
|
+
end if '# (success = true)
|
377
|
+
|
378
|
+
'# remove hooks
|
379
|
+
if not (SetConsoleCtrlHandler(@_console_handler, 0) = 0) then
|
380
|
+
success = true
|
381
|
+
end if
|
382
|
+
end if '# not (pid = 0)
|
383
|
+
|
384
|
+
return result
|
385
|
+
end function
|
386
|
+
|
387
|
+
function ConsoleProcess._console_handler(byval dwCtrlType as DWORD) as BOOL
|
388
|
+
dim result as BOOL
|
389
|
+
|
390
|
+
if (dwCtrlType = CTRL_C_EVENT) then
|
391
|
+
result = 1
|
392
|
+
elseif (dwCtrlType = CTRL_BREAK_EVENT) then
|
393
|
+
result = 1
|
394
|
+
end if
|
395
|
+
|
396
|
+
return result
|
397
|
+
end function
|