winevt_c 0.7.3 → 0.9.1

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.
@@ -0,0 +1,425 @@
1
+ #include <winevt_c.h>
2
+
3
+ /* clang-format off */
4
+ /*
5
+ * Document-class: Winevt::EventLog::Session
6
+ *
7
+ * Manage Session information for Windows EventLog.
8
+ *
9
+ * @example
10
+ * require 'winevt'
11
+ *
12
+ * @session = Winevt::EventLog::Session.new("127.0.0.1")
13
+ *
14
+ * @session.domain = "<EXAMPLEGROUP>"
15
+ * @session.username = "<username>"
16
+ * @session.password = "<password>"
17
+ * # Then pass @session veriable into Winevt::EventLog::Query or
18
+ * # Winevt::EventLog::Subscribe#subscribe
19
+ * @query = Winevt::EventLog::Query.new(
20
+ * "Application",
21
+ * "*[System[(Level <= 3) and TimeCreated[timediff(@SystemTime) <= 86400000]]]",
22
+ * @session
23
+ * )
24
+ * # some stuff.
25
+ *
26
+ * @subscribe = Winevt::EventLog::Subscribe.new
27
+ * @subscribe.subscribe(
28
+ * "Application",
29
+ * "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]",
30
+ * @session
31
+ * )
32
+ * # And some stuff.
33
+ * @since v0.9.0
34
+ */
35
+ /* clang-format on */
36
+
37
+ VALUE rb_cSession;
38
+ VALUE rb_cRpcLoginFlag;
39
+
40
+ static void session_free(void* ptr);
41
+
42
+ static const rb_data_type_t rb_winevt_session_type = { "winevt/session",
43
+ {
44
+ 0,
45
+ session_free,
46
+ 0,
47
+ },
48
+ NULL,
49
+ NULL,
50
+ RUBY_TYPED_FREE_IMMEDIATELY };
51
+
52
+ static void
53
+ session_free(void* ptr)
54
+ {
55
+ struct WinevtSession* winevtSession = (struct WinevtSession*)ptr;
56
+
57
+ if (winevtSession->server)
58
+ free(winevtSession->server);
59
+ if (winevtSession->domain)
60
+ free(winevtSession->domain);
61
+ if (winevtSession->username)
62
+ free(winevtSession->username);
63
+ if (winevtSession->password)
64
+ free(winevtSession->password);
65
+
66
+ xfree(ptr);
67
+ }
68
+
69
+ static VALUE
70
+ rb_winevt_session_alloc(VALUE klass)
71
+ {
72
+ VALUE obj;
73
+ struct WinevtSession* winevtSession;
74
+ obj = TypedData_Make_Struct(
75
+ klass, struct WinevtSession, &rb_winevt_session_type, winevtSession);
76
+ return obj;
77
+ }
78
+
79
+ /*
80
+ * Initalize Session class.
81
+ *
82
+ * @overload initialize(server, domain=nil, username=nil, password=nil, flags=Winevt::EventLog::Session::RpcLoginFlag::AuthDefault)
83
+ * @param server [String] Server ip address or fqdn.
84
+ * @param domain [String] Domain name.
85
+ * @param username [String] username on remote server.
86
+ * @param password [String] Remote server user password.
87
+ * @param flags [Integer] Flags for authentication method choices.
88
+ * @return [Session]
89
+ *
90
+ */
91
+
92
+ static VALUE
93
+ rb_winevt_session_initialize(VALUE self)
94
+ {
95
+ struct WinevtSession* winevtSession;
96
+
97
+ TypedData_Get_Struct(
98
+ self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
99
+
100
+ winevtSession->server = NULL;
101
+ winevtSession->domain = NULL;
102
+ winevtSession->username = NULL;
103
+ winevtSession->password = NULL;
104
+ winevtSession->flags = EvtRpcLoginAuthDefault;
105
+
106
+ return Qnil;
107
+ }
108
+
109
+ /*
110
+ * This method returns server for remoting access.
111
+ *
112
+ * @return [String]
113
+ */
114
+ static VALUE
115
+ rb_winevt_session_get_server(VALUE self)
116
+ {
117
+ struct WinevtSession* winevtSession;
118
+
119
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
120
+
121
+ if (winevtSession->server) {
122
+ return wstr_to_rb_str(CP_UTF8, winevtSession->server, -1);
123
+ } else {
124
+ return rb_str_new2("(NULL)");
125
+ }
126
+ }
127
+
128
+ /*
129
+ * This method specifies server for remoting access.
130
+ *
131
+ * @param rb_server [String] server
132
+ */
133
+ static VALUE
134
+ rb_winevt_session_set_server(VALUE self, VALUE rb_server)
135
+ {
136
+ struct WinevtSession* winevtSession;
137
+ DWORD len;
138
+ VALUE vserverBuf;
139
+ PWSTR wServer;
140
+
141
+ Check_Type(rb_server, T_STRING);
142
+
143
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
144
+
145
+ len =
146
+ MultiByteToWideChar(CP_UTF8, 0,
147
+ RSTRING_PTR(rb_server), RSTRING_LEN(rb_server),
148
+ NULL, 0);
149
+ wServer = ALLOCV_N(WCHAR, vserverBuf, len + 1);
150
+ MultiByteToWideChar(CP_UTF8, 0,
151
+ RSTRING_PTR(rb_server), RSTRING_LEN(rb_server),
152
+ wServer, len);
153
+ winevtSession->server = _wcsdup(wServer);
154
+ wServer[len] = L'\0';
155
+
156
+ ALLOCV_END(vserverBuf);
157
+
158
+ return Qnil;
159
+ }
160
+
161
+ /*
162
+ * This method returns domain for remoting access.
163
+ *
164
+ * @return [String]
165
+ */
166
+ static VALUE
167
+ rb_winevt_session_get_domain(VALUE self)
168
+ {
169
+ struct WinevtSession* winevtSession;
170
+
171
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
172
+
173
+ if (winevtSession->domain) {
174
+ return wstr_to_rb_str(CP_UTF8, winevtSession->domain, -1);
175
+ } else {
176
+ return rb_str_new2("(NULL)");
177
+ }
178
+ }
179
+
180
+ /*
181
+ * This method specifies domain for remoting access.
182
+ *
183
+ * @param rb_domain [String] domain
184
+ */
185
+ static VALUE
186
+ rb_winevt_session_set_domain(VALUE self, VALUE rb_domain)
187
+ {
188
+ struct WinevtSession* winevtSession;
189
+ DWORD len;
190
+ VALUE vdomainBuf;
191
+ PWSTR wDomain;
192
+
193
+ Check_Type(rb_domain, T_STRING);
194
+
195
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
196
+
197
+ len =
198
+ MultiByteToWideChar(CP_UTF8, 0,
199
+ RSTRING_PTR(rb_domain), RSTRING_LEN(rb_domain),
200
+ NULL, 0);
201
+ wDomain = ALLOCV_N(WCHAR, vdomainBuf, len + 1);
202
+ MultiByteToWideChar(CP_UTF8, 0,
203
+ RSTRING_PTR(rb_domain), RSTRING_LEN(rb_domain),
204
+ wDomain, len);
205
+ wDomain[len] = L'\0';
206
+
207
+ winevtSession->domain = _wcsdup(wDomain);
208
+
209
+ ALLOCV_END(vdomainBuf);
210
+
211
+ return Qnil;
212
+ }
213
+
214
+ /*
215
+ * This method returns username for remoting access.
216
+ *
217
+ * @return [String]
218
+ */
219
+ static VALUE
220
+ rb_winevt_session_get_username(VALUE self)
221
+ {
222
+ struct WinevtSession* winevtSession;
223
+
224
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
225
+
226
+ if (winevtSession->username) {
227
+ return wstr_to_rb_str(CP_UTF8, winevtSession->username, -1);
228
+ } else {
229
+ return rb_str_new2("(NULL)");
230
+ }
231
+ }
232
+
233
+ /*
234
+ * This method specifies username for remoting access.
235
+ *
236
+ * @param rb_username [String] username
237
+ */
238
+ static VALUE
239
+ rb_winevt_session_set_username(VALUE self, VALUE rb_username)
240
+ {
241
+ struct WinevtSession* winevtSession;
242
+ DWORD len;
243
+ VALUE vusernameBuf;
244
+ PWSTR wUsername;
245
+
246
+ Check_Type(rb_username, T_STRING);
247
+
248
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
249
+
250
+ len =
251
+ MultiByteToWideChar(CP_UTF8, 0,
252
+ RSTRING_PTR(rb_username), RSTRING_LEN(rb_username),
253
+ NULL, 0);
254
+ wUsername = ALLOCV_N(WCHAR, vusernameBuf, len + 1);
255
+ MultiByteToWideChar(CP_UTF8, 0,
256
+ RSTRING_PTR(rb_username), RSTRING_LEN(rb_username),
257
+ wUsername, len);
258
+ wUsername[len] = L'\0';
259
+
260
+ winevtSession->username = _wcsdup(wUsername);
261
+
262
+ ALLOCV_END(vusernameBuf);
263
+
264
+ return Qnil;
265
+ }
266
+
267
+ /*
268
+ * This method returns password for remoting access.
269
+ *
270
+ * @return [String]
271
+ */
272
+ static VALUE
273
+ rb_winevt_session_get_password(VALUE self)
274
+ {
275
+ struct WinevtSession* winevtSession;
276
+
277
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
278
+
279
+ if (winevtSession->password) {
280
+ return wstr_to_rb_str(CP_UTF8, winevtSession->password, -1);
281
+ } else {
282
+ return rb_str_new2("(NULL)");
283
+ }
284
+ }
285
+
286
+ /*
287
+ * This method specifies password for remoting access.
288
+ *
289
+ * @param rb_password [String] password
290
+ */
291
+ static VALUE
292
+ rb_winevt_session_set_password(VALUE self, VALUE rb_password)
293
+ {
294
+ struct WinevtSession* winevtSession;
295
+ DWORD len;
296
+ VALUE vpasswordBuf;
297
+ PWSTR wPassword;
298
+
299
+ Check_Type(rb_password, T_STRING);
300
+
301
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
302
+
303
+ len =
304
+ MultiByteToWideChar(CP_UTF8, 0,
305
+ RSTRING_PTR(rb_password), RSTRING_LEN(rb_password),
306
+ NULL, 0);
307
+ wPassword = ALLOCV_N(WCHAR, vpasswordBuf, len + 1);
308
+ MultiByteToWideChar(CP_UTF8, 0,
309
+ RSTRING_PTR(rb_password), RSTRING_LEN(rb_password),
310
+ wPassword, len);
311
+ wPassword[len] = L'\0';
312
+
313
+ winevtSession->password = _wcsdup(wPassword);
314
+
315
+ ALLOCV_END(vpasswordBuf);
316
+
317
+ return Qnil;
318
+ }
319
+
320
+ /*
321
+ * This method returns flags for remoting access.
322
+ *
323
+ * @return [Integer]
324
+ */
325
+ static VALUE
326
+ rb_winevt_session_get_flags(VALUE self)
327
+ {
328
+ struct WinevtSession* winevtSession;
329
+
330
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
331
+
332
+ return LONG2NUM(winevtSession->flags);
333
+ }
334
+
335
+ static DWORD
336
+ get_session_rpc_login_flag_from_cstr(char* flag_str)
337
+ {
338
+ if (strcmp(flag_str, "default") == 0)
339
+ return EvtRpcLoginAuthDefault;
340
+ else if (strcmp(flag_str, "negociate") == 0)
341
+ return EvtRpcLoginAuthNegotiate;
342
+ else if (strcmp(flag_str, "kerberos") == 0)
343
+ return EvtRpcLoginAuthKerberos;
344
+ else if (strcmp(flag_str, "ntlm") == 0)
345
+ return EvtRpcLoginAuthNTLM;
346
+ else
347
+ rb_raise(rb_eArgError, "Unknown rpc login flag: %s", flag_str);
348
+
349
+ return 0;
350
+ }
351
+
352
+
353
+ /*
354
+ * This method specifies flags for remoting access.
355
+ *
356
+ * @param rb_flags [Integer] flags
357
+ */
358
+ static VALUE
359
+ rb_winevt_session_set_flags(VALUE self, VALUE rb_flags)
360
+ {
361
+ struct WinevtSession* winevtSession;
362
+ EVT_RPC_LOGIN_FLAGS flags = EvtRpcLoginAuthDefault;
363
+
364
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
365
+
366
+ switch(TYPE(rb_flags)) {
367
+ case T_SYMBOL:
368
+ flags = get_session_rpc_login_flag_from_cstr(RSTRING_PTR(rb_sym2str(rb_flags)));
369
+ break;
370
+ case T_STRING:
371
+ flags = get_session_rpc_login_flag_from_cstr(StringValuePtr(rb_flags));
372
+ break;
373
+ case T_FIXNUM:
374
+ flags = NUM2LONG(rb_flags);
375
+ break;
376
+ default:
377
+ rb_raise(rb_eArgError, "Expected Symbol, String or Fixnum in flags");
378
+ }
379
+ winevtSession->flags = flags;
380
+
381
+ return Qnil;
382
+ }
383
+
384
+ void
385
+ Init_winevt_session(VALUE rb_cEventLog)
386
+ {
387
+ rb_cSession = rb_define_class_under(rb_cEventLog, "Session", rb_cObject);
388
+
389
+ rb_define_alloc_func(rb_cSession, rb_winevt_session_alloc);
390
+
391
+ rb_cRpcLoginFlag = rb_define_module_under(rb_cSession, "RpcLoginFlag");
392
+
393
+ rb_define_method(rb_cSession, "initialize", rb_winevt_session_initialize, 0);
394
+ rb_define_method(rb_cSession, "server", rb_winevt_session_get_server, 0);
395
+ rb_define_method(rb_cSession, "server=", rb_winevt_session_set_server, 1);
396
+ rb_define_method(rb_cSession, "domain", rb_winevt_session_get_domain, 0);
397
+ rb_define_method(rb_cSession, "domain=", rb_winevt_session_set_domain, 1);
398
+ rb_define_method(rb_cSession, "username", rb_winevt_session_get_username, 0);
399
+ rb_define_method(rb_cSession, "username=", rb_winevt_session_set_username, 1);
400
+ rb_define_method(rb_cSession, "password", rb_winevt_session_get_password, 0);
401
+ rb_define_method(rb_cSession, "password=", rb_winevt_session_set_password, 1);
402
+ rb_define_method(rb_cSession, "flags", rb_winevt_session_get_flags, 0);
403
+ rb_define_method(rb_cSession, "flags=", rb_winevt_session_set_flags, 1);
404
+
405
+ /*
406
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthDefault
407
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
408
+ */
409
+ rb_define_const(rb_cRpcLoginFlag, "AuthDefault", LONG2NUM(EvtRpcLoginAuthDefault));
410
+ /*
411
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthNegociate
412
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
413
+ */
414
+ rb_define_const(rb_cRpcLoginFlag, "AuthNegociate", LONG2NUM(EvtRpcLoginAuthNegotiate));
415
+ /*
416
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthKerberos
417
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
418
+ */
419
+ rb_define_const(rb_cRpcLoginFlag, "AuthKerberos", LONG2NUM(EvtRpcLoginAuthKerberos));
420
+ /*
421
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthNTLM
422
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
423
+ */
424
+ rb_define_const(rb_cRpcLoginFlag, "AuthNTLM", LONG2NUM(EvtRpcLoginAuthNTLM));
425
+ }
@@ -39,23 +39,42 @@ static const rb_data_type_t rb_winevt_subscribe_type = { "winevt/subscribe",
39
39
  RUBY_TYPED_FREE_IMMEDIATELY };
40
40
 
41
41
  static void
42
- subscribe_free(void* ptr)
42
+ close_handles(struct WinevtSubscribe* winevtSubscribe)
43
43
  {
44
- struct WinevtSubscribe* winevtSubscribe = (struct WinevtSubscribe*)ptr;
45
- if (winevtSubscribe->signalEvent)
44
+ if (winevtSubscribe->signalEvent) {
46
45
  CloseHandle(winevtSubscribe->signalEvent);
46
+ winevtSubscribe->signalEvent = NULL;
47
+ }
47
48
 
48
- if (winevtSubscribe->subscription)
49
+ if (winevtSubscribe->subscription) {
49
50
  EvtClose(winevtSubscribe->subscription);
51
+ winevtSubscribe->subscription = NULL;
52
+ }
50
53
 
51
- if (winevtSubscribe->bookmark)
54
+ if (winevtSubscribe->bookmark) {
52
55
  EvtClose(winevtSubscribe->bookmark);
56
+ winevtSubscribe->bookmark = NULL;
57
+ }
53
58
 
54
59
  for (int i = 0; i < winevtSubscribe->count; i++) {
55
60
  if (winevtSubscribe->hEvents[i]) {
56
61
  EvtClose(winevtSubscribe->hEvents[i]);
62
+ winevtSubscribe->hEvents[i] = NULL;
57
63
  }
58
64
  }
65
+ winevtSubscribe->count = 0;
66
+
67
+ if (winevtSubscribe->remoteHandle) {
68
+ EvtClose(winevtSubscribe->remoteHandle);
69
+ winevtSubscribe->remoteHandle = NULL;
70
+ }
71
+ }
72
+
73
+ static void
74
+ subscribe_free(void* ptr)
75
+ {
76
+ struct WinevtSubscribe* winevtSubscribe = (struct WinevtSubscribe*)ptr;
77
+ close_handles(winevtSubscribe);
59
78
 
60
79
  xfree(ptr);
61
80
  }
@@ -90,6 +109,7 @@ rb_winevt_subscribe_initialize(VALUE self)
90
109
  winevtSubscribe->renderAsXML = TRUE;
91
110
  winevtSubscribe->readExistingEvents = TRUE;
92
111
  winevtSubscribe->preserveQualifiers = FALSE;
112
+ winevtSubscribe->localeInfo = &default_locale;
93
113
 
94
114
  return Qnil;
95
115
  }
