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,109 @@
|
|
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_bi__
|
17
|
+
#define __ServiceFB_bi__
|
18
|
+
|
19
|
+
#include once "windows.bi"
|
20
|
+
#inclib "advapi32"
|
21
|
+
|
22
|
+
namespace fb
|
23
|
+
namespace svc '# fb.svc
|
24
|
+
#ifdef SERVICEFB_DEBUG_LOG
|
25
|
+
'# debug print
|
26
|
+
declare sub _dprint(byref as string)
|
27
|
+
#else
|
28
|
+
#define _dprint(message)
|
29
|
+
#endif
|
30
|
+
|
31
|
+
'# service states used by end user with 'state' property
|
32
|
+
enum ServiceStateEnum
|
33
|
+
Running = SERVICE_RUNNING
|
34
|
+
Paused = SERVICE_PAUSED
|
35
|
+
Stopped = SERVICE_STOPPED
|
36
|
+
end enum
|
37
|
+
|
38
|
+
|
39
|
+
'# ServiceProcess type (object)
|
40
|
+
'# use this to create new services and reference the on*() methods to perform the related
|
41
|
+
'# tasks.
|
42
|
+
type ServiceProcess
|
43
|
+
'# ctor/dtor
|
44
|
+
declare constructor()
|
45
|
+
declare constructor(byref as string)
|
46
|
+
declare destructor()
|
47
|
+
|
48
|
+
'# methods (public)
|
49
|
+
declare sub Run()
|
50
|
+
declare sub StillAlive(byval as integer = 10)
|
51
|
+
|
52
|
+
'# helper methods (private)
|
53
|
+
declare sub UpdateState(byval as DWORD, byval as integer = 0, byval as integer = 0)
|
54
|
+
|
55
|
+
'# pseudo-events
|
56
|
+
'# for onInit you should return FALSE (0) in case you want to abort
|
57
|
+
'# service initialization.
|
58
|
+
'# If everything was ok, then return TRUE (-1)
|
59
|
+
onInit as function(byref as ServiceProcess) as integer
|
60
|
+
onStart as sub(byref as ServiceProcess)
|
61
|
+
onStop as sub(byref as ServiceProcess)
|
62
|
+
onPause as sub(byref as ServiceProcess)
|
63
|
+
onContinue as sub(byref as ServiceProcess)
|
64
|
+
|
65
|
+
'# call_* are used to avoid the warning arround ThreadCreate
|
66
|
+
declare static sub call_onStart(byval as any ptr)
|
67
|
+
|
68
|
+
'# properties (public)
|
69
|
+
name as string
|
70
|
+
description as string
|
71
|
+
state as ServiceStateEnum
|
72
|
+
commandline as string '# TODO
|
73
|
+
shared_process as integer
|
74
|
+
|
75
|
+
'# properties (private)
|
76
|
+
_svcStatus as SERVICE_STATUS
|
77
|
+
_svcHandle as SERVICE_STATUS_HANDLE
|
78
|
+
_svcStopEvent as HANDLE
|
79
|
+
_threadHandle as any ptr
|
80
|
+
end type
|
81
|
+
|
82
|
+
|
83
|
+
'# ServiceHost type (object)
|
84
|
+
'# use this, beside ServiceProcess, to manage the registration and running of
|
85
|
+
'# several services sharing the same process.
|
86
|
+
'# NOTE: ServiceHost.Run() and ServiceProcess.Run() are mutually exclusive, that
|
87
|
+
'# means don't mix single service with multiple service in the same program!
|
88
|
+
type ServiceHost
|
89
|
+
'# ctor/dtor()
|
90
|
+
declare constructor()
|
91
|
+
declare destructor()
|
92
|
+
|
93
|
+
'# methods (public)
|
94
|
+
declare sub Add(byref as ServiceProcess)
|
95
|
+
declare sub Run()
|
96
|
+
|
97
|
+
'# properties (public)
|
98
|
+
count as integer
|
99
|
+
end type
|
100
|
+
end namespace '# fb.svc
|
101
|
+
end namespace '# fb
|
102
|
+
|
103
|
+
#ifdef SERVICEFB_INCLUDE_UTILS
|
104
|
+
#include once "ServiceFB_Utils.bi"
|
105
|
+
#endif
|
106
|
+
|
107
|
+
#endif '# __ServiceFB_bi__
|
108
|
+
#endif '# __FB_WIN32__
|
109
|
+
#endif '# __FB_VERSION__
|
@@ -0,0 +1,495 @@
|
|
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
|
+
#include once "ServiceFB.bi"
|
9
|
+
#include once "_internals.bi"
|
10
|
+
#include once "ServiceFB_Utils.bi"
|
11
|
+
#include once "_utils_internals.bi"
|
12
|
+
|
13
|
+
namespace fb
|
14
|
+
namespace svc
|
15
|
+
namespace utils '# fb.svc.utils
|
16
|
+
'# private (internals) for ServiceProcess.Console()
|
17
|
+
dim shared _svc_stop_signal as any ptr
|
18
|
+
dim shared _svc_stop_mutex as any ptr
|
19
|
+
dim shared _svc_stopped as BOOL
|
20
|
+
dim shared _svc_in_console as ServiceProcess ptr
|
21
|
+
dim shared _svc_in_console_stop_flag as BOOL
|
22
|
+
|
23
|
+
'#####################
|
24
|
+
'# ServiceController
|
25
|
+
'# ctor()
|
26
|
+
constructor ServiceController()
|
27
|
+
with this
|
28
|
+
.product = "My Product"
|
29
|
+
.version = "v0.1"
|
30
|
+
.copyright = "my copyright goes here."
|
31
|
+
end with
|
32
|
+
end constructor
|
33
|
+
|
34
|
+
|
35
|
+
'# ctor(product)
|
36
|
+
constructor ServiceController(byref new_product as string)
|
37
|
+
this.product = new_product
|
38
|
+
end constructor
|
39
|
+
|
40
|
+
|
41
|
+
'# ctor(product, version)
|
42
|
+
constructor ServiceController(byref new_product as string, byref new_version as string)
|
43
|
+
constructor(new_product)
|
44
|
+
this.version = new_version
|
45
|
+
end constructor
|
46
|
+
|
47
|
+
|
48
|
+
'# ctor(product, version, copyright)
|
49
|
+
constructor ServiceController(byref new_product as string, byref new_version as string, byref new_copyright as string)
|
50
|
+
constructor(new_product, new_version)
|
51
|
+
this.copyright = new_copyright
|
52
|
+
end constructor
|
53
|
+
|
54
|
+
|
55
|
+
'# dtor()
|
56
|
+
destructor ServiceController()
|
57
|
+
end destructor
|
58
|
+
|
59
|
+
|
60
|
+
'# Banner() will display in the console, information regarding your program
|
61
|
+
'# using this formatting:
|
62
|
+
'# 'Product', 'Version'
|
63
|
+
'# 'Copyright'
|
64
|
+
sub ServiceController.Banner()
|
65
|
+
'# display Product and Version
|
66
|
+
print this.product; ", "; this.version
|
67
|
+
print this.copyright
|
68
|
+
print ""
|
69
|
+
'# leave a empty line between banner (header) and other info
|
70
|
+
end sub
|
71
|
+
|
72
|
+
|
73
|
+
'# RunMode() provide a simple way to get (*you*) from where this process was started
|
74
|
+
'# and do the corresponding action.
|
75
|
+
function ServiceController.RunMode() as ServiceRunMode
|
76
|
+
dim result as ServiceRunMode
|
77
|
+
dim currPID as DWORD
|
78
|
+
dim parent_pid as uinteger
|
79
|
+
dim parent_name as string
|
80
|
+
dim start_mode as string
|
81
|
+
|
82
|
+
_dprint("ServiceController.RunMode()")
|
83
|
+
|
84
|
+
'# get this process PID
|
85
|
+
currPID = GetCurrentProcessId()
|
86
|
+
_dprint("CurrentPID: " + str(currPID))
|
87
|
+
|
88
|
+
'# get the parent PID
|
89
|
+
parent_pid = _parent_pid(currPID)
|
90
|
+
_dprint("ParentPID: " + str(parent_pid))
|
91
|
+
|
92
|
+
'# now the the name
|
93
|
+
parent_name = _process_name(parent_pid)
|
94
|
+
if (parent_name = "<unknown>") then
|
95
|
+
parent_name = _process_name_dyn_psapi(parent_pid)
|
96
|
+
end if
|
97
|
+
_dprint("Parent Name: " + parent_name)
|
98
|
+
|
99
|
+
'# this process started as service?
|
100
|
+
'# that means his parent is services.exe
|
101
|
+
if (parent_name = "services.exe") then
|
102
|
+
result = RunAsService
|
103
|
+
else
|
104
|
+
'# ok, it didn't start as service, analyze command line then
|
105
|
+
start_mode = lcase(trim(command(1)))
|
106
|
+
if (start_mode = "manage") then
|
107
|
+
'# start ServiceController.Manage()
|
108
|
+
result = RunAsManager
|
109
|
+
elseif (start_mode = "console") then
|
110
|
+
'# start ServiceController.Console()
|
111
|
+
result = RunAsConsole
|
112
|
+
elseif (start_mode = "service") then
|
113
|
+
result = RunAsService
|
114
|
+
else
|
115
|
+
'# ok, the first paramenter in the commandline didn't work,
|
116
|
+
'# report back so we could send the banner!
|
117
|
+
result = RunAsUnknown
|
118
|
+
end if
|
119
|
+
end if
|
120
|
+
|
121
|
+
_dprint("ServiceController.RunMode() done")
|
122
|
+
return result
|
123
|
+
end function
|
124
|
+
|
125
|
+
|
126
|
+
'# Manage will offer the user (end-user) option in the commandline to
|
127
|
+
'# install, remove, start, stop and query the status of the installed service
|
128
|
+
'# use Manage() when you code a multi-services (ServiceHost) based programs
|
129
|
+
'# for single services, use Manage(service)
|
130
|
+
sub ServiceController.Manage()
|
131
|
+
end sub
|
132
|
+
|
133
|
+
|
134
|
+
'# this is used when you want management capabilities for your service
|
135
|
+
'# use this for single services, or call Manage() for multi services
|
136
|
+
sub ServiceController.Manage(byref service as ServiceProcess)
|
137
|
+
end sub
|
138
|
+
|
139
|
+
|
140
|
+
'# this offer the user a way to test/debug your service or run it like a normal
|
141
|
+
'# program, from the command line
|
142
|
+
'# will let you SHUTDOWN the service using CTRL+C
|
143
|
+
'# use this for multi-services (ServiceHost) based programs
|
144
|
+
sub ServiceController.Console()
|
145
|
+
dim working_thread as any ptr
|
146
|
+
dim run_mode as string
|
147
|
+
dim service_name as string
|
148
|
+
dim service as ServiceProcess ptr
|
149
|
+
dim commandline as string
|
150
|
+
dim success as integer
|
151
|
+
|
152
|
+
_dprint("ServiceController.Console()")
|
153
|
+
|
154
|
+
'# show the controller banner
|
155
|
+
this.Banner()
|
156
|
+
|
157
|
+
'# determine how many service exist in references
|
158
|
+
if (_svc_references_count > 0) then
|
159
|
+
_build_commandline(run_mode, service_name, commandline)
|
160
|
+
service = _find_in_references(service_name)
|
161
|
+
|
162
|
+
if (service = 0) then
|
163
|
+
'# no valid service reference, list available services
|
164
|
+
_list_references()
|
165
|
+
else
|
166
|
+
'# build the command line, excluding 'console' and service_name
|
167
|
+
service->commandline = commandline
|
168
|
+
|
169
|
+
'# got a service reference
|
170
|
+
'# also, set the global handler that will be used by _control_handler
|
171
|
+
_svc_in_console = service
|
172
|
+
|
173
|
+
'# create the signal used to stop the service thread.
|
174
|
+
_svc_stop_signal = condcreate()
|
175
|
+
_svc_stop_mutex = mutexcreate()
|
176
|
+
|
177
|
+
'# register the Console Handler
|
178
|
+
SetConsoleCtrlHandler(@_console_handler, TRUE)
|
179
|
+
|
180
|
+
print "Starting service '"; service_name; "' in console mode, please wait..."
|
181
|
+
|
182
|
+
'# onInit should be started inline,
|
183
|
+
'# and its result validated!
|
184
|
+
if not (service->onInit = 0) then
|
185
|
+
success = service->onInit(*service)
|
186
|
+
end if
|
187
|
+
|
188
|
+
'# only continue if success
|
189
|
+
if not (success = 0) then
|
190
|
+
'# now set service.state to running
|
191
|
+
service->state = Running
|
192
|
+
|
193
|
+
'# now, fire the main loop (onStart)
|
194
|
+
if not (service->onStart = 0) then
|
195
|
+
'# create the thread
|
196
|
+
working_thread = threadcreate(@ServiceProcess.call_onStart, service)
|
197
|
+
if (working_thread = 0) then
|
198
|
+
print "Failed to create working thread."
|
199
|
+
end if
|
200
|
+
end if
|
201
|
+
|
202
|
+
print "Service is in running state."
|
203
|
+
print "Press Ctrl-C to stop it."
|
204
|
+
|
205
|
+
'# now that onStart is running, must monitor the stop_signal
|
206
|
+
'# in case it arrives, the service state must change to exit the
|
207
|
+
'# working thread.
|
208
|
+
mutexlock(_svc_stop_mutex)
|
209
|
+
do while (_svc_stopped = FALSE)
|
210
|
+
condwait(_svc_stop_signal, _svc_stop_mutex)
|
211
|
+
loop
|
212
|
+
mutexunlock(_svc_stop_mutex)
|
213
|
+
|
214
|
+
print "Stop signal received, stopping..."
|
215
|
+
|
216
|
+
'# received the signal, so set state = Stopped
|
217
|
+
service->state = Stopped
|
218
|
+
|
219
|
+
print "Waiting for onStart() to exit..."
|
220
|
+
|
221
|
+
'# now wait for the thread to terminate
|
222
|
+
if not (working_thread = 0) then
|
223
|
+
threadwait(working_thread)
|
224
|
+
end if
|
225
|
+
|
226
|
+
else
|
227
|
+
print "Error starting the service, onInit() failed."
|
228
|
+
end if
|
229
|
+
|
230
|
+
print "Service stopped, doing cleanup."
|
231
|
+
|
232
|
+
'# remove the console handler
|
233
|
+
SetConsoleCtrlHandler(@_console_handler, FALSE)
|
234
|
+
|
235
|
+
'# now that service was stopped, destroy the references.
|
236
|
+
conddestroy(_svc_stop_signal)
|
237
|
+
mutexdestroy(_svc_stop_mutex)
|
238
|
+
print "Done."
|
239
|
+
end if
|
240
|
+
else
|
241
|
+
print "ERROR: No services could be served by this program. Exiting."
|
242
|
+
end if
|
243
|
+
|
244
|
+
_dprint("ServiceController.Console() done")
|
245
|
+
end sub
|
246
|
+
|
247
|
+
|
248
|
+
'# this offer the user a way to test/debug your service or run it like a normal
|
249
|
+
'# program, from the command line
|
250
|
+
'# will let you SHUTDOWN the service using CTRL+C
|
251
|
+
'# use this for single-services
|
252
|
+
sub ServiceController.Console(byref service as ServiceProcess)
|
253
|
+
|
254
|
+
_dprint("ServiceController.RunMode(service)")
|
255
|
+
|
256
|
+
'# register the service in the references table
|
257
|
+
_add_to_references(service)
|
258
|
+
|
259
|
+
_dprint("delegate to Console()")
|
260
|
+
'# now delegate control to Console()
|
261
|
+
this.Console()
|
262
|
+
|
263
|
+
_dprint("ServiceController.Console(service) done")
|
264
|
+
end sub
|
265
|
+
|
266
|
+
|
267
|
+
'# console_handler is used to get feedback form keyboard and allow
|
268
|
+
'# shutdown of service using Ctrl+C / Ctrl+Break from keyboard
|
269
|
+
function _console_handler(byval dwCtrlType as DWORD) as BOOL
|
270
|
+
dim result as BOOL
|
271
|
+
dim service as ServiceProcess ptr
|
272
|
+
|
273
|
+
_dprint("_console_handler()")
|
274
|
+
|
275
|
+
'# get the reference from svc_in_console
|
276
|
+
service = _svc_in_console
|
277
|
+
|
278
|
+
'# we default processing of the message to false
|
279
|
+
result = FALSE
|
280
|
+
|
281
|
+
'# avoid recursion problems
|
282
|
+
if (_svc_in_console_stop_flag = FALSE) then
|
283
|
+
_dprint("no previous signaled, process event")
|
284
|
+
'# all the CtrlType events listed will raise the onStop
|
285
|
+
'# of the service
|
286
|
+
'# here also will be raised the _svc_stop_signal
|
287
|
+
select case dwCtrlType
|
288
|
+
case CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_BREAK_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT:
|
289
|
+
_dprint("got supported CTRL_*_EVENT")
|
290
|
+
'# avoid recursion problems
|
291
|
+
_svc_in_console_stop_flag = TRUE
|
292
|
+
_dprint("set signaled to TRUE")
|
293
|
+
|
294
|
+
'# the service defined onStop?
|
295
|
+
if not (service->onStop = 0) then
|
296
|
+
_dprint("pass control to onStop()")
|
297
|
+
service->onStop(*service)
|
298
|
+
end if
|
299
|
+
|
300
|
+
'# now fire the signal
|
301
|
+
_dprint("fire stop signal")
|
302
|
+
mutexlock(_svc_stop_mutex)
|
303
|
+
condsignal(_svc_stop_signal)
|
304
|
+
result = TRUE
|
305
|
+
_svc_in_console_stop_flag = FALSE
|
306
|
+
_svc_stopped = TRUE
|
307
|
+
mutexunlock(_svc_stop_mutex)
|
308
|
+
|
309
|
+
case else:
|
310
|
+
_dprint("unsupported CTRL EVENT")
|
311
|
+
result = FALSE
|
312
|
+
end select
|
313
|
+
else
|
314
|
+
_dprint("already running onStop(), do not pass the message to other message handlers!")
|
315
|
+
result = TRUE
|
316
|
+
end if
|
317
|
+
|
318
|
+
_dprint("_console_handler() done")
|
319
|
+
return result
|
320
|
+
end function
|
321
|
+
|
322
|
+
|
323
|
+
'# helper private subs used to list the services and their descriptions
|
324
|
+
'# in _svc_references
|
325
|
+
private sub _list_references()
|
326
|
+
dim item as ServiceProcess ptr
|
327
|
+
dim idx as integer
|
328
|
+
|
329
|
+
print "Available services in this program:"
|
330
|
+
|
331
|
+
for idx = 0 to (_svc_references_count - 1)
|
332
|
+
item = _svc_references[idx]
|
333
|
+
|
334
|
+
print space(2);
|
335
|
+
print trim(item->name), , trim(item->description)
|
336
|
+
next idx
|
337
|
+
|
338
|
+
end sub
|
339
|
+
|
340
|
+
|
341
|
+
'# TODO: SimpleLogger
|
342
|
+
'# TODO: EventLogger
|
343
|
+
|
344
|
+
|
345
|
+
'#####################
|
346
|
+
'# private (internals)
|
347
|
+
'# _parent_pid is used to retrieve, based on the PID you passed by, the one of the parent
|
348
|
+
'# that launched that process.
|
349
|
+
'# on fail, it will return 0
|
350
|
+
'# Thanks to MichaelW (FreeBASIC forums) for his help about this.
|
351
|
+
private function _parent_pid(byval PID as uinteger) as uinteger
|
352
|
+
dim as uinteger result
|
353
|
+
dim as HANDLE hProcessSnap
|
354
|
+
dim as PROCESSENTRY32 pe32
|
355
|
+
|
356
|
+
'# initialize result, 0 = fail, other number, ParentPID
|
357
|
+
result = 0
|
358
|
+
|
359
|
+
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
360
|
+
if not (hProcessSnap = INVALID_HANDLE_VALUE) then
|
361
|
+
pe32.dwSize = sizeof(PROCESSENTRY32)
|
362
|
+
if (Process32First(hProcessSnap, @pe32) = TRUE) then
|
363
|
+
do
|
364
|
+
if (pe32.th32ProcessID = PID) then
|
365
|
+
result = pe32.th32ParentProcessID
|
366
|
+
exit do
|
367
|
+
end if
|
368
|
+
loop while not (Process32Next(hProcessSnap, @pe32) = 0)
|
369
|
+
end if
|
370
|
+
end if
|
371
|
+
|
372
|
+
CloseHandle(hProcessSnap)
|
373
|
+
return result
|
374
|
+
end function
|
375
|
+
|
376
|
+
|
377
|
+
'# _process_name is used to retrieve the name (ImageName, BaseModule, whatever) of the PID you
|
378
|
+
'# pass to it. if no module name was found, it should return <unknown>
|
379
|
+
private function _process_name(byval PID as uinteger) as string
|
380
|
+
dim result as string
|
381
|
+
dim hProcess as HANDLE
|
382
|
+
dim hMod as HMODULE
|
383
|
+
dim cbNeeded as DWORD
|
384
|
+
|
385
|
+
'# assign "<unknown>" to process name, allocate MAX_PATH (260 bytes)
|
386
|
+
result = "<unknown>"
|
387
|
+
result += space(MAX_PATH - len(result))
|
388
|
+
|
389
|
+
'# get a handle to the Process
|
390
|
+
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, PID)
|
391
|
+
|
392
|
+
'# if valid, get the process name
|
393
|
+
if not (hProcess = NULL) then
|
394
|
+
'# success getting Process modules
|
395
|
+
if not (EnumProcessModules(hProcess, @hMod, sizeof(hMod), @cbNeeded) = 0) then
|
396
|
+
result = space(cbNeeded)
|
397
|
+
GetModuleBaseName(hProcess, hMod, strptr(result), len(result))
|
398
|
+
end if
|
399
|
+
end if
|
400
|
+
|
401
|
+
CloseHandle(hProcess)
|
402
|
+
|
403
|
+
'# return a trimmed result
|
404
|
+
result = trim(result)
|
405
|
+
return result
|
406
|
+
end function
|
407
|
+
|
408
|
+
'# _process_name_dyn_psapi is a workaround for some issues with x64 versions of Windows.
|
409
|
+
'# by default, 32bits process can't query information from 64bits modules.
|
410
|
+
private function _process_name_dyn_psapi(byval PID as uinteger) as string
|
411
|
+
dim result as string
|
412
|
+
dim chop as uinteger
|
413
|
+
dim zresult as zstring * MAX_PATH
|
414
|
+
dim hLib as any ptr
|
415
|
+
dim hProcess as HANDLE
|
416
|
+
dim cbNeeded as DWORD
|
417
|
+
dim GetProcessImageFileName as function (byval as HANDLE, byval as LPTSTR, byval as DWORD) as DWORD
|
418
|
+
|
419
|
+
'# assign "<unknown>" to process name, allocate MAX_PATH (260 bytes)
|
420
|
+
zresult = "<unknown>" + chr(0)
|
421
|
+
|
422
|
+
'# get dynlib
|
423
|
+
hLib = dylibload("psapi.dll")
|
424
|
+
if not (hlib = 0) then
|
425
|
+
GetProcessImageFileName = dylibsymbol(hlib, "GetProcessImageFileNameA")
|
426
|
+
if not (GetProcessImageFileName = 0) then
|
427
|
+
'# get a handle to the Process
|
428
|
+
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID)
|
429
|
+
|
430
|
+
'# if valid, get the process name
|
431
|
+
if not (hProcess = NULL) then
|
432
|
+
cbNeeded = sizeof(zresult)
|
433
|
+
if (GetProcessImageFileName(hProcess, @zresult, cbNeeded) = 0) then
|
434
|
+
_dprint("Error with GetProcessImageFileName")
|
435
|
+
_dprint("GetLastError: " + str(GetLastError()) + _show_error())
|
436
|
+
else
|
437
|
+
result = zresult
|
438
|
+
chop = InStrRev(0, result, "\")
|
439
|
+
if (chop > 0) then
|
440
|
+
result = mid(result, chop + 1, (len(result) - chop))
|
441
|
+
end if
|
442
|
+
end if
|
443
|
+
else
|
444
|
+
_dprint("Error with OpenProcess")
|
445
|
+
_dprint("GetLastError: " + str(GetLastError()) + _show_error())
|
446
|
+
end if
|
447
|
+
|
448
|
+
CloseHandle(hProcess)
|
449
|
+
else
|
450
|
+
_dprint("Unable to get a reference to dynamic symbol GetProcessImageFileNameA.")
|
451
|
+
end if
|
452
|
+
else
|
453
|
+
_dprint("Unable to dynamic load psapi.dll")
|
454
|
+
end if
|
455
|
+
|
456
|
+
'# return a trimmed result
|
457
|
+
'result = trim(result)
|
458
|
+
return result
|
459
|
+
end function
|
460
|
+
|
461
|
+
private function _show_error() as string
|
462
|
+
dim buffer as string * 1024
|
463
|
+
dim p as integer
|
464
|
+
|
465
|
+
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,_
|
466
|
+
0,_
|
467
|
+
GetLastError(),_
|
468
|
+
0,_
|
469
|
+
strptr(buffer),_
|
470
|
+
1024,_
|
471
|
+
0 )
|
472
|
+
buffer = rtrim(buffer)
|
473
|
+
p = instr(buffer, chr(13))
|
474
|
+
if p then buffer = left(buffer, p - 1)
|
475
|
+
|
476
|
+
return buffer
|
477
|
+
end function
|
478
|
+
|
479
|
+
private function InStrRev(byval start as uinteger = 0, byref src as string, byref search as string) as uinteger
|
480
|
+
dim lensearch as uinteger = len(search)
|
481
|
+
dim as uinteger b, a = 0, exit_loop = 0
|
482
|
+
|
483
|
+
do
|
484
|
+
b = a
|
485
|
+
a += 1
|
486
|
+
a = instr(a, src, search)
|
487
|
+
if start >= lensearch then if a + lensearch > start then exit_loop = 1
|
488
|
+
loop while (a > 0) and (exit_loop = 0)
|
489
|
+
|
490
|
+
return b
|
491
|
+
end function
|
492
|
+
|
493
|
+
end namespace '# fb.svc.utils
|
494
|
+
end namespace '# fb.svc
|
495
|
+
end namespace '# fb
|