win32-eventlog 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.5.3 - 26-Oct-2011
2
+ * Fixed the String#nstrip method for Ruby 1.9.x. Thanks go to Jason Bourne
3
+ for the spot and Ben Jansen for the patch.
4
+ * Refactored the Rakefile and tests.
5
+
1
6
  == 0.5.2 - 21-Aug-2009
2
7
  * EventLogStruct's are now frozen on read operations. This is read-only data.
3
8
  * Added a test to validate that structs are frozen.
@@ -103,7 +108,7 @@
103
108
  * Added the EventLog#full? method.
104
109
  * Documentation updates and corrections, including the tutorial on creating
105
110
  and writing to your own event source.
106
-
111
+
107
112
  == 0.3.3 - 2-Jan-2006
108
113
  * If EventLog.new fails, it now raises EventLogError instead of StandardError.
109
114
  * Added documentation to the eventlog.txt file for EventLog#notify_change.
@@ -129,7 +134,7 @@
129
134
  no block is given.
130
135
  * Added the 'rubymsg.mc' message category file. If installed, this will
131
136
  create a 'RubyMsg' event source for use with win32-service, though you
132
- may use it for whatever you wish. See the README for more details.
137
+ may use it for whatever you wish. See the README for more details.
133
138
  * Renamed struct back to EventLogStruct - sorry, sorry - I promise I won't
134
139
  change this again.