@@ -131,23 +151,27 @@ rb_winevt_subscribe_read_existing_events_p(VALUE self)
131
151
  /*
132
152
  * Subscribe into a Windows EventLog channel.
133
153
  *
134
- * @overload subscribe(path, query, options={})
154
+ * @overload subscribe(path, query, bookmark=nil, session=nil)
135
155
  * @param path [String] Subscribe Channel
136
156
  * @param query [String] Query string for channel
137
- * @option options [Bookmark] bookmark Bookmark class instance.
157
+ * @param bookmark [Bookmark] bookmark Bookmark class instance.
158
+ * @param session [Session] Session information for remoting access.
138
159
  * @return [Boolean]
139
160
  *
140
161
  */
141
162
  static VALUE
142
163
  rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
143
164
  {
144
- VALUE rb_path, rb_query, rb_bookmark;
165
+ VALUE rb_path, rb_query, rb_bookmark, rb_session;
145
166
  EVT_HANDLE hSubscription = NULL, hBookmark = NULL;
146
167
  HANDLE hSignalEvent;
168
+ EVT_HANDLE hRemoteHandle = NULL;
147
169
  DWORD len, flags = 0L;
170
+ DWORD err = ERROR_SUCCESS;
148
171
  VALUE wpathBuf, wqueryBuf, wBookmarkBuf;
149
172
  PWSTR path, query, bookmarkXml;
150
173
  DWORD status = ERROR_SUCCESS;
174
+ struct WinevtSession* winevtSession;
151
175
  struct WinevtSubscribe* winevtSubscribe;
152
176
 
153
177
  hSignalEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -155,7 +179,7 @@ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
155
179
  TypedData_Get_Struct(
156
180
  self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
157
181
 
158
- rb_scan_args(argc, argv, "21", &rb_path, &rb_query, &rb_bookmark);
182
+ rb_scan_args(argc, argv, "22", &rb_path, &rb_query, &rb_bookmark, &rb_session);
159
183
  Check_Type(rb_path, T_STRING);
160
184
  Check_Type(rb_query, T_STRING);
161
185
 
@@ -178,6 +202,19 @@ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
178
202
  raise_system_error(rb_eWinevtQueryError, status);
179
203
  }
180
204
  }
