win32-eventlog 0.5.2 → 0.5.3

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/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