135
140
  * Fixed a bug where using EventLog::SEEK_READ did not work properly (thanks
data/README CHANGED
@@ -1,73 +1,68 @@
1
1
  == Description
2
- The win32-eventlog library provides an interface for reading from and
3
- writing to the MS Windows Event Log.
2
+ The win32-eventlog library provides an interface for reading from and
3
+ writing to the MS Windows Event Log.
4
4
 
5
- In addition, you can create your own message event sources using the
6
- win32-mc library (provided with this distro), assuming you have the
7
- proper tools installed.
5
+ In addition, you can create your own message event sources using the
6
+ win32-mc library (provided with this distro), assuming you have the
7
+ proper tools installed.
8
8
 
9
9
  == Prerequisites
10
- Ruby 1.8.2 or later.
11
- windows-pr 0.9.3 or later.
12
-
13
- The 'mc', 'rc' and 'link' command line tools are required to create and
14
- install message sources. You won't need these for simply reading from or
15
- writing to an existing event log.
10
+ windows-pr 0.9.3 or later.
11
+
12
+ The 'mc', 'rc' and 'link' command line tools are required to create and
13
+ install message sources. You won't need these for simply reading from or
14
+ writing to an existing event log.
16
15
 
17
16
  == Installation
18
- === Gem Installation
19
- gem install win32-eventlog
20
- === Local Installation
21
- rake test (optional)
22
- rake install (non-gem) or rake install_gem (gem)
17
+ gem install win32-eventlog
23
18
 
24
19
  === General Installation Notes
25
- This will install both the win32-eventlog and win32-mc libraries. The latter
26
- is strictly for turning .mc files into .dll files. See the mc documentation
27
- for more details.
20
+ This will install both the win32-eventlog and win32-mc libraries. The latter
21
+ is strictly for turning .mc files into .dll files. See the mc documentation
22
+ for more details.
28
23
 
29
24
  == Installing the 'RubyMsg' event source
30
- If you wish to install the RubyMsg event source, run the 'install_msg.rb'
31
- script in the 'misc' directory. This will create a 'rubymsg' directory
32
- under your toplevel Ruby installation directory (usually C:\ruby), and
33
- create the .dll, .h, .rc and .res files there, in addition to copying the
34
- rubymsg.mc file. It will then install the 'RubyMsg' event source into your
35
- registry.
25
+ If you wish to install the RubyMsg event source then run the
26
+ event_source:install Rake task. This will create a 'rubymsg' directory
27
+ under your toplevel Ruby installation directory (usually C:\ruby), and
28
+ create the .dll, .h, .rc and .res files there, in addition to copying the
29
+ rubymsg.mc file. It will then install the 'RubyMsg' event source into your
30
+ registry.
36
31
 
37
- DO NOT MOVE THE DLL FILE ONCE IT IS INSTALLED! If you do, you will have
38
- to delete the registry entry and reinstall it with the correct path.
32
+ DO NOT MOVE THE DLL FILE ONCE IT IS INSTALLED! If you do, you will have
33
+ to delete the registry entry and reinstall it with the correct path.
39
34
 
40
- Take a look at the rubymsg.mc file for the category and message values. If
41
- you do not understand this, please read the 'tutorial.txt' file in the 'doc'
42
- directory.
35
+ Take a look at the rubymsg.mc file for the category and message values. If
36
+ you do not understand this, please read the 'tutorial.txt' file in the 'doc'
37
+ directory.
43
38
 
44
39
  == Additional documentation
45
- If you are unfamiliar with message files and event logging on Windows in
46
- general, please read the 'tutorial.txt' file.
40
+ If you are unfamiliar with message files and event logging on Windows in
41
+ general, please read the 'tutorial.txt' file.
47
42
 
48
- There are also a couple of sample test scripts under the 'examples'
49
- directory if you want to futz around and get a feel for how things work.
43
+ There are also a couple of sample test scripts under the 'examples'
44
+ directory if you want to futz around and get a feel for how things work.
50
45
 
51
46
  == If the test_mc.rb tests are skipped
52
- If the tests from the test_mc.rb file are omitted then you either don't
53
- have the mc, rc and/or link commands installed or they're not in your
54
- system's %PATH%. If you have MSVC++, you should have them somewhere on your
55
- system.
47
+ If the tests from the test_mc.rb file are omitted then you either don't
48
+ have the mc, rc and/or link commands installed or they're not in your
49
+ system's %PATH%. If you have MSVC++, you should have them somewhere on your
50
+ system.
56
51
 
57
52
  == Known Issues
58
- None known.
53
+ None known.
59
54
 
60
- Please file any bug reports on the project page at
61
- http://www.rubyforge.org/projects/win32utils.
55
+ Please file any bug reports on the project page at
56
+ http://www.rubyforge.org/projects/win32utils.
62
57
 
63
58
  == License
64
- Artistic 2.0
59
+ Artistic 2.0
65
60
 
66
61
  == Warranty
67
- This package is provided "as is" and without any express or
68
- implied warranties, including, without limitation, the implied
69
- warranties of merchantability and fitness for a particular purpose.
62
+ This package is provided "as is" and without any express or
63
+ implied warranties, including, without limitation, the implied
64
+ warranties of merchantability and fitness for a particular purpose.
70
65
 
71
66
  == Authors
72
- Daniel J. Berger
73
- Park Heesob
67
+ Daniel J. Berger
68
+ Park Heesob
data/Rakefile CHANGED
@@ -1,46 +1,65 @@
1
1
  require 'rake'
2
+ require 'rake/clean'
2
3
  require 'rake/testtask'
3
4
 
4
- desc 'Install win32-eventlog and win32-mc (non-gem)'
5
- task :install do
6
- dest = File.join(Config::CONFIG['sitelibdir'], 'win32')
7
- Dir.mkdir(dest) unless File.exists? dest
8
- cp 'lib/win32/eventlog.rb', dest, :verbose => true
9
- cp 'lib/win32/mc.rb', dest, :verbose => true
10
- end
5
+ CLEAN.include('**/*.gem', '**/*.rbc')
6
+
7
+ namespace :gem do
8
+ desc 'Create the win32-eventlog gem'
9
+ task :create => [:clean] do
10
+ spec = eval(IO.read('win32-eventlog.gemspec'))
11
+ Gem::Builder.new(spec).build
12
+ end
11
13
 
12
- desc 'Install the win32-eventlog library as a gem'
13
- task :install_gem do
14
- ruby 'win32-eventlog.gemspec'
15
- file = Dir["*.gem"].first
16
- sh "gem install #{file}"
14
+ desc 'Install the win32-eventlog gem'
15
+ task :install => [:create] do
16
+ ruby 'win32-eventlog.gemspec'
17
+ file = Dir["*.gem"].first
18
+ sh "gem install #{file}"
19
+ end
17
20
  end
18
21
 
19
- desc 'Run the notify (tail) example program'
20
- task :example_notify do
21
- ruby '-Ilib examples/example_notify.rb'
22
+ namespace :example do
23
+ desc 'Run the notify (tail) example program'
24
+ task :notify do
25
+ ruby '-Ilib examples/example_notify.rb'
26
+ end
22
27
 
23
- desc 'Run the read example program'
24
- task :example_read do
25
- ruby '-Ilib examples/example_read.rb'
28
+ desc 'Run the read example program'
29
+ task :read do
30
+ ruby '-Ilib examples/example_read.rb'
31
+ end
26
32
 
27
- desc 'Run the write example program'
28
- task :example_write do
29
- ruby '-Ilib examples/example_write.rb'
33
+ desc 'Run the write example program'
34
+ task :write do
35
+ ruby '-Ilib examples/example_write.rb'
36
+ end
37
+ end
30
38
 
31
- Rake::TestTask.new(:test) do |t|
32
- t.warning = true
33
- t.verbose = true
39
+ namespace :event_source do
40
+ desc 'Install the RubyMsg event source'
41
+ task :install do
42
+ sh "ruby -Ilib misc/install_msg.rb"
43
+ end
34
44
  end
35
45
 
36
- Rake::TestTask.new(:test_eventlog) do |t|
37
- t.warning = true
38
- t.verbose = true
39
- t.test_files = Dir['test/test_eventlog.rb']
46
+ Rake::TestTask.new do |t|
47
+ t.warning = true
48
+ t.verbose = true
40
49
  end
41
50
 
42
- Rake::TestTask.new(:test_mc) do |t|
43
- t.warning = true
44
- t.verbose = true
45
- t.test_files = Dir['test/test_mc.rb']
51
+ namespace :test do
52
+ Rake::TestTask.new(:eventlog) do |t|
53
+ t.warning = true
54
+ t.verbose = true
55
+ t.test_files = Dir['test/test_eventlog.rb']
56
+ end
57
+
58
+ Rake::TestTask.new(:mc) do |t|
59
+ t.warning = true
60
+ t.verbose = true
61
+ t.test_files = Dir['test/test_mc.rb']
62
+ end
46
63
  end
64
+
65
+ task :default => :test
@@ -8,11 +8,16 @@ require 'windows/synchronize'
8
8
  require 'windows/handle'
9
9
 
10
10
  class String
11
- # Return the portion of the string up to the first NULL character. This
12
- # was added for both speed and convenience.
13
- def nstrip
14
- self[ /^[^\0]*/ ]
15
- end
11
+ # Return the portion of the string up to the first NULL character. This
12
+ # was added for both speed and convenience.
13
+ def nstrip
14
+ if RUBY_VERSION.to_f >= 1.9
15
+ unless self.ascii_only?
16
+ self.force_encoding('BINARY')
17
+ end
18
+ end
19
+ self[ /^[^\0]*/ ]
20
+ end
16
21
  end
17
22
 
18
23
  # The Win32 module serves as a namespace only.
@@ -21,11 +26,11 @@ module Win32
21
26
  # The EventLog class encapsulates an Event Log source and provides methods
22
27
  # for interacting with that source.
23
28
  class EventLog
24
-
29
+
25
30
  # The EventLog::Error is raised in cases where interaction with the
26
31
  # event log should happen to fail for any reason.
27
32
  class Error < StandardError; end
28
-
33
+
29
34
  include Windows::Error
30
35
  include Windows::EventLog
31
36
  include Windows::Security
@@ -36,10 +41,10 @@ module Win32
36
41
  include Windows::Handle
37
42
  extend Windows::Error
38
43
  extend Windows::Registry
39
-
44
+
40
45
  # The version of the win32-eventlog library
41
- VERSION = '0.5.2'
42
-
46
+ VERSION = '0.5.3'
47
+
43
48
  # The log is read in chronological order, i.e. oldest to newest.
44
49
  FORWARDS_READ = EVENTLOG_FORWARDS_READ
45
50
 
@@ -55,7 +60,7 @@ module Win32
55
60
  SEQUENTIAL_READ = EVENTLOG_SEQUENTIAL_READ
56
61
 
57
62
  # Event types
58
-
63
+
59
64
  # Information event, an event that describes the successful operation
60
65
  # of an application, driver or service.
61
66
  SUCCESS = EVENTLOG_SUCCESS
@@ -81,72 +86,72 @@ module Win32
81
86
  AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE
82
87
 
83
88
  private
84
-
89
+
85
90
  # :stopdoc:
86
-
91
+
87
92
  BUFFER_SIZE = 1024 * 64
88
93
  MAX_SIZE = 256
89
94
  MAX_STRINGS = 16
90
95
  BASE_KEY = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
91
-
96
+
92
97
  # :startdoc:
93
98
 
94
99
  public
95
-
100
+
96
101
  # The EventLogStruct encapsulates a single event log record.
97
102
  EventLogStruct = Struct.new('EventLogStruct', :record_number,
98
103
  :time_generated, :time_written, :event_id, :event_type, :category,
99
104
  :source, :computer, :user, :string_inserts, :description
100
105
  )
101
-
106
+
102
107
  # The name of the event log source. This will typically be
103
108
  # 'Application', 'System' or 'Security', but could also refer to
104
109
  # a custom event log source.
105
- #
110
+ #
106
111
  attr_reader :source
107
-
112
+
108
113
  # The name of the server which the event log is reading from.
109
- #
114
+ #
110
115
  attr_reader :server
111
-
116
+
112
117
  # The name of the file used in the EventLog.open_backup method. This is
113
118
  # set to nil if the file was not opened using the EventLog.open_backup
114
119
  # method.
115
- #
120
+ #
116
121
  attr_reader :file
117
-
122
+
118
123
  # Opens a handle to the new EventLog +source+ on +server+, or the local
119
124
  # machine if no host is specified. Typically, your source will be
120
125
  # 'Application, 'Security' or 'System', although you can specify a
121
126
  # custom log file as well.
122
- #
127
+ #
123
128
  # If a custom, registered log file name cannot be found, the event
124
129
  # logging service opens the 'Application' log file. This is the
125
130
  # behavior of the underlying Windows function, not my own doing.
126
- #
131
+ #
127
132
  def initialize(source = 'Application', server = nil, file = nil)
128
133
  @source = source || 'Application' # In case of explicit nil
129
134
  @server = server
130
135
  @file = file
131
-
136
+
132
137
  # Avoid potential segfaults from win32-api
133
138
  raise TypeError unless @source.is_a?(String)
134
139
  raise TypeError unless @server.is_a?(String) if @server
135
-
140
+
136
141
  function = 'OpenEventLog()'
137
-
142
+
138
143
  if @file.nil?
139
144
  @handle = OpenEventLog(@server, @source)
140
145
  else
141
146
  @handle = OpenBackupEventLog(@server, @file)
142
147
  function = 'OpenBackupEventLog()'
143
148
  end
144
-
149
+
145
150
  if @handle == 0
146
151
  error = "#{function} failed: " + get_last_error
147
152
  raise Error, error
148
153
  end
149
-
154
+
150
155
  # Ensure the handle is closed at the end of a block
151
156
  if block_given?
152
157
  begin
@@ -156,33 +161,33 @@ module Win32
156
161
  end
157
162
  end
158
163
  end
159
-
164
+
160
165
  # Class method aliases
161
166
  class << self
162
167
  alias :open :new
163
168
  end
164
-
169
+
165
170
  # Nearly identical to EventLog.open, except that the source is a backup
166
171
  # file and not an event source (and there is no default).
167
- #
172
+ #
168
173
  def self.open_backup(file, source = 'Application', server = nil, &block)
169
174
  @file = file
170
175
  @source = source
171
176
  @server = server
172
-
177
+
173
178
  # Avoid potential segfaults from win32-api
174
179
  raise TypeError unless @file.is_a?(String)
175
180
  raise TypeError unless @source.is_a?(String)
176
- raise TypeError unless @server.is_a?(String) if @server
181
+ raise TypeError unless @server.is_a?(String) if @server
177
182
 
178
183
  self.new(source, server, file, &block)
179
184
  end
180
-
185
+
181
186
  # Adds an event source to the registry. Returns the disposition, which
182
187
  # is either REG_CREATED_NEW_KEY (1) or REG_OPENED_EXISTING_KEY (2).
183
188
  #
184
189
  # The following are valid keys:
185
- #
190
+ #
186
191
  # * source # Source name. Set to "Application" by default
187
192
  # * key_name # Name stored as the registry key
188
193
  # * category_count # Number of supported (custom) categories
@@ -201,10 +206,10 @@ module Win32
201
206
  # The +event_message_file+ and +category_message_file+ are typically,
202
207
  # though not necessarily, the same file. See the documentation on .mc files
203
208
  # for more details.
204
- #
209
+ #
205
210
  def self.add_event_source(args)
206
211
  raise TypeError unless args.is_a?(Hash)
207
-
212
+
208
213
  valid_keys = %w/
209
214
  source
210
215
  key_name
@@ -222,8 +227,8 @@ module Win32
222
227
  'source' => 'Application',
223
228
  'supported_types' => ERROR | WARN | INFO
224
229
  }
