win32-service 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,89 +1,95 @@
1
- LOG_FILE = 'C:\\test.log'
1
+ LOG_FILE = 'C:\\win32_daemon_test.log'
2
2
 
3
3
  begin
4
- require 'win32/daemon'
5
- include Win32
4
+ require 'rubygems'
5
+ require 'win32/daemon'
6
+ include Win32
6
7
 
7
- class DemoDaemon < Daemon
8
- # This method fires off before the +service_main+ mainloop is entered.
9
- # Any pre-setup code you need to run before your service's mainloop
10
- # starts should be put here. Otherwise the service might fail with a
11
- # timeout error when you try to start it.
12
- #
13
- def service_init
14
- 10.times{ |i|
15
- File.open(LOG_FILE , 'a'){ |f| f.puts("#{i}") }
16
- sleep 1
17
- }
18
- end
8
+ class DemoDaemon < Daemon
9
+ # This method fires off before the +service_main+ mainloop is entered.
10
+ # Any pre-setup code you need to run before your service's mainloop
11
+ # starts should be put here. Otherwise the service might fail with a
12
+ # timeout error when you try to start it.
13
+ #
14
+ def service_init
15
+ 10.times{ |i|
16
+ File.open(LOG_FILE , 'a'){ |f| f.puts("#{i}") }
17
+ sleep 1
18
+ }
19
+ end
19
20
 
20
- # This is the daemon's mainloop. In other words, whatever runs here
21
- # is the code that runs while your service is running. Note that the
22
- # loop is not implicit.
23
- #
24
- # You must setup a loop as I've done here with the 'while running?'
25
- # code, or setup your own loop. Otherwise your service will exit and
26
- # won't be especially useful.
27
- #
28
- # In this particular case, I've setup a loop to append a short message
29
- # and timestamp to a file on your C: drive every 20 seconds. Be sure
30
- # to stop the service when you're done!
31
- #
32
- def service_main(*args)
33
- msg = 'service_main entered at: ' + Time.now.to_s
34
- File.open(LOG_FILE, 'a'){ |f|
35
- f.puts msg
36
- f.puts "Args: " + args.join(',')
37
- }
21
+ # This is the daemon's mainloop. In other words, whatever runs here
22
+ # is the code that runs while your service is running. Note that the
23
+ # loop is not implicit.
24
+ #
25
+ # You must setup a loop as I've done here with the 'while running?'
26
+ # code, or setup your own loop. Otherwise your service will exit and
27
+ # won't be especially useful.
28
+ #
29
+ # In this particular case, I've setup a loop to append a short message
30
+ # and timestamp to a file on your C: drive every 20 seconds. Be sure
31
+ # to stop the service when you're done!
32
+ #
33
+ def service_main(*args)
34
+ msg = 'service_main entered at: ' + Time.now.to_s
35
+
36
+ File.open(LOG_FILE, 'a'){ |f|
37
+ f.puts msg
38
+ f.puts "Args: " + args.join(',')
39
+ }
38
40
 
39
- while running?
40
- if state == RUNNING
41
- sleep 20
42
- msg = 'Service is running as of: ' + Time.now.to_s
43
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
44
- else
45
- sleep 0.5
46
- end
47
- end
41
+ # While we're in here the daemon is running.
42
+ while running?
43
+ if state == RUNNING
44
+ sleep 20
45
+ msg = 'Service is running as of: ' + Time.now.to_s
46
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
47
+ else # PAUSED or IDLE
48
+ sleep 0.5
49
+ end
50
+ end
51
+
52
+ # We've left the loop, the daemon is about to exit.
48
53
 
49
- File.open(LOG_FILE, 'a'){ |f| f.puts "STATE: #{state}" }
54
+ File.open(LOG_FILE, 'a'){ |f| f.puts "STATE: #{state}" }
50
55
 
51
- msg = 'service_main left at: ' + Time.now.to_s
52
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
53
- end
56
+ msg = 'service_main left at: ' + Time.now.to_s
57
+
58
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
59
+ end
54
60
 
55
- # This event triggers when the service receives a signal to stop. I've
56
- # added an explicit "exit!" here to ensure that the Ruby interpreter exits
57
- # properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
58
- # raise a SystemExitError, which I don't want.
59
- #
60
- def service_stop
61
- msg = 'Received stop signal at: ' + Time.now.to_s
62
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
63
- exit!
64
- end
61
+ # This event triggers when the service receives a signal to stop. I've
62
+ # added an explicit "exit!" here to ensure that the Ruby interpreter exits
63
+ # properly. I use 'exit!' instead of 'exit' because otherwise Ruby will
64
+ # raise a SystemExitError, which I don't want.
65
+ #
66
+ def service_stop
67
+ msg = 'Received stop signal at: ' + Time.now.to_s
68
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
69
+ exit!
70
+ end
65
71
 