205
+ if (rb_obj_is_kind_of(rb_session, rb_cSession)) {
206
+ winevtSession = EventSession(rb_session);
207
+ hRemoteHandle = connect_to_remote(winevtSession->server,
208
+ winevtSession->domain,
209
+ winevtSession->username,
210
+ winevtSession->password,
211
+ winevtSession->flags);
212
+
213
+ err = GetLastError();
214
+ if (err != ERROR_SUCCESS) {
215
+ raise_system_error(rb_eRuntimeError, err);
216
+ }
217
+ }
181
218
 
182
219
  // path : To wide char
183
220
  len =
@@ -203,7 +240,7 @@ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
203
240
  }
204
241
 
205
242
  hSubscription =
206
- EvtSubscribe(NULL, hSignalEvent, path, query, hBookmark, NULL, NULL, flags);
243
+ EvtSubscribe(hRemoteHandle, hSignalEvent, path, query, hBookmark, NULL, NULL, flags);
207
244
  if (!hSubscription) {
208
245
  if (hBookmark != NULL) {
209
246
  EvtClose(hBookmark);
@@ -212,7 +249,11 @@ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
212
249
  CloseHandle(hSignalEvent);
213
250
  }
214
251
  status = GetLastError();
215
- raise_system_error(rb_eWinevtQueryError, status);
252
+ if (rb_obj_is_kind_of(rb_session, rb_cSession)) {
253
+ rb_raise(rb_eRemoteHandlerError, "Remoting subscription is not working. errCode: %ld\n", status);
254
+ } else {
255
+ raise_system_error(rb_eWinevtQueryError, status);
256
+ }
216
257
  }