225
-
226
- # Validate the keys, and convert symbols and case to lowercase strings.
230
+
231
+ # Validate the keys, and convert symbols and case to lowercase strings.
227
232
  args.each{ |key, val|
228
233
  key = key.to_s.downcase
229
234
  unless valid_keys.include?(key)
@@ -231,17 +236,17 @@ module Win32
231
236
  end
232
237
  hash[key] = val
233
238
  }
234
-
239
+
235
240
  # The key_name must be specified
236
241
  unless hash['key_name']
237
242
  raise Error, 'no event_type specified'
238
243
  end
239
-
240
- hkey = [0].pack('L')
244
+
245
+ hkey = [0].pack('L')
241
246
  key = key_base + hash['source']
242
-
247
+
243
248
  disposition = [0].pack('L')
244
-
249
+
245
250
  rv = RegCreateKeyEx(
246
251
  HKEY_LOCAL_MACHINE,
247
252
  key,
@@ -252,16 +257,16 @@ module Win32
252
257
  nil,
253
258
  hkey,
254
259
  disposition
255
- )
256
-
260
+ )
261
+
257
262
  if rv != ERROR_SUCCESS
258
263
  error = 'RegCreateKeyEx() failed: ' + get_last_error
259
264
  raise Error, error
260
265
  end