66
- # This event triggers when the service receives a signal to pause.
67
- #
68
- def service_pause
69
- msg = 'Received pause signal at: ' + Time.now.to_s
70
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
71
- end
72
+ # This event triggers when the service receives a signal to pause.
73
+ #
74
+ def service_pause
75
+ msg = 'Received pause signal at: ' + Time.now.to_s
76
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
77
+ end
72
78
 
73
- # This event triggers when the service receives a signal to resume
74
- # from a paused state.
75
- #
76
- def service_resume
77
- msg = 'Received resume signal at: ' + Time.now.to_s
78
- File.open(LOG_FILE, 'a'){ |f| f.puts msg }
79
- end
80
- end
79
+ # This event triggers when the service receives a signal to resume
80
+ # from a paused state.
81
+ #
82
+ def service_resume
83
+ msg = 'Received resume signal at: ' + Time.now.to_s
84
+ File.open(LOG_FILE, 'a'){ |f| f.puts msg }
85
+ end
86
+ end
81
87
 
82
- # Create an instance of the Daemon and put it into a loop. I borrowed the
83
- # method name 'mainloop' from Tk, btw.
84
- #
85
- DemoDaemon.mainloop
88
+ # Create an instance of the Daemon and put it into a loop. I borrowed the
89
+ # method name 'mainloop' from Tk, btw.
90
+ #
91
+ DemoDaemon.mainloop
86
92
  rescue Exception => err
87
- File.open(LOG_FILE, 'a'){ |fh| fh.puts 'Daemon failure: ' + err }
88
- raise
93
+ File.open(LOG_FILE, 'a'){ |fh| fh.puts 'Daemon failure: ' + err }
94
+ raise
89
95
  end
@@ -3,21 +3,28 @@
3
3
  #
4
4
  # Test script for general futzing that shows off the basic
5
5
  # capabilities of this library. Modify as you see fit.
6
+ #
7
+ # You can run this sample program via the "example:services" task.
6
8
  #######################################################################
7
9
  require 'win32/service'
8
10
  include Win32
9
11
 
10
12
  puts "VERSION: " + Service::VERSION
11
13
 
12
- p Service.exists?("ClipSrv")
13
- p Service.exists?("foo")
14
+ p Service.exists?('Schedule')
15
+ p Service.exists?('bogusxxx')
14
16
 
15
- status = Service.status("ClipSrv")
17
+ status = Service.status('Schedule')
16
18
  p status
17
19
 
18
- info = Service.config_info("ClipSrv")
20
+ info = Service.config_info('Schedule')
21
+
22
+ print "\n\nShowing config info for Schedule service\n\n"
19
23
  p info
20
24
 
25
+ print "\n\nAbout to show all services\n\n"
26
+ sleep 10
27
+
21
28
  Service.services{ |struct|
22
- p struct
29
+ p struct
23
30
  }
data/ext/extconf.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'mkmf'
2
2
 
3
3
  dir_config('win32-daemon')
4
+ dir_config('seh')
5
+
4
6
  have_func('RegisterServiceCtrlHandlerEx')
7
+ have_header('seh.h')
8
+
5
9
  create_makefile('win32/daemon', 'win32')
data/ext/win32/daemon.c CHANGED
@@ -5,7 +5,11 @@
5
5
  #include <malloc.h>
6
6
  #include <tchar.h>
7
7
 
8
- #define WIN32_SERVICE_VERSION "0.7.1"
8
+ #ifdef HAVE_SEH_H
9
+ #include <seh.h>
10
+ #endif
11
+
12
+ #define WIN32_SERVICE_VERSION "0.7.2"
9
13
 
10
14
  // Ruby 1.9.x
11
15
  #ifndef RSTRING_PTR
@@ -52,71 +56,73 @@ void SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
52
56
  // Return an error code as a string
53
57
  LPTSTR ErrorDescription(DWORD p_dwError)
54
58
  {
55
- HLOCAL hLocal = NULL;
56
- static TCHAR ErrStr[1024];
57
- int len;
58
-
59
- if (!(len=FormatMessage(
60
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
61
- FORMAT_MESSAGE_FROM_SYSTEM |
62
- FORMAT_MESSAGE_IGNORE_INSERTS,
63
- NULL,
64
- p_dwError,
65
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
66
- (LPTSTR)&hLocal,
67
- 0,
68
- NULL)))
69
- {
70
- rb_raise(rb_eStandardError, "unable to format error message");
71
- }
72
- memset(ErrStr, 0, sizeof(ErrStr));
73
- strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
74
- LocalFree(hLocal);
75
- return ErrStr;
59
+ HLOCAL hLocal = NULL;
60
+ static TCHAR ErrStr[1024];
61
+ int len;
62
+
63
+ if (!(len=FormatMessage(
64
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
65
+ FORMAT_MESSAGE_FROM_SYSTEM |
66
+ FORMAT_MESSAGE_IGNORE_INSERTS,
67
+ NULL,
68
+ p_dwError,
69
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
70
+ (LPTSTR)&hLocal,
71
+ 0,
72
+ NULL)))
73
+ {
74
+ rb_raise(rb_eStandardError, "unable to format error message");
75
+ }
76
+ memset(ErrStr, 0, sizeof(ErrStr));
77
+ strncpy(ErrStr, (LPTSTR)hLocal, len-2); // remove \r\n
78
+ LocalFree(hLocal);
79
+ return ErrStr;
76
80
  }
