thin_service 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +53 -0
- data/README.md +26 -0
- data/Rakefile +17 -0
- data/bin/thin_service +17 -0
- data/lib/thin_service.rb +15 -0
- data/lib/thin_service/command.rb +343 -0
- data/lib/thin_service/logger.rb +74 -0
- data/lib/thin_service/service_manager.rb +78 -0
- data/lib/thin_service/version.rb +3 -0
- data/resource/thin_service_wrapper.exe +0 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/thin_service_spec.rb +43 -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/thin_service/_debug.bi +61 -0
- data/src/thin_service/boolean.bi +18 -0
- data/src/thin_service/console_process.bas +397 -0
- data/src/thin_service/console_process.bi +75 -0
- data/src/thin_service/thin_service.bas +199 -0
- data/src/thin_service/thin_service.bi +61 -0
- data/tasks/native_lib.rake +40 -0
- data/tasks/native_service.rake +43 -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/thin_service.gemspec +21 -0
- data/tools/freebasic.rb +359 -0
- metadata +106 -0
@@ -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,61 @@
|
|
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
|
+
'# Adapted for Thin by Garth Smedley
|
14
|
+
'#
|
15
|
+
'##################################################################
|
16
|
+
|
17
|
+
'##################################################################
|
18
|
+
'# Requirements:
|
19
|
+
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
20
|
+
'#
|
21
|
+
'##################################################################
|
22
|
+
|
23
|
+
#ifndef __Debug_bi__
|
24
|
+
#define __Debug_bi__
|
25
|
+
|
26
|
+
#ifdef DEBUG_LOG
|
27
|
+
#include once "vbcompat.bi"
|
28
|
+
#ifndef DEBUG_LOG_FILE
|
29
|
+
#define DEBUG_LOG_FILE EXEPATH + "\debug.log"
|
30
|
+
#endif
|
31
|
+
|
32
|
+
'# this procedure is only used for debugging purposed, will be removed from
|
33
|
+
'# final compilation
|
34
|
+
private sub debug_to_file(byref message as string, byref file as string, byval linenumber as uinteger, byref func as string)
|
35
|
+
dim handle as integer
|
36
|
+
static first_time as integer
|
37
|
+
|
38
|
+
handle = freefile
|
39
|
+
open DEBUG_LOG_FILE for append as #handle
|
40
|
+
|
41
|
+
if (first_time = 0) then
|
42
|
+
print #handle, "# Logfile created on "; format(now(), "dd/mm/yyyy HH:mm:ss")
|
43
|
+
print #handle, ""
|
44
|
+
first_time = 1
|
45
|
+
end if
|
46
|
+
|
47
|
+
'# src/module.bas:123, namespace.function:
|
48
|
+
'# message
|
49
|
+
'#
|
50
|
+
print #handle, file; ":"; str(linenumber); ", "; lcase(func); ":"
|
51
|
+
print #handle, space(2); message
|
52
|
+
print #handle, ""
|
53
|
+
|
54
|
+
close #handle
|
55
|
+
end sub
|
56
|
+
#define debug(message) debug_to_file(message, __FILE__, __LINE__, __FUNCTION__)
|
57
|
+
#else
|
58
|
+
#define debug(message)
|
59
|
+
#endif '# DEBUG_LOG
|
60
|
+
|
61
|
+
#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
|