261
-
262
- hkey = hkey.unpack('L')[0]
266
+
267
+ hkey = hkey.unpack('L')[0]
263
268
  data = "%SystemRoot%\\System32\\config\\#{hash['source']}.evt"
264
-
269
+
265
270
  begin
266
271
  rv = RegSetValueEx(
267
272
  hkey,
@@ -271,20 +276,20 @@ module Win32
271
276
  data,
272
277
  data.size
273
278
  )
274
-
279
+
275
280
  if rv != ERROR_SUCCESS
276
281
  error = 'RegSetValueEx() failed: ', get_last_error
277
282
  raise Error, error
278
283
  end
279
284
  ensure
280
- RegCloseKey(hkey)
285
+ RegCloseKey(hkey)
281
286
  end
282
-
287
+
283
288
  hkey = [0].pack('L')
284
289
  key = key_base << hash['source'] << "\\" << hash['key_name']
285
-
290
+
286
291
  disposition = [0].pack('L')
287
-
292
+
288
293
  begin
289
294
  rv = RegCreateKeyEx(
290
295
  HKEY_LOCAL_MACHINE,
@@ -297,16 +302,16 @@ module Win32
297
302
  hkey,
298
303
  disposition
299
304
  )
300
-
305
+
301
306
  if rv != ERROR_SUCCESS
302
307
  raise Error, 'RegCreateKeyEx() failed: ' + get_last_error
303
308
  end
304
-
309
+
305
310
  hkey = hkey.unpack('L')[0]
306
-
311
+
307
312
  if hash['category_count']
308
313
  data = [hash['category_count']].pack('L')
309
-
314
+
310
315
  rv = RegSetValueEx(
311
316
  hkey,
312
317
  'CategoryCount',
@@ -315,16 +320,16 @@ module Win32
315
320
  data,
316
321
  data.size
317
322
  )
318
-
323
+
319
324
  if rv != ERROR_SUCCESS
320
325
  error = 'RegSetValueEx() failed: ' + get_last_error
321
326
  raise Error, error
322
327
  end
323
328
  end
324
-
329
+
325
330
  if hash['category_message_file']
326
331
  data = File.expand_path(hash['category_message_file'])
327
-
332
+
328
333
  rv = RegSetValueEx(
329
334
  hkey,
330
335
  'CategoryMessageFile',
@@ -333,16 +338,16 @@ module Win32
333
338
  data,
334
339
  data.size
335
340
  )
336
-
341
+
337
342
  if rv != ERROR_SUCCESS
338
343
  error = 'RegSetValueEx() failed: ' + get_last_error
339
344
  raise Error, error
340
345
  end
341
346
  end
342
-
347
+
343
348
  if hash['event_message_file']
344
349
  data = File.expand_path(hash['event_message_file'])
345
-
350
+
346
351
  rv = RegSetValueEx(
347
352
  hkey,
348
353
  'EventMessageFile',
@@ -351,16 +356,16 @@ module Win32
351
356
  data,
352
357
  data.size
353
358
  )
354
-
359
+
355
360
  if rv != ERROR_SUCCESS
356
361
  error = 'RegSetValueEx() failed: ' + get_last_error
357
362
  raise Error, error
358
363
  end
359
364
  end
360
-
365
+
361
366
  if hash['parameter_message_file']
362
367
  data = File.expand_path(hash['parameter_message_file'])
363
-
368
+
364
369
  rv = RegSetValueEx(
365
370
  hkey,
366
371
  'ParameterMessageFile',
@@ -369,13 +374,13 @@ module Win32
369
374
  data,
370
375
  data.size
371
376
  )
372
-
377
+
373
378
  if rv != ERROR_SUCCESS
374
379
  error = 'RegSetValueEx() failed: ' + get_last_error
375
380
  raise Error, error
376
381
  end
377
- end
378
-
382
+ end
383
+
379
384
  data = [hash['supported_types']].pack('L')
380
385
 
381
386
  rv = RegSetValueEx(
@@ -386,7 +391,7 @@ module Win32
386
391
  data,
387
392
  data.size
388
393
  )