77
81
 
78
82
  // Called by the service control manager after the call to
79
83
  // StartServiceCtrlDispatcher.
80
84
  void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
81
85
  {
82
- // Obtain the name of the service.
83
- LPTSTR lpszServiceName = lpszArgv[0];
86
+ // Obtain the name of the service.
87
+ LPTSTR lpszServiceName = lpszArgv[0];
84
88
 
85
- // Args passed to Service.start
86
- if(dwArgc > 1){
87
- unsigned int i;
88
- Argc = dwArgc - 1;
89
- Argv = malloc(sizeof(VALUE)*Argc);
89
+ // Args passed to Service.start
90
+ if(dwArgc > 1){
91
+ unsigned int i;
92
+ Argc = dwArgc - 1;
93
+ Argv = malloc(sizeof(VALUE)*Argc);
90
94
 
91
- for(i=1; i < dwArgc; i++)
92
- Argv[i-1] = rb_str_new2(lpszArgv[i]);
93
- }
95
+ for(i=1; i < dwArgc; i++)
96
+ Argv[i-1] = rb_str_new2(lpszArgv[i]);
97
+ }
94
98
 
95
- // Register the service ctrl handler.
96
- ssh = RegisterServiceCtrlHandler(lpszServiceName,
97
- (LPHANDLER_FUNCTION)Service_Ctrl);
99
+ // Register the service ctrl handler.
100
+ ssh = RegisterServiceCtrlHandler(
101
+ lpszServiceName,
102
+ (LPHANDLER_FUNCTION)Service_Ctrl
103
+ );
98
104
 
99
- // no service to stop, no service handle to notify, nothing to do but exit
100
- if(ssh == (SERVICE_STATUS_HANDLE)0)
101
- return;
105
+ // no service to stop, no service handle to notify, nothing to do but exit
106
+ if(ssh == (SERVICE_STATUS_HANDLE)0)
107
+ return;
102
108
 
103
- // The service has started.
104
- SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
109
+ // The service has started.
110
+ SetTheServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0);
105
111
 
106
- SetEvent(hStartEvent);
112
+ SetEvent(hStartEvent);
107
113
 
108
- // Main loop for the service.
109
- while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
110
- {
111
- }
114
+ // Main loop for the service.
115
+ while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
116
+ {
117
+ }
112
118
 
113
- // Main loop for the service.
114
- while(WaitForSingleObject(hStopCompletedEvent, 1000) != WAIT_OBJECT_0)
115
- {
116
- }
119
+ // Main loop for the service.
120
+ while(WaitForSingleObject(hStopCompletedEvent, 1000) != WAIT_OBJECT_0)
121
+ {
122
+ }
117
123
 
118
- // Stop the service.
119
- SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
124
+ // Stop the service.
125
+ SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
120
126
  }
121
127
 
122
128
  VALUE Service_Event_Dispatch(VALUE val)
@@ -134,174 +140,174 @@ VALUE Service_Event_Dispatch(VALUE val)
134
140
  return result;
135
141
  }
136
142
 