217
258
 
218
259
  if (winevtSubscribe->subscription != NULL) {
@@ -225,6 +266,7 @@ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
225
266
 
226
267
  winevtSubscribe->signalEvent = hSignalEvent;
227
268
  winevtSubscribe->subscription = hSubscription;
269
+ winevtSubscribe->remoteHandle = hRemoteHandle;
228
270
  if (hBookmark) {
229
271
  winevtSubscribe->bookmark = hBookmark;
230
272
  } else {
@@ -302,6 +344,11 @@ rb_winevt_subscribe_next(VALUE self)
302
344
  return Qfalse;
303
345
  }
304
346
 
347
+ /* If subscription handle is NULL, it should return false. */
348
+ if (!winevtSubscribe->subscription) {
349
+ return Qfalse;
350
+ }
351
+
305
352
  if (!EvtNext(winevtSubscribe->subscription,
306
353
  SUBSCRIBE_ARRAY_SIZE,
307
354
  hEvents,
@@ -309,6 +356,9 @@ rb_winevt_subscribe_next(VALUE self)
309
356
  0,
310
357
  &count)) {
311
358
  status = GetLastError();
359
+ if (ERROR_CANCELLED == status) {
360
+ return Qfalse;
361
+ }
312
362
  if (ERROR_NO_MORE_ITEMS != status) {
313
363
  return Qfalse;
314
364
  }
@@ -345,12 +395,12 @@ rb_winevt_subscribe_render(VALUE self, EVT_HANDLE event)
345
395
  }
346
396
 
347
397
  static VALUE
348
- rb_winevt_subscribe_message(EVT_HANDLE event)
398
+ rb_winevt_subscribe_message(EVT_HANDLE event, LocaleInfo* localeInfo, EVT_HANDLE hRemote)
349
399
  {
350
400
  WCHAR* wResult;
351
401
  VALUE utf8str;
352
402
 
353
- wResult = get_description(event);
403
+ wResult = get_description(event, localeInfo->langID, hRemote);
354
404
  utf8str = wstr_to_rb_str(CP_UTF8, wResult, -1);
355
405
  free(wResult);
356
406
 
@@ -393,7 +443,8 @@ rb_winevt_subscribe_each_yield(VALUE self)
393
443
  for (int i = 0; i < winevtSubscribe->count; i++) {
394
444
  rb_yield_values(3,
395
445
  rb_winevt_subscribe_render(self, winevtSubscribe->hEvents[i]),
396
- rb_winevt_subscribe_message(winevtSubscribe->hEvents[i]),
446
+ rb_winevt_subscribe_message(winevtSubscribe->hEvents[i], winevtSubscribe->localeInfo,
447
+ winevtSubscribe->remoteHandle),
397
448
  rb_winevt_subscribe_string_inserts(winevtSubscribe->hEvents[i]));
398
449
  }
399
450
 
@@ -522,7 +573,7 @@ rb_winevt_subscribe_set_render_as_xml(VALUE self, VALUE rb_render_as_xml)
522
573
  * This method specifies whether preserving qualifiers key or not.
523
574
  *
524
575
  * @since 0.7.3
525
- * @param rb_render_as_xml [Boolean]
576
+ * @param rb_preserve_qualifiers [Boolean]
526
577
  */
527
578
  static VALUE
528
579
  rb_winevt_subscribe_set_preserve_qualifiers(VALUE self, VALUE rb_preserve_qualifiers)
@@ -554,6 +605,93 @@ rb_winevt_subscribe_get_preserve_qualifiers_p(VALUE self)
554
605
  return winevtSubscribe->preserveQualifiers ? Qtrue : Qfalse;
555
606
  }
556
607
 
608
+ /*
609
+ * This method specifies locale with [String].
610
+ *
611
+ * @since 0.8.0
612
+ * @param rb_locale_str [String]
613
+ */
614
+ static VALUE
615
+ rb_winevt_subscribe_set_locale(VALUE self, VALUE rb_locale_str)
616
+ {
617
+ struct WinevtSubscribe* winevtSubscribe;
618
+ LocaleInfo* locale_info = &default_locale;
619
+
620
+ TypedData_Get_Struct(
621
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
622
+
623
+ locale_info = get_locale_info_from_rb_str(rb_locale_str);
624
+
625
+ winevtSubscribe->localeInfo = locale_info;
626
+
627
+ return Qnil;
628
+ }
629
+
630
+ /*
631
+ * This method obtains specified locale with [String].
632
+ *
633
+ * @since 0.8.0
634
+ */
635
+ static VALUE
636
+ rb_winevt_subscribe_get_locale(VALUE self)
637
+ {
638
+ struct WinevtSubscribe* winevtSubscribe;
639
+
640
+ TypedData_Get_Struct(
641
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
642
+
643
+ if (winevtSubscribe->localeInfo->langCode) {
644
+ return rb_str_new2(winevtSubscribe->localeInfo->langCode);
645
+ } else {
646
+ return rb_str_new2(default_locale.langCode);
647
+ }
648
+ }
649
+
650
+ /*
651
+ * This method cancels channel subscription.
652
+ *
653
+ * @return [Boolean]
654
+ * @since 0.9.1
655
+ */
656
+ static VALUE
657
+ rb_winevt_subscribe_cancel(VALUE self)
658
+ {
659
+ struct WinevtSubscribe* winevtSubscribe;
660
+ BOOL result = FALSE;
661
+
662
+ TypedData_Get_Struct(
663
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
664
+
665
+ if (winevtSubscribe->subscription) {
666
+ result = EvtCancel(winevtSubscribe->subscription);
667
+ }
668
+
669
+ if (result) {
670
+ return Qtrue;
671
+ } else {
672
+ return Qfalse;
673
+ }
674
+ }
675
+
676
+ /*
677
+ * This method closes channel handles forcibly.
678
+ *
679
+ * @since 0.9.1
680
+ */
681
+ static VALUE
682
+ rb_winevt_subscribe_close(VALUE self)
683
+ {
684
+ struct WinevtSubscribe* winevtSubscribe;
685
+
686
+ TypedData_Get_Struct(
687
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
688
+
689
+ close_handles(winevtSubscribe);
690
+
691
+ return Qnil;
692
+ }
693
+
694
+
557
695
  void
558
696
  Init_winevt_subscribe(VALUE rb_cEventLog)
559
697
  {
@@ -596,4 +734,24 @@ Init_winevt_subscribe(VALUE rb_cEventLog)
596
734
  */
597
735
  rb_define_method(
598
736
  rb_cSubscribe, "preserve_qualifiers=", rb_winevt_subscribe_set_preserve_qualifiers, 1);
737
+ /*
738
+ * @since 0.8.0
739
+ */
740
+ rb_define_method(
741
+ rb_cSubscribe, "locale", rb_winevt_subscribe_get_locale, 0);
742
+ /*
743
+ * @since 0.8.0
744
+ */
745
+ rb_define_method(
746
+ rb_cSubscribe, "locale=", rb_winevt_subscribe_set_locale, 1);
747
+ /*
748
+ * @since 0.9.1
749
+ */
750
+ rb_define_method(
751
+ rb_cSubscribe, "cancel", rb_winevt_subscribe_cancel, 0);
752
+ /*
753
+ * @since 0.9.1
754
+ */
755
+ rb_define_method(
756
+ rb_cSubscribe, "close", rb_winevt_subscribe_close, 0);
599
757
  }