389
-
394
+
390
395
  if rv != ERROR_SUCCESS
391
396
  error = 'RegSetValueEx() failed: ' + get_last_error
392
397
  raise Error, error
@@ -394,13 +399,13 @@ module Win32
394
399
  ensure
395
400
  RegCloseKey(hkey)
396
401
  end
397
-
402
+
398
403
  disposition.unpack('L')[0]
399
404
  end
400
-
405
+
401
406
  # Backs up the event log to +file+. Note that you cannot backup to
402
407
  # a file that already exists or a Error will be raised.
403
- #
408
+ #
404
409
  def backup(file)
405
410
  raise TypeError unless file.is_a?(String)
406
411
  unless BackupEventLog(@handle, file)
@@ -409,99 +414,99 @@ module Win32
409
414
  end
410
415
  self
411
416
  end
412
-
417
+
413
418
  # Clears the EventLog. If +backup_file+ is provided, it backs up the
414
419
  # event log to that file first.
415
- #
420
+ #
416
421
  def clear(backup_file = nil)
417
422
  raise TypeError unless backup_file.is_a?(String) if backup_file
418
423
  backup_file = 0 unless backup_file
419
-
424
+
420
425
  unless ClearEventLog(@handle, backup_file)
421
426
  error = 'ClearEventLog() failed: ' + get_last_error
422
427
  raise Error
423
428
  end
424
-
429
+
425
430
  self
426
431
  end
427
-
432
+
428
433
  # Closes the EventLog handle. The handle is automatically closed for you
429
434
  # if you use the block form of EventLog.new.
430
- #
435
+ #
431
436
  def close
432
437
  CloseEventLog(@handle)
433
438
  end
434
-
439
+
435
440
  # Indicates whether or not the event log is full.
436
- #
441
+ #
437
442
  def full?
438
443
  buf = [0].pack('L') # dwFull
439
444
  needed = [0].pack('L')
440
-
445
+
441
446
  unless GetEventLogInformation(@handle, 0, buf, buf.size, needed)
442
447
  raise Error, 'GetEventLogInformation() failed: ' + get_last_error
443
448
  end
444
449
 
445
450
  buf[0,4].unpack('L')[0] != 0
446
451
  end
447
-
452
+
448
453
  # Returns the absolute record number of the oldest record. Note that
449
454
  # this is not guaranteed to be 1 because event log records can be
450
455
  # overwritten.
451
- #
456
+ #
452
457
  def oldest_record_number
453
458
  rec = [0].pack('L')
454
-
459
+
455
460
  unless GetOldestEventLogRecord(@handle, rec)
456
461
  error = 'GetOldestEventLogRecord() failed: ' + get_last_error
457
462
  raise Error, error
458
463
  end
459
-
464
+
460
465
  rec.unpack('L')[0]
461
466
  end
462
-
467
+
463
468
  # Returns the total number of records for the given event log.
464
- #
469
+ #
465
470
  def total_records
466
471
  total = [0].pack('L')
467
-
472
+
468
473
  unless GetNumberOfEventLogRecords(@handle, total)
469
474
  error = 'GetNumberOfEventLogRecords() failed: ' + get_last_error
470
475
  raise Error, error
471
476
  end
472
-
477
+
473
478
  total.unpack('L')[0]
474
479
  end
475
-
480
+
476
481
  # Yields an EventLogStruct every time a record is written to the event
477
482
  # log. Unlike EventLog#tail, this method breaks out of the block after
478
483
  # the event.
479
- #
484
+ #
480
485
  # Raises an Error if no block is provided.
481
- #
486
+ #
482
487
  def notify_change(&block)
483
488
  unless block_given?
484
489
  raise Error, 'block missing for notify_change()'
485
490
  end
486
-
491
+
487
492
  # Reopen the handle because the NotifyChangeEventLog() function will
488
493
  # choke after five or six reads otherwise.
489
494
  @handle = OpenEventLog(@server, @source)
490
-
495
+
491
496
  if @handle == 0
492
497
  error = 'OpenEventLog() failed: ' + get_last_error
493
498
  raise Error, error
494
499
  end
495
-
500
+
496
501
  event = CreateEvent(0, 0, 0, 0)
497
-
502
+
498
503
  unless NotifyChangeEventLog(@handle, event)
499
504
  error = 'NotifyChangeEventLog() failed: ' + get_last_error
500
505
  raise Error, error
501
506
  end
502
-
507
+
503
508
  wait_result = WaitForSingleObject(event, INFINITE)
504
-
509
+
505
510
  begin
506
511
  if wait_result == WAIT_FAILED
507
512
  error = 'WaitForSingleObject() failed: ' + get_last_error
@@ -513,33 +518,33 @@ module Win32
513
518
  ensure
514
519
  CloseHandle(event)
515
520
  end
516
-
521
+
517
522
  self
518
523
  end
519
-
524
+
520
525
  # Yields an EventLogStruct every time a record is written to the event
521
526
  # log, once every +frequency+ seconds. Unlike EventLog#notify_change,
522
527
  # this method does not break out of the block after the event. The read
523
528
  # +frequency+ is set to 5 seconds by default.
524
- #
529
+ #
525
530
  # Raises an Error if no block is provided.
526
- #
531
+ #
527
532
  # The delay between reads is due to the nature of the Windows event log.
528
533
  # It is not really designed to be tailed in the manner of a Unix syslog,
529
- # for example, in that not nearly as many events are typically recorded.
534
+ # for example, in that not nearly as many events are typically recorded.
530
535
  # It's just not designed to be polled that heavily.
531
536
  #
532
537
  def tail(frequency = 5)
533
538
  unless block_given?
534
539
  raise Error, 'block missing for tail()'