137
- VALUE Ruby_Service_Ctrl(VALUE self)
138
- {
139
- while (WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT)
140
- {
141
- __try
142
- {
143
- EnterCriticalSection(&csControlCode);
144
-
145
- // Check to see if anything interesting has been signaled
146
- if (waiting_control_code != IDLE_CONTROL_CODE)
147
- {
148
- if (waiting_control_code != SERVICE_CONTROL_STOP) {
149
- // if there is a code, create a ruby thread to deal with it
150
- // this might be over engineering the solution, but I don't
151
- // want to block Service_Ctrl longer than necessary and the
152
- // critical section will block it.
153
- VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
154
-
155
- if(EventHookHash != Qnil){
156
- VALUE val = rb_hash_aref(EventHookHash, INT2NUM(waiting_control_code));
157
-
158
- if(val != Qnil)
159
- rb_thread_create(Service_Event_Dispatch, (void*) val);
160
- }
161
- }
162
- else {
163
- break;
164
- }
165
- waiting_control_code = IDLE_CONTROL_CODE;
166
- }
167
- }
168
- __finally
169
- {
170
- LeaveCriticalSection(&csControlCode);
171
- }
143
+ VALUE Ruby_Service_Ctrl(VALUE self){
144
+ while(WaitForSingleObject(hStopEvent,0) == WAIT_TIMEOUT){
145
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
146
+ __try{
147
+ #endif
148
+ EnterCriticalSection(&csControlCode);
172
149
 
173
- // This is an ugly polling loop, be as polite as possible
174
- rb_thread_polling();
175
- }
150
+ // Check to see if anything interesting has been signaled
151
+ if(waiting_control_code != IDLE_CONTROL_CODE){
152
+ if(waiting_control_code != SERVICE_CONTROL_STOP){
153
+ // If there is a code, create a ruby thread to deal with it
154
+ // this might be over engineering the solution, but I don't
155
+ // want to block Service_Ctrl longer than necessary and the
156
+ // critical section will block it.
157
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
158
+
159
+ if(EventHookHash != Qnil){
160
+ VALUE val = rb_hash_aref(
161
+ EventHookHash,
162
+ INT2NUM(waiting_control_code)
163
+ );
164
+
165
+ if(val != Qnil)
166
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
167
+ }
168
+ }
169
+ else{
170
+ break;
171
+ }
172
+
173
+ waiting_control_code = IDLE_CONTROL_CODE;
174
+ }
175
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
176
+ }
177
+ __finally {
178
+ #endif
179
+ LeaveCriticalSection(&csControlCode);
180
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
181
+ }
182
+ #endif
183
+ // This is an ugly polling loop, be as polite as possible
184
+ rb_thread_polling();
185
+ }
176
186
 
177
- // force service_stop call
178
- {
179
- VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
187
+ // Force service_stop call
188
+ {
189
+ VALUE EventHookHash = rb_ivar_get(self, rb_intern("@event_hooks"));
180
190
 
181
- if(EventHookHash != Qnil){
182
- VALUE val = rb_hash_aref(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP));
191
+ if(EventHookHash != Qnil){
192
+ VALUE val = rb_hash_aref(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP));
183
193
 
184
- if(val!=Qnil)
185
- rb_thread_create(Service_Event_Dispatch, (void*) val);
186
- }
187
- }
194
+ if(val!=Qnil)
195
+ rb_thread_create(Service_Event_Dispatch, (void*) val);
196
+ }
197
+ }
188
198
 
189
- return Qnil;
199
+ return Qnil;
190
200
  }
191
201
 
192
202
  // Handles control signals from the service control manager.
193
203
  void WINAPI Service_Ctrl(DWORD dwCtrlCode)
