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