535
540
  end
536
-
537
- old_total = total_records()
541
+
542
+ old_total = total_records()
538
543
  flags = FORWARDS_READ | SEEK_READ
539
544
  rec_num = read_last_event.record_number
540
-
541
- while true
542
- new_total = total_records()
545
+
546
+ while true
547
+ new_total = total_records()
543
548
  if new_total != old_total
544
549
  rec_num = oldest_record_number() if full?
545
550
  read(flags, rec_num).each{ |log| yield log }
@@ -549,20 +554,20 @@ module Win32
549
554
  sleep frequency
550
555
  end
551
556
  end
552
-
557
+
553
558
  # Iterates over each record in the event log, yielding a EventLogStruct
554
559
  # for each record. The offset value is only used when used in
555
560
  # conjunction with the EventLog::SEEK_READ flag. Otherwise, it is
556
561
  # ignored. If no flags are specified, then the default flags are:
557
- #
562
+ #
558
563
  # EventLog::SEQUENTIAL_READ | EventLog::FORWARDS_READ
559
564
  #
560
565
  # Note that, if you're performing a SEEK_READ, then the offset must
561
566
  # refer to a record number that actually exists. The default of 0
562
567
  # may or may not work for your particular event log.
563
- #
568
+ #
564
569
  # The EventLogStruct struct contains the following members:
565
- #
570
+ #
566
571
  # * record_number # Fixnum
567
572
  # * time_generated # Time
568
573
  # * time_written # Time
@@ -574,16 +579,16 @@ module Win32
574
579
  # * user # String or nil
575
580
  # * description # String or nil
576
581
  # * string_inserts # An array of Strings or nil
577
- #
582
+ #
578
583
  # If no block is given the method returns an array of EventLogStruct's.
579
- #
584
+ #
580
585
  def read(flags = nil, offset = 0)
581
- buf = 0.chr * BUFFER_SIZE # 64k buffer
586
+ buf = 0.chr * BUFFER_SIZE # 64k buffer
582
587
  read = [0].pack('L')
583
588
  needed = [0].pack('L')
584
589
  array = []
585
590
  lkey = HKEY_LOCAL_MACHINE
586
-
591
+
587
592
  unless flags
588
593
  flags = FORWARDS_READ | SEQUENTIAL_READ
589
594
  end
@@ -595,27 +600,27 @@ module Win32
595
600
  end
596
601
  lkey = hkey.unpack('L').first
597
602
  end
598
-
603
+
599
604
  while ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed) ||
600
605
  GetLastError() == ERROR_INSUFFICIENT_BUFFER
601
-
606
+
602
607
  if GetLastError() == ERROR_INSUFFICIENT_BUFFER
603
608
  buf = (0.chr * buf.size) + (0.chr * needed.unpack('L')[0])
604
609
  unless ReadEventLog(@handle, flags, offset, buf, buf.size, read, needed)
605
610
  raise Error, get_last_error
606
611
  end
607
612
  end
608
-
613
+
609
614
  dwread = read.unpack('L')[0]
610
-
615
+
611
616
  while dwread > 0
612
617
  struct = EventLogStruct.new
613
618
  event_source = buf[56..-1].nstrip
614
619
  computer = buf[56 + event_source.length + 1..-1].nstrip
615
-
620
+
616
621
  user = get_user(buf)
617
622
  strings, desc = get_description(buf, event_source, lkey)
618
-
623
+
619
624
  struct.source = event_source
620
625
  struct.computer = computer
621
626
  struct.record_number = buf[8,4].unpack('L')[0]
@@ -627,7 +632,7 @@ module Win32
627
632
  struct.category = buf[28,2].unpack('S')[0]
628
633
  struct.string_inserts = strings
629
634
  struct.description = desc
630
-
635
+
631
636
  struct.freeze # This is read-only information
632
637
 
633
638
  if block_given?
@@ -635,30 +640,30 @@ module Win32
635
640
  else
636
641
  array.push(struct)
637
642
  end
638
-
643
+
639
644
  if flags & EVENTLOG_BACKWARDS_READ > 0
640
645
  offset = buf[8,4].unpack('L')[0] - 1
641
646
  else
642
647
  offset = buf[8,4].unpack('L')[0] + 1
643
648
  end
644
-
649
+
645
650
  length = buf[0,4].unpack('L')[0] # Length
646
651
 
647
652
  dwread -= length
648
653
  buf = buf[length..-1]
649
654
  end
650
-
655
+
651
656
  buf = 0.chr * BUFFER_SIZE
652
657
  read = [0].pack('L')
653
658
  end
654
-
659
+
655
660
  block_given? ? nil : array
656
661
  end
657
-
662
+
658
663
  # This class method is nearly identical to the EventLog#read instance
659
664
  # method, except that it takes a +source+ and +server+ as the first two
660
665
  # arguments.
661
- #
666
+ #
662
667
  def self.read(source='Application', server=nil, flags=nil, offset=0)