194
204
  {
195
- DWORD dwState = SERVICE_RUNNING;
205
+ DWORD dwState = SERVICE_RUNNING;
196
206
 
197
- // hard to image this code ever failing, so we probably
198
- // don't need the __try/__finally wrapper
199
- __try
200
- {
201
- EnterCriticalSection(&csControlCode);
202
- waiting_control_code = dwCtrlCode;
203
- }
204
- __finally
205
- {
206
- LeaveCriticalSection(&csControlCode);
207
- }
208
-
209
- switch(dwCtrlCode)
210
- {
211
- case SERVICE_CONTROL_STOP:
212
- dwState = SERVICE_STOP_PENDING;
213
- break;
214
-
215
- case SERVICE_CONTROL_SHUTDOWN:
216
- dwState = SERVICE_STOP_PENDING;
217
- break;
218
-
219
- case SERVICE_CONTROL_PAUSE:
220
- dwState = SERVICE_PAUSED;
221
- break;
222
-
223
- case SERVICE_CONTROL_CONTINUE:
224
- dwState = SERVICE_RUNNING;
225
- break;
226
-
227
- case SERVICE_CONTROL_INTERROGATE:
228
- break;
229
-
230
- default:
231
- break;
232
- }
233
-
234
- // Set the status of the service.
235
- SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
236
-
237
- // Tell service_main thread to stop.
238
- if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
239
- (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
240
- {
241
- if(!SetEvent(hStopEvent))
242
- SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
243
- }
207
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
208
+ __try{
209
+ #endif
210
+ EnterCriticalSection(&csControlCode);
211
+ waiting_control_code = dwCtrlCode;
212
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
213
+ }
214
+ __finally{
215
+ #endif
216
+ LeaveCriticalSection(&csControlCode);
217
+ #if !defined(__GNUC__) || defined(HAVE_SEH_H)
218
+ }
219
+ #endif
220
+
221
+ switch(dwCtrlCode)
222
+ {
223
+ case SERVICE_CONTROL_STOP:
224
+ dwState = SERVICE_STOP_PENDING;
225
+ break;
226
+ case SERVICE_CONTROL_SHUTDOWN:
227
+ dwState = SERVICE_STOP_PENDING;
228
+ break;
229
+ case SERVICE_CONTROL_PAUSE:
230
+ dwState = SERVICE_PAUSED;
231
+ break;
232
+ case SERVICE_CONTROL_CONTINUE:
233
+ dwState = SERVICE_RUNNING;
234
+ break;
235
+ case SERVICE_CONTROL_INTERROGATE:
236
+ break;
237
+ default:
238
+ break;
239
+ }
240
+
241
+ // Set the status of the service.
242
+ SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
243
+
244
+ // Tell service_main thread to stop.
245
+ if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
246
+ (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
247
+ {
248
+ if(!SetEvent(hStopEvent))
249
+ SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
250
+ }
244
251
  }
245
252
 
246
253
  // Wraps SetServiceStatus.
247
254
  void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
248
255
  DWORD dwCheckPoint, DWORD dwWaitHint)
249
256
  {
250
- SERVICE_STATUS ss; // Current status of the service.
251
-
252
- // Disable control requests until the service is started.
253
- if(dwCurrentState == SERVICE_START_PENDING){
254
- ss.dwControlsAccepted = 0;
255
- }
256
- else{
257
- ss.dwControlsAccepted =
258
- SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
259
- SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
260
- }
261
-
262
- // Initialize ss structure.
263
- ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
264
- ss.dwServiceSpecificExitCode = 0;
265
- ss.dwCurrentState = dwCurrentState;
266
- ss.dwWin32ExitCode = dwWin32ExitCode;
267
- ss.dwCheckPoint = dwCheckPoint;
268
- ss.dwWaitHint = dwWaitHint;
269
-
270
- dwServiceState = dwCurrentState;
271
-
272
- // Send status of the service to the Service Controller.
273
- if(!SetServiceStatus(ssh, &ss))
274
- SetEvent(hStopEvent);
257
+ SERVICE_STATUS ss; // Current status of the service.
258
+
259
+ // Disable control requests until the service is started.
260
+ if(dwCurrentState == SERVICE_START_PENDING){
261
+ ss.dwControlsAccepted = 0;
262
+ }
263
+ else{
264
+ ss.dwControlsAccepted =
265
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
266
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN;
267
+ }
268
+
269
+ // Initialize ss structure.
270
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
271
+ ss.dwServiceSpecificExitCode = 0;
272
+ ss.dwCurrentState = dwCurrentState;
273
+ ss.dwWin32ExitCode = dwWin32ExitCode;
274
+ ss.dwCheckPoint = dwCheckPoint;
275
+ ss.dwWaitHint = dwWaitHint;
276
+
277
+ dwServiceState = dwCurrentState;
278
+
279
+ // Send status of the service to the Service Controller.
280
+ if(!SetServiceStatus(ssh, &ss))
281
+ SetEvent(hStopEvent);
275
282
  }
276
283
 
277
284
  DWORD WINAPI ThreadProc(LPVOID lpParameter){
278
- SERVICE_TABLE_ENTRY ste[] =
279
- {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
285
+ SERVICE_TABLE_ENTRY ste[] =
286
+ {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};
280
287
 
281
- // No service to step, no service handle, no ruby exceptions, just
282
- // terminate the thread.
283
- if(!StartServiceCtrlDispatcher(ste))
284
- return 1;
288
+ // No service to step, no service handle, no ruby exceptions, just
289
+ // terminate the thread.
290
+ if(!StartServiceCtrlDispatcher(ste))
291
+ return 1;
285
292
 
286
- return 0;
293
+ return 0;
287
294
  }
288
295
 
289
296
  static VALUE daemon_allocate(VALUE klass){
290
- return Data_Wrap_Struct(klass, 0, 0, 0);
297
+ return Data_Wrap_Struct(klass, 0, 0, 0);
291
298
  }
292
299
 
293
-
294
- // Call service_main method
300
+ // Call service_main method
295
301
  static VALUE daemon_mainloop_protect(VALUE self)
296
302
  {
297
- if(rb_respond_to(self,rb_intern("service_main"))){
298
- if(Argc == 0)
299
- rb_funcall(self, rb_intern("service_main"), 0);
300
- else
301
- rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
302
- }
303
-
304
- return self;
303
+ if(rb_respond_to(self,rb_intern("service_main"))){
304
+ if(Argc == 0)
305
+ rb_funcall(self, rb_intern("service_main"), 0);
306
+ else
307
+ rb_funcall2(self, rb_intern("service_main"), Argc, Argv);
308
+ }
309
+
310
+ return self;
305
311
  }
306
312
 
307
313
  static VALUE daemon_mainloop_ensure(VALUE self)
@@ -345,151 +351,151 @@ static VALUE daemon_mainloop_ensure(VALUE self)
345
351
  */
346
352
  static VALUE daemon_mainloop(VALUE self)
347
353
  {
348
- DWORD ThreadId;
349
- HANDLE events[2];
350
- DWORD index;
351
- VALUE result, EventHookHash;
352
- int status = 0;
353
-
354
- dwServiceState = 0;
355
-
356
- // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
357
- // associated with a tty. This helps newbs avoid Errno::EBADF errors.
358
- if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
359
- rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
360
-
361
- if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
362
- rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
363
-
364
- if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
365
- rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
366
-
367
- // Use a markable instance variable to prevent the garbage collector
368
- // from freeing the hash before Ruby_Service_Ctrl exits, or just
369
- // at any ole time while running the service
370
- EventHookHash = rb_hash_new();
371
- rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
372
-
373
- // Event hooks
374
- if(rb_respond_to(self, rb_intern("service_stop"))){
375
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
376
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
377
- }
378
-
379
- if(rb_respond_to(self, rb_intern("service_pause"))){
380
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
381
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
382
- }
383
-
384
- if(rb_respond_to(self, rb_intern("service_resume"))){
385
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
386
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
387
- }
388
-
389
- if(rb_respond_to(self, rb_intern("service_interrogate"))){
390
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
391
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
392
- }
393
-
394
- if(rb_respond_to(self, rb_intern("service_shutdown"))){
395
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
396
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
397
- }
354
+ DWORD ThreadId;
355
+ HANDLE events[2];
356
+ DWORD index;
357
+ VALUE result, EventHookHash;
358
+ int status = 0;
359
+
360
+ dwServiceState = 0;
361
+
362
+ // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
363
+ // associated with a tty. This helps newbs avoid Errno::EBADF errors.
364
+ if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
365
+ rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));
366
+
367
+ if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
368
+ rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));
369
+
370
+ if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
371
+ rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));
372
+
373
+ // Use a markable instance variable to prevent the garbage collector
374
+ // from freeing the hash before Ruby_Service_Ctrl exits, or just
375
+ // at any ole time while running the service
376
+ EventHookHash = rb_hash_new();
377
+ rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);
378
+
379
+ // Event hooks
380
+ if(rb_respond_to(self, rb_intern("service_stop"))){
381
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
382
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
383
+ }
384
+
385
+ if(rb_respond_to(self, rb_intern("service_pause"))){
386
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
387
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
388
+ }
389
+
390
+ if(rb_respond_to(self, rb_intern("service_resume"))){
391
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
392
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
393
+ }
394
+
395
+ if(rb_respond_to(self, rb_intern("service_interrogate"))){
396
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
397
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
398
+ }
399
+
400
+ if(rb_respond_to(self, rb_intern("service_shutdown"))){
401
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
402
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
403
+ }
398
404
 
399
405
  #ifdef SERVICE_CONTROL_PARAMCHANGE
400
- if(rb_respond_to(self, rb_intern("service_paramchange"))){
401
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
402
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
403
- }
406
+ if(rb_respond_to(self, rb_intern("service_paramchange"))){
407
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
408
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
409
+ }
404
410
  #endif
405
411
 
406
412
  #ifdef SERVICE_CONTROL_NETBINDADD
407
- if(rb_respond_to(self, rb_intern("service_netbindadd"))){
408
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
409
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
410
- }
413
+ if(rb_respond_to(self, rb_intern("service_netbindadd"))){
414
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
415
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
416
+ }
411
417
  #endif
412
418
 
413
419
  #ifdef SERVICE_CONTROL_NETBINDREMOVE
414
- if(rb_respond_to(self, rb_intern("service_netbindremove"))){
415
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
416
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
417
- }
420
+ if(rb_respond_to(self, rb_intern("service_netbindremove"))){
421
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
422
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
423
+ }
418
424
  #endif
419
425
 
420
426
  #ifdef SERVICE_CONTROL_NETBINDENABLE
421
- if(rb_respond_to(self, rb_intern("service_netbindenable"))){
422
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
423
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
424
- }
427
+ if(rb_respond_to(self, rb_intern("service_netbindenable"))){
428
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
429
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
430
+ }
425
431
  #endif
426
432
 
427
433
  #ifdef SERVICE_CONTROL_NETBINDDISABLE
428
- if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
429
- rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
430
- rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
431
- }
434
+ if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
435
+ rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
436
+ rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
437
+ }
432
438
  #endif
433
439
 
434
- // Calling init here so that init failures never even tries to
435
- // start the service... of course that means that init methods
436
- // must be very quick, because the SCM will be receiving no
437
- // START_PENDING messages while init's running - I may fix this
438
- // later
439
- if(rb_respond_to(self, rb_intern("service_init")))
440
- rb_funcall(self, rb_intern("service_init"),0);
440
+ // Calling init here so that init failures never even tries to
441
+ // start the service... of course that means that init methods
442
+ // must be very quick, because the SCM will be receiving no
443
+ // START_PENDING messages while init's running - I may fix this
444
+ // later
445
+ if(rb_respond_to(self, rb_intern("service_init")))
446
+ rb_funcall(self, rb_intern("service_init"),0);
441
447
 
442
- // Create the event to signal the service to start.
443
- hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
448
+ // Create the event to signal the service to start.
449
+ hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
444
450
 