663
668
  self.new(source, server){ |log|
664
669
  if block_given?
@@ -670,28 +675,28 @@ module Win32
670
675
  end
671
676
 
672
677
  # Writes an event to the event log. The following are valid keys:
673
- #
678
+ #
674
679
  # * source # Event log source name. Defaults to "Application"
675
680
  # * event_id # Event ID (defined in event message file)
676
681
  # * category # Event category (defined in category message file)
677
682
  # * data # String that is written to the log
678
683
  # * event_type # Type of event, e.g. EventLog::ERROR, etc.
679
- #
684
+ #
680
685
  # The +event_type+ keyword is the only mandatory keyword. The others are
681
686
  # optional. Although the +source+ defaults to "Application", I
682
687
  # recommend that you create an application specific event source and use
683
688
  # that instead. See the 'EventLog.add_event_source' method for more
684
689
  # details.
685
- #
690
+ #
686
691
  # The +event_id+ and +category+ values are defined in the message
687
692
  # file(s) that you created for your application. See the tutorial.txt
688
693
  # file for more details on how to create a message file.
689
- #
694
+ #
690
695
  # An ArgumentError is raised if you attempt to use an invalid key.
691
- #
696
+ #
692
697
  def report_event(args)
693
698
  raise TypeError unless args.is_a?(Hash)
694
-
699
+
695
700
  valid_keys = %w/source event_id category data event_type/
696
701
  num_strings = 0
697
702
 
@@ -702,8 +707,8 @@ module Win32
702
707
  'category' => 0,
703
708
  'data' => 0
704
709
  }
705
-
706
- # Validate the keys, and convert symbols and case to lowercase strings.
710
+
711
+ # Validate the keys, and convert symbols and case to lowercase strings.
707
712
  args.each{ |key, val|
708
713
  key = key.to_s.downcase
709
714
  unless valid_keys.include?(key)
@@ -711,19 +716,19 @@ module Win32
711
716
  end
712
717
  hash[key] = val
713
718
  }
714
-
719
+
715
720
  # The event_type must be specified
716
721
  unless hash['event_type']
717
722
  raise Error, 'no event_type specified'
718
723
  end
719
-
724
+
720
725
  handle = RegisterEventSource(@server, hash['source'])
721
-
726
+
722
727
  if handle == 0
723
728
  error = 'RegisterEventSource() failed: ' + get_last_error
724
729
  raise Error, error
725
730
  end
726
-
731
+
727
732
  if hash['data'].is_a?(String)
728
733
  data = hash['data'] << 0.chr
729
734
  data = [data].pack('p*')
@@ -732,7 +737,7 @@ module Win32
732
737
  data = 0
733
738
  num_strings = 0
734
739
  end
735
-
740
+
736
741
  bool = ReportEvent(
737
742
  handle,
738
743
  hash['event_type'],
@@ -744,29 +749,29 @@ module Win32
744
749
  data,
745
750
  0
746
751
  )
747
-
752
+
748
753
  unless bool
749
754
  error = 'ReportEvent() failed: ' + get_last_error
750
755
  raise Error, error
751
756
  end
752
-
757
+
753
758
  self
754
759
  end
755
-
760
+
756
761
  alias :write :report_event
757
-
762
+
758
763
  private
759
-
764
+
760
765
  # A private method that reads the last event log record.
761
- #
766
+ #
762
767
  def read_last_event(handle=@handle, source=@source, server=@server)
763
768
  buf = 0.chr * BUFFER_SIZE # 64k buffer
764
769
  read = [0].pack('L')
765
770
  needed = [0].pack('L')
766
771
  lkey = HKEY_LOCAL_MACHINE
767
-
772
+
768
773
  flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ
769
-
774
+
770
775
  unless ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed)
771
776
  error = GetLastError()
772
777
  if error == ERROR_INSUFFICIENT_BUFFER
@@ -786,13 +791,13 @@ module Win32
786
791
  end
787
792
  lkey = hkey.unpack('L').first
788
793
  end
789
-
794
+
790
795
  event_source = buf[56..-1].nstrip
791
796
  computer = buf[56 + event_source.length + 1..-1].nstrip
792
797
  event_type = get_event_type(buf[24,2].unpack('S')[0])
793
798
  user = get_user(buf)
794
799
  strings, desc = get_description(buf, event_source, lkey)
795
-
800
+
796
801
  struct = EventLogStruct.new
797
802
  struct.source = event_source
798
803
  struct.computer = computer
@@ -805,16 +810,16 @@ module Win32
805
810
  struct.category = buf[28,2].unpack('S')[0]
806
811
  struct.string_inserts = strings
807
812
  struct.description = desc
808
-
813
+
809
814
  struct.freeze # This is read-only information
810
-
815
+
811
816
  struct
812
817
  end
813
-
818
+
814
819
  # Private method that retrieves the user name based on data in the
815
820
  # EVENTLOGRECORD buffer.
816
- #
817
- def get_user(buf)
821
+ #
822
+ def get_user(buf)
818
823
  return nil if buf[40,4].unpack('L')[0] <= 0 # UserSidLength
819
824
 
820
825
  name = 0.chr * MAX_SIZE
@@ -822,9 +827,9 @@ module Win32
822
827
  domain = 0.chr * MAX_SIZE
823
828
  domain_size = [domain.size].pack('L')
824
829
  snu = 0.chr * 4
825
-
830
+
826
831
  offset = buf[44,4].unpack('L')[0] # UserSidOffset
827
-
832
+
828
833
  val = LookupAccountSid(
829
834
  @server,
830
835
  [buf].pack('P').unpack('L')[0] + offset,
@@ -834,14 +839,14 @@ module Win32
834
839
  domain_size,
835
840
  snu
836
841
  )
837
-
842
+
838
843
  # Return nil if the lookup failed
839
844
  return val ? name.nstrip : nil
840
845
  end
841
-
846
+
842
847
  # Private method that converts a numeric event type into a human
843
848
  # readable string.
844
- #
849
+ #
845
850
  def get_event_type(event)
846
851
  case event
847
852
  when EVENTLOG_ERROR_TYPE
@@ -858,11 +863,11 @@ module Win32
858
863
  nil
859
864
  end
860
865
  end
861
-
866
+
862
867
  # Private method that gets the string inserts (Array) and the full
863
868
  # event description (String) based on data from the EVENTLOGRECORD
864
869
  # buffer.
865
- #
870
+ #
866
871
  def get_description(rec, event_source, lkey)