445
- if(hStartEvent == NULL)
446
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
451
+ if(hStartEvent == NULL)
452
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
447
453
 
448
- // Create the event to signal the service to stop.
449
- hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
454
+ // Create the event to signal the service to stop.
455
+ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
450
456
 
451
- if(hStopEvent == NULL)
452
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
457
+ if(hStopEvent == NULL)
458
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
453
459
 
454
- // Create the event to signal the service that stop has completed
455
- hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
460
+ // Create the event to signal the service that stop has completed
461
+ hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
456
462
 
457
- if(hStopCompletedEvent == NULL)
458
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
463
+ if(hStopCompletedEvent == NULL)
464
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
459
465
 
460
- // Create Thread for service main
461
- hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
466
+ // Create Thread for service main
467
+ hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);
462
468
 
463
- if(hThread == INVALID_HANDLE_VALUE)
464
- rb_raise(cDaemonError, ErrorDescription(GetLastError()));
469
+ if(hThread == INVALID_HANDLE_VALUE)
470
+ rb_raise(cDaemonError, ErrorDescription(GetLastError()));
465
471
 
466
- events[0] = hThread;
467
- events[1] = hStartEvent;
472
+ events[0] = hThread;
473
+ events[1] = hStartEvent;
468
474
 
469
- // wait for Service_Main function to either start the service OR terminate
470
- while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
471
- {
472
- }
475
+ // wait for Service_Main function to either start the service OR terminate
476
+ while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
477
+ {
478
+ }
473
479
 
474
- // thread exited, so the show is off
475
- if(index == WAIT_OBJECT_0)
476
- rb_raise(cDaemonError, "Service_Main thread exited abnormally");
480
+ // thread exited, so the show is off
481
+ if(index == WAIT_OBJECT_0)
482
+ rb_raise(cDaemonError, "Service_Main thread exited abnormally");
477
483
 
478
- // from this point onward, stopevent must be triggered!
484
+ // from this point onward, stopevent must be triggered!
479
485
 
480
- // Create the green thread to poll for Service_Ctrl events
481
- rb_thread_create(Ruby_Service_Ctrl, (void *)self);
486
+ // Create the green thread to poll for Service_Ctrl events
487
+ rb_thread_create(Ruby_Service_Ctrl, (void *)self);
482
488
 
483
- result = rb_protect(daemon_mainloop_protect, self, &status);
489
+ result = rb_protect(daemon_mainloop_protect, self, &status);
484
490
 
485
- // service_main raised an exception
486
- if(status){
487
- daemon_mainloop_ensure(self);
488
- rb_jump_tag(status);
489
- }
491
+ // service_main raised an exception
492
+ if(status){
493
+ daemon_mainloop_ensure(self);
494
+ rb_jump_tag(status);
495
+ }
490
496
 
491
- // service_main exited cleanly
492
- return daemon_mainloop_ensure(self);
497
+ // service_main exited cleanly
498
+ return daemon_mainloop_ensure(self);
493
499
  }
494
500
 
495
501
  /*
@@ -510,7 +516,7 @@ static VALUE daemon_mainloop(VALUE self)
510
516
  * See the Daemon#running? method for an abstraction of the above code.
511
517
  */
512
518
  static VALUE daemon_state(VALUE self){
513
- return UINT2NUM(dwServiceState);
519
+ return UINT2NUM(dwServiceState);
514
520
  }
515
521
 
516
522
  /*
@@ -529,83 +535,78 @@ static VALUE daemon_state(VALUE self){
529
535
  * end
530
536
  */
531
537
  static VALUE daemon_is_running(VALUE self){
532
- VALUE v_bool = Qfalse;
533
- if(
534
- (dwServiceState == SERVICE_RUNNING) ||
535
- (dwServiceState == SERVICE_PAUSED) ||
536
- (dwServiceState == 0)
537
- ){
538
- v_bool = Qtrue;
539
- }
540
-
541
- return v_bool;
538
+ VALUE v_bool = Qfalse;
539
+
540
+ if(
541
+ (dwServiceState == SERVICE_RUNNING) ||
542
+ (dwServiceState == SERVICE_PAUSED) ||
543
+ (dwServiceState == 0)
544
+ ){
545
+ v_bool = Qtrue;
546
+ }
547
+
548
+ return v_bool;
542
549
  }
543
550
 
544
551
  /*
545
552
  * This is a shortcut for Daemon.new + Daemon#mainloop.
546
553
  */
547
554
  static VALUE daemon_c_mainloop(VALUE klass){
548
- VALUE v_args[1];
549
- VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
550
- return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
555
+ VALUE v_args[1];
556
+ VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
557
+ return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
551
558
  }
552
559
 
553
560
  void Init_daemon()
554
561
  {
555
- /* The Win32 module serves as a namespace only. */
556
- VALUE mWin32 = rb_define_module("Win32");
562
+ /* The Win32 module serves as a namespace only. */
563
+ VALUE mWin32 = rb_define_module("Win32");
557
564
 
558
- /* The Daemon class encapsulates a Windows service through the use
559
- * of callback methods and a main loop.
560
- */
561
- VALUE cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
565
+ /* The Daemon class encapsulates a Windows service through the use
566
+ * of callback methods and a main loop.
567
+ */
568
+ VALUE cDaemon = rb_define_class_under(mWin32, "Daemon", rb_cObject);
562
569
 
563
- /* Error typically raised if something goes wrong with your daemon. */
564
- cDaemonError = rb_define_class_under(cDaemon, "Error", rb_eStandardError);
570
+ /* Error typically raised if something goes wrong with your daemon. */
571
+ cDaemonError = rb_define_class_under(cDaemon, "Error", rb_eStandardError);
565
572
 
566
- rb_define_alloc_func(cDaemon, daemon_allocate);
567
- rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
568
- rb_define_method(cDaemon, "state", daemon_state, 0);
569
- rb_define_method(cDaemon, "running?", daemon_is_running, 0);
573
+ rb_define_alloc_func(cDaemon, daemon_allocate);
574
+ rb_define_method(cDaemon, "mainloop", daemon_mainloop, 0);
575
+ rb_define_method(cDaemon, "state", daemon_state, 0);
576
+ rb_define_method(cDaemon, "running?", daemon_is_running, 0);
570
577
 
571
- rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
578
+ rb_define_singleton_method(cDaemon, "mainloop", daemon_c_mainloop, 0);
572
579
 
573
- // Intialize critical section used by green polling thread
574
- InitializeCriticalSection(&csControlCode);
580
+ // Intialize critical section used by green polling thread
581
+ InitializeCriticalSection(&csControlCode);
575
582
 
576
- // Constants
583
+ // Constants
577
584
 
578
- /* 0.7.0: The version of this library */
579
- rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
585
+ /* 0.7.2: The version of this library */
586
+ rb_define_const(cDaemon, "VERSION", rb_str_new2(WIN32_SERVICE_VERSION));
580
587
 
581
- /* Service has received a signal to resume but is not yet running */
582
- rb_define_const(cDaemon, "CONTINUE_PENDING",
583
- INT2NUM(SERVICE_CONTINUE_PENDING));
588
+ /* Service has received a signal to resume but is not yet running */
589
+ rb_define_const(cDaemon, "CONTINUE_PENDING",
590
+ INT2NUM(SERVICE_CONTINUE_PENDING));
584
591
 
585
- /* Service has received a signal to pause but is not yet paused */
586
- rb_define_const(cDaemon, "PAUSE_PENDING",
587
- INT2NUM(SERVICE_PAUSE_PENDING));
592
+ /* Service has received a signal to pause but is not yet paused */
593
+ rb_define_const(cDaemon, "PAUSE_PENDING", INT2NUM(SERVICE_PAUSE_PENDING));
588
594
 
589
- /* Service is in a paused state */
590
- rb_define_const(cDaemon, "PAUSED",
591
- INT2NUM(SERVICE_PAUSED));
595
+ /* Service is in a paused state */
596
+ rb_define_const(cDaemon, "PAUSED", INT2NUM(SERVICE_PAUSED));
592
597
 
593
- /* Service is running */
594
- rb_define_const(cDaemon, "RUNNING",
595
- INT2NUM(SERVICE_RUNNING));
598
+ /* Service is running */
599
+ rb_define_const(cDaemon, "RUNNING", INT2NUM(SERVICE_RUNNING));
596
600
 
597
- /* Service has received a signal to start but is not yet running */
598
- rb_define_const(cDaemon, "START_PENDING",
599
- INT2NUM(SERVICE_START_PENDING));
601
+ /* Service has received a signal to start but is not yet running */
602
+ rb_define_const(cDaemon, "START_PENDING", INT2NUM(SERVICE_START_PENDING));
600
603
 
601
- /* Service has received a signal to stop but has not yet stopped */
602
- rb_define_const(cDaemon, "STOP_PENDING",
603
- INT2NUM(SERVICE_STOP_PENDING));
604
+ /* Service has received a signal to stop but has not yet stopped */
605
+ rb_define_const(cDaemon, "STOP_PENDING", INT2NUM(SERVICE_STOP_PENDING));
604
606
 
605
- /* Service is stopped. */
606
- rb_define_const(cDaemon, "STOPPED",
607
- INT2NUM(SERVICE_STOPPED));
607
+ /* Service is stopped. */
608
+ rb_define_const(cDaemon, "STOPPED", INT2NUM(SERVICE_STOPPED));
608
609
 
609
- /* Service is in an idle state */
610
- rb_define_const(cDaemon, "IDLE", INT2NUM(0));
610
+ /* Service is in an idle state */
611
+ rb_define_const(cDaemon, "IDLE", INT2NUM(0));
611
612
  }