867
872
  str = rec[rec[36,4].unpack('L')[0] .. -1]
868
873
  num = rec[26,2].unpack('S')[0] # NumStrings
@@ -892,7 +897,7 @@ module Win32
892
897
  hkey2 = [0].pack('L')
893
898
  key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
894
899
  key << "WINEVT\\Publishers\\#{guid}"
895
-
900
+
896
901
  if RegOpenKeyEx(lkey, key, 0, KEY_READ|0x100, hkey2) == 0
897
902
  hkey2 = hkey2.unpack('L')[0]
898
903
  value = 'ParameterMessageFile'
@@ -905,7 +910,7 @@ module Win32
905
910
  ExpandEnvironmentStrings(file, exe, exe.size)
906
911
  param_exe = exe.nstrip
907
912
  end
908
-
913
+
909
914
  value = 'MessageFileName'
910
915
  file = 0.chr * MAX_SIZE
911
916
  size = [file.length].pack('L')
@@ -916,7 +921,7 @@ module Win32
916
921
  ExpandEnvironmentStrings(file, exe, exe.size)
917
922
  message_exe = exe.nstrip
918
923
  end
919
-
924
+
920
925
  RegCloseKey(hkey2)
921
926
  end
922
927
  else
@@ -940,9 +945,9 @@ module Win32
940
945
  exe = 0.chr * MAX_SIZE
941
946
  ExpandEnvironmentStrings(file, exe, exe.size)
942
947
  message_exe = exe.nstrip
943
- end
944
- end
945
-
948
+ end
949
+ end
950
+
946
951
  RegCloseKey(hkey)
947
952
  elsif defined? EvtOpenPublisherMetadata # Vista or later
948
953
  pubMetadata = EvtOpenPublisherMetadata(
@@ -952,11 +957,11 @@ module Win32
952
957
  1024, # Default LCID
953
958
  0
954
959
  )
955
-
960
+
956
961
  if pubMetadata > 0
957
962
  buf2 = 0.chr * 8192
958
963
  val = 0.chr*4
959
-
964
+
960
965
  EvtGetPublisherMetadataProperty(
961
966
  pubMetadata,
962
967
  2, # EvtPublisherMetadataParameterFilePath
@@ -965,7 +970,7 @@ module Win32
965
970
  buf2,
966
971
  val
967
972
  )
968
-
973
+
969
974
  file = wide_to_multi(buf2[16..-1])
970
975
  exe = 0.chr * MAX_SIZE
971
976
  ExpandEnvironmentStrings(file, exe, exe.size)
@@ -973,7 +978,7 @@ module Win32
973
978
 
974
979
  buf2 = 0.chr * 8192
975
980
  val = 0.chr*4
976
-
981
+
977
982
  EvtGetPublisherMetadataProperty(
978
983
  pubMetadata,
979
984
  3, # EvtPublisherMetadataMessageFilePath
@@ -982,7 +987,7 @@ module Win32
982
987
  buf2,
983
988
  val
984
989
  )
985
-
990
+
986
991
  file = wide_to_multi(buf2[16..-1])
987
992
  exe = 0.chr * MAX_SIZE
988
993
  ExpandEnvironmentStrings(file, exe, exe.size)
@@ -1015,7 +1020,7 @@ module Win32
1015
1020
  buf.size,
1016
1021
  v
1017
1022
  )
1018
-
1023
+
1019
1024
  if res == 0
1020
1025
  event_id = 0xB0000000 | event_id
1021
1026
  res = FormatMessage(
@@ -1052,7 +1057,7 @@ module Win32
1052
1057
  )
1053
1058
 
1054
1059
  event_id = rec[20,4].unpack('L')[0]
1055
-
1060
+
1056
1061
  if hmodule != 0
1057
1062
  res = FormatMessage(
1058
1063
  FORMAT_MESSAGE_FROM_HMODULE |
@@ -1064,10 +1069,10 @@ module Win32
1064
1069
  buf.size,
1065
1070
  nil
1066
1071
  )
1067
-
1072
+
1068
1073
  if res == 0
1069
1074
  event_id = 0xB0000000 | event_id
1070
-
1075
+
1071
1076
  res = FormatMessage(
1072
1077
  FORMAT_MESSAGE_FROM_HMODULE |
1073
1078
  FORMAT_MESSAGE_IGNORE_INSERTS,
@@ -1079,7 +1084,7 @@ module Win32
1079
1084
  nil
1080
1085
  )
1081
1086
  end
1082
-
1087
+
1083
1088
  FreeLibrary(hmodule)
1084
1089
  break if buf.nstrip != "" # All messages read
1085
1090
  end
@@ -1098,7 +1103,7 @@ module Win32
1098
1103
  [x + 0.chr].pack('P').unpack('L')[0]
1099
1104
  }.pack('L*')
1100
1105
  end
1101
-
1106
+
1102
1107
  message_exe.split(';').each{ |file|
1103
1108
  hmodule = LoadLibraryEx(
1104
1109
  file,
@@ -1119,10 +1124,10 @@ module Win32
1119
1124
  buf.size,
1120
1125
  va_list_ptr
1121
1126
  )
1122
-
1127
+
1123
1128
  if res == 0
1124
1129
  event_id = 0xB0000000 | event_id
1125
-
1130
+
1126
1131
  res = FormatMessage(
1127
1132
  FORMAT_MESSAGE_FROM_HMODULE |
1128
1133
  FORMAT_MESSAGE_ARGUMENT_ARRAY,
@@ -1147,6 +1152,6 @@ module Win32
1147
1152
  end
1148
1153
 
1149
1154
  [va_list0, buf.nstrip]
1150
- end
1155
+ end
1151
1156
  end
1152
1157
  end