winevt_c 0.8.0 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,696 +1,727 @@
1
- #include <winevt_c.h>
2
-
3
- #include <sddl.h>
4
- #include <stdlib.h>
5
- #include <string>
6
- #include <vector>
7
-
8
- VALUE
9
- wstr_to_rb_str(UINT cp, const WCHAR* wstr, int clen)
10
- {
11
- VALUE vstr;
12
- CHAR* ptr;
13
- int len = WideCharToMultiByte(cp, 0, wstr, clen, nullptr, 0, nullptr, nullptr);
14
- ptr = ALLOCV_N(CHAR, vstr, len);
15
- WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, nullptr, nullptr);
16
- VALUE str = rb_utf8_str_new_cstr(ptr);
17
- ALLOCV_END(vstr);
18
-
19
- return str;
20
- }
21
-
22
- void
23
- raise_system_error(VALUE error, DWORD errorCode)
24
- {
25
- WCHAR msgBuf[256] = { 0 };
26
- VALUE errorMessage;
27
-
28
- FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
29
- NULL,
30
- errorCode,
31
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
32
- msgBuf,
33
- _countof(msgBuf),
34
- NULL);
35
- errorMessage = wstr_to_rb_str(CP_UTF8, msgBuf, -1);
36
-
37
- #pragma GCC diagnostic push
38
- #pragma GCC diagnostic ignored "-Wformat="
39
- #pragma GCC diagnostic ignored "-Wformat-extra-args"
40
- rb_raise(error, "ErrorCode: %lu\nError: %" PRIsVALUE "\n", errorCode, errorMessage);
41
- #pragma GCC diagnostic pop
42
- }
43
-
44
- VALUE
45
- render_to_rb_str(EVT_HANDLE handle, DWORD flags)
46
- {
47
- VALUE vbuffer;
48
- WCHAR* buffer;
49
- ULONG bufferSize = 0;
50
- ULONG bufferSizeUsed = 0;
51
- ULONG count;
52
- BOOL succeeded;
53
- VALUE result;
54
-
55
- if (flags != EvtRenderEventXml && flags != EvtRenderBookmark) {
56
- return Qnil;
57
- }
58
-
59
- // Get the size of the buffer
60
- EvtRender(nullptr, handle, flags, 0, NULL, &bufferSize, &count);
61
-
62
- // bufferSize is in bytes, not characters
63
- buffer = (WCHAR*)ALLOCV(vbuffer, bufferSize);
64
-
65
- succeeded =
66
- EvtRender(nullptr, handle, flags, bufferSize, buffer, &bufferSizeUsed, &count);
67
- if (!succeeded) {
68
- DWORD status = GetLastError();
69
- ALLOCV_END(vbuffer);
70
- raise_system_error(rb_eWinevtQueryError, status);
71
- }
72
-
73
- result = wstr_to_rb_str(CP_UTF8, buffer, -1);
74
- ALLOCV_END(vbuffer);
75
-
76
- return result;
77
- }
78
-
79
- static std::wstring
80
- guid_to_wstr(const GUID& guid)
81
- {
82
- LPOLESTR p = nullptr;
83
- if (FAILED(StringFromCLSID(guid, &p))) {
84
- return nullptr;
85
- }
86
- std::wstring s(p);
87
- CoTaskMemFree(p);
88
- return s;
89
- }
90
-
91
- static VALUE
92
- extract_user_evt_variants(PEVT_VARIANT pRenderedValues, DWORD propCount)
93
- {
94
- VALUE userValues = rb_ary_new();
95
- VALUE rbObj;
96
-
97
- for (DWORD i = 0; i < propCount; i++) {
98
- switch (pRenderedValues[i].Type) {
99
- case EvtVarTypeNull:
100
- rb_ary_push(userValues, Qnil);
101
- break;
102
- case EvtVarTypeString:
103
- if (pRenderedValues[i].StringVal == nullptr) {
104
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
105
- } else {
106
- std::wstring wStr(pRenderedValues[i].StringVal);
107
- rbObj = wstr_to_rb_str(CP_UTF8, &wStr[0], -1);
108
- rb_ary_push(userValues, rbObj);
109
- }
110
- break;
111
- case EvtVarTypeAnsiString:
112
- if (pRenderedValues[i].AnsiStringVal == nullptr) {
113
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
114
- } else {
115
- rb_ary_push(
116
- userValues,
117
- rb_utf8_str_new_cstr(const_cast<char*>(pRenderedValues[i].AnsiStringVal)));
118
- }
119
- break;
120
- case EvtVarTypeSByte:
121
- rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].SByteVal));
122
- rb_ary_push(userValues, rbObj);
123
- break;
124
- case EvtVarTypeByte:
125
- rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].ByteVal));
126
- rb_ary_push(userValues, rbObj);
127
- break;
128
- case EvtVarTypeInt16:
129
- rbObj = INT2NUM(static_cast<INT32>(pRenderedValues[i].Int16Val));
130
- rb_ary_push(userValues, rbObj);
131
- break;
132
- case EvtVarTypeUInt16:
133
- rbObj = UINT2NUM(static_cast<UINT32>(pRenderedValues[i].UInt16Val));
134
- rb_ary_push(userValues, rbObj);
135
- break;
136
- case EvtVarTypeInt32:
137
- rbObj = INT2NUM(pRenderedValues[i].Int32Val);
138
- rb_ary_push(userValues, rbObj);
139
- break;
140
- case EvtVarTypeUInt32:
141
- rbObj = UINT2NUM(pRenderedValues[i].UInt32Val);
142
- rb_ary_push(userValues, rbObj);
143
- break;
144
- case EvtVarTypeInt64:
145
- rbObj = LONG2NUM(pRenderedValues[i].Int64Val);
146
- rb_ary_push(userValues, rbObj);
147
- break;
148
- case EvtVarTypeUInt64:
149
- rbObj = ULONG2NUM(pRenderedValues[i].UInt64Val);
150
- rb_ary_push(userValues, rbObj);
151
- break;
152
- case EvtVarTypeSingle: {
153
- CHAR sResult[256];
154
- _snprintf_s(
155
- sResult, _countof(sResult), _TRUNCATE, "%f", pRenderedValues[i].SingleVal);
156
- rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
157
- break;
158
- }
159
- case EvtVarTypeDouble: {
160
- CHAR sResult[256];
161
- _snprintf_s(
162
- sResult, _countof(sResult), _TRUNCATE, "%lf", pRenderedValues[i].DoubleVal);
163
- rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
164
- break;
165
- }
166
- case EvtVarTypeBoolean:
167
- rbObj = pRenderedValues[i].BooleanVal ? Qtrue : Qfalse;
168
- rb_ary_push(userValues, rbObj);
169
- break;
170
- case EvtVarTypeGuid:
171
- if (pRenderedValues[i].GuidVal != nullptr) {
172
- const GUID guid = *pRenderedValues[i].GuidVal;
173
- std::wstring wstr = guid_to_wstr(guid);
174
- rbObj = wstr_to_rb_str(CP_UTF8, wstr.c_str(), -1);
175
- rb_ary_push(userValues, rbObj);
176
- } else {
177
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
178
- }
179
- break;
180
- case EvtVarTypeSizeT:
181
- rbObj = SIZET2NUM(pRenderedValues[i].SizeTVal);
182
- rb_ary_push(userValues, rbObj);
183
- break;
184
- case EvtVarTypeFileTime: {
185
- LARGE_INTEGER timestamp;
186
- CHAR strTime[128];
187
- FILETIME ft;
188
- SYSTEMTIME st;
189
- timestamp.QuadPart = pRenderedValues[i].FileTimeVal;
190
- ft.dwHighDateTime = timestamp.HighPart;
191
- ft.dwLowDateTime = timestamp.LowPart;
192
- if (FileTimeToSystemTime(&ft, &st)) {
193
- _snprintf_s(strTime,
194
- _countof(strTime),
195
- _TRUNCATE,
196
- "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
197
- st.wYear,
198
- st.wMonth,
199
- st.wDay,
200
- st.wHour,
201
- st.wMinute,
202
- st.wSecond,
203
- st.wMilliseconds);
204
- rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
205
- } else {
206
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
207
- }
208
- break;
209
- }
210
- case EvtVarTypeSysTime: {
211
- CHAR strTime[128];
212
- SYSTEMTIME st;
213
- if (pRenderedValues[i].SysTimeVal != nullptr) {
214
- st = *pRenderedValues[i].SysTimeVal;
215
- _snprintf_s(strTime,
216
- _countof(strTime),
217
- _TRUNCATE,
218
- "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
219
- st.wYear,
220
- st.wMonth,
221
- st.wDay,
222
- st.wHour,
223
- st.wMinute,
224
- st.wSecond,
225
- st.wMilliseconds);
226
- rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
227
- } else {
228
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
229
- }
230
- break;
231
- }
232
- case EvtVarTypeSid: {
233
- WCHAR* tmpWChar = nullptr;
234
- if (ConvertSidToStringSidW(pRenderedValues[i].SidVal, &tmpWChar)) {
235
- rbObj = wstr_to_rb_str(CP_UTF8, tmpWChar, -1);
236
- rb_ary_push(userValues, rbObj);
237
- LocalFree(tmpWChar);
238
- } else {
239
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
240
- }
241
- break;
242
- }
243
- case EvtVarTypeHexInt32:
244
- rbObj = rb_sprintf("%#x", pRenderedValues[i].UInt32Val);
245
- rb_ary_push(userValues, rbObj);
246
- break;
247
- case EvtVarTypeHexInt64:
248
- rbObj = rb_sprintf("%#I64x", pRenderedValues[i].UInt64Val);
249
- rb_ary_push(userValues, rbObj);
250
- break;
251
- case EvtVarTypeEvtXml:
252
- if (pRenderedValues[i].XmlVal == nullptr) {
253
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
254
- } else {
255
- rbObj = wstr_to_rb_str(CP_UTF8, pRenderedValues[i].XmlVal, -1);
256
- rb_ary_push(userValues, rbObj);
257
- }
258
- break;
259
- default:
260
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
261
- break;
262
- }
263
- }
264
-
265
- return userValues;
266
- }
267
-
268
- VALUE
269
- get_values(EVT_HANDLE handle)
270
- {
271
- VALUE vbuffer;
272
- PEVT_VARIANT pRenderedValues;
273
- ULONG bufferSize = 0;
274
- ULONG bufferSizeUsed = 0;
275
- DWORD propCount = 0;
276
- BOOL succeeded;
277
- VALUE userValues = Qnil;
278
-
279
- EVT_HANDLE renderContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextUser);
280
- if (renderContext == nullptr) {
281
- rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
282
- }
283
-
284
- // Get the size of the buffer
285
- EvtRender(
286
- renderContext, handle, EvtRenderEventValues, 0, NULL, &bufferSize, &propCount);
287
-
288
- // bufferSize is in bytes, not array size
289
- pRenderedValues = (PEVT_VARIANT)ALLOCV(vbuffer, bufferSize);
290
-
291
- succeeded = EvtRender(renderContext,
292
- handle,
293
- EvtRenderEventValues,
294
- bufferSize,
295
- pRenderedValues,
296
- &bufferSizeUsed,
297
- &propCount);
298
- if (!succeeded) {
299
- DWORD status = GetLastError();
300
- ALLOCV_END(vbuffer);
301
- EvtClose(renderContext);
302
- raise_system_error(rb_eWinevtQueryError, status);
303
- }
304
-
305
- userValues = extract_user_evt_variants(pRenderedValues, propCount);
306
-
307
- ALLOCV_END(vbuffer);
308
- EvtClose(renderContext);
309
-
310
- return userValues;
311
- }
312
-
313
- static std::vector<WCHAR>
314
- get_message(EVT_HANDLE hMetadata, EVT_HANDLE handle)
315
- {
316
- #define BUFSIZE 4096
317
- std::vector<WCHAR> result;
318
- ULONG status;
319
- ULONG bufferSizeNeeded = 0;
320
- LPVOID lpMsgBuf;
321
- std::vector<WCHAR> message(BUFSIZE);
322
-
323
- if (!EvtFormatMessage(hMetadata,
324
- handle,
325
- 0xffffffff,
326
- 0,
327
- nullptr,
328
- EvtFormatMessageEvent,
329
- message.size(),
330
- &message[0],
331
- &bufferSizeNeeded)) {
332
- status = GetLastError();
333
-
334
- if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
335
- switch (status) {
336
- case ERROR_EVT_MESSAGE_NOT_FOUND:
337
- case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
338
- case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
339
- case ERROR_RESOURCE_LANG_NOT_FOUND:
340
- case ERROR_MUI_FILE_NOT_FOUND:
341
- case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT: {
342
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
343
- FORMAT_MESSAGE_IGNORE_INSERTS,
344
- nullptr,
345
- status,
346
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
347
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
348
- 0,
349
- nullptr) == 0)
350
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
351
- FORMAT_MESSAGE_IGNORE_INSERTS,
352
- nullptr,
353
- status,
354
- MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
355
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
356
- 0,
357
- nullptr);
358
-
359
- std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
360
- std::copy(ret.begin(), ret.end(), std::back_inserter(result));
361
- LocalFree(lpMsgBuf);
362
-
363
- goto cleanup;
364
- }
365
- }
366
-
367
- if (status != ERROR_INSUFFICIENT_BUFFER)
368
- rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
369
- }
370
-
371
- if (status == ERROR_INSUFFICIENT_BUFFER) {
372
- message.resize(bufferSizeNeeded);
373
- message.shrink_to_fit();
374
-
375
- if (!EvtFormatMessage(hMetadata,
376
- handle,
377
- 0xffffffff,
378
- 0,
379
- nullptr,
380
- EvtFormatMessageEvent,
381
- message.size(),
382
- &message.front(),
383
- &bufferSizeNeeded)) {
384
- status = GetLastError();
385
-
386
- if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
387
- switch (status) {
388
- case ERROR_EVT_MESSAGE_NOT_FOUND:
389
- case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
390
- case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
391
- case ERROR_RESOURCE_LANG_NOT_FOUND:
392
- case ERROR_MUI_FILE_NOT_FOUND:
393
- case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT:
394
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
395
- FORMAT_MESSAGE_FROM_SYSTEM |
396
- FORMAT_MESSAGE_IGNORE_INSERTS,
397
- nullptr,
398
- status,
399
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
400
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
401
- 0,
402
- nullptr) == 0)
403
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
404
- FORMAT_MESSAGE_FROM_SYSTEM |
405
- FORMAT_MESSAGE_IGNORE_INSERTS,
406
- nullptr,
407
- status,
408
- MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
409
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
410
- 0,
411
- nullptr);
412
-
413
- std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
414
- std::copy(ret.begin(), ret.end(), std::back_inserter(result));
415
- LocalFree(lpMsgBuf);
416
-
417
- goto cleanup;
418
- }
419
-
420
- rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
421
- }
422
- }
423
- }
424
- }
425
-
426
- result = message;
427
-
428
- cleanup:
429
-
430
- return result;
431
-
432
- #undef BUFSIZE
433
- }
434
-
435
- WCHAR*
436
- get_description(EVT_HANDLE handle, LANGID langID)
437
- {
438
- #define BUFSIZE 4096
439
- std::vector<WCHAR> buffer(BUFSIZE);
440
- ULONG bufferSizeNeeded = 0;
441
- ULONG status, count;
442
- std::vector<WCHAR> result;
443
- EVT_HANDLE hMetadata = nullptr;
444
-
445
- static PCWSTR eventProperties[] = { L"Event/System/Provider/@Name" };
446
- EVT_HANDLE renderContext =
447
- EvtCreateRenderContext(1, eventProperties, EvtRenderContextValues);
448
- if (renderContext == nullptr) {
449
- rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
450
- }
451
-
452
- if (EvtRender(renderContext,
453
- handle,
454
- EvtRenderEventValues,
455
- buffer.size(),
456
- &buffer.front(),
457
- &bufferSizeNeeded,
458
- &count) != FALSE) {
459
- status = ERROR_SUCCESS;
460
- } else {
461
- status = GetLastError();
462
- }
463
-
464
- if (status != ERROR_SUCCESS) {
465
- raise_system_error(rb_eWinevtQueryError, status);
466
- }
467
-
468
- // Obtain buffer as EVT_VARIANT pointer. To avoid ErrorCide 87 in EvtRender.
469
- const PEVT_VARIANT values = reinterpret_cast<PEVT_VARIANT>(&buffer.front());
470
-
471
- // Open publisher metadata
472
- hMetadata = EvtOpenPublisherMetadata(
473
- nullptr,
474
- values[0].StringVal,
475
- nullptr,
476
- MAKELCID(langID, SORT_DEFAULT),
477
- 0);
478
- if (hMetadata == nullptr) {
479
- // When winevt_c cannot open metadata, then give up to obtain
480
- // message file and clean up immediately.
481
- goto cleanup;
482
- }
483
-
484
- result = get_message(hMetadata, handle);
485
-
486
- #undef BUFSIZE
487
-
488
- cleanup:
489
-
490
- if (renderContext)
491
- EvtClose(renderContext);
492
-
493
- if (hMetadata)
494
- EvtClose(hMetadata);
495
-
496
- return _wcsdup(result.data());
497
- }
498
-
499
- VALUE
500
- render_system_event(EVT_HANDLE hEvent, BOOL preserve_qualifiers)
501
- {
502
- DWORD status = ERROR_SUCCESS;
503
- EVT_HANDLE hContext = NULL;
504
- DWORD dwBufferSize = 0;
505
- DWORD dwBufferUsed = 0;
506
- DWORD dwPropertyCount = 0;
507
- VALUE vRenderedValues;
508
- PEVT_VARIANT pRenderedValues = NULL;
509
- WCHAR wsGuid[50];
510
- LPSTR pwsSid = NULL;
511
- ULONGLONG ullTimeStamp = 0;
512
- ULONGLONG ullNanoseconds = 0;
513
- SYSTEMTIME st;
514
- FILETIME ft;
515
- CHAR buffer[32];
516
- VALUE rbstr;
517
- DWORD EventID;
518
- VALUE hash = rb_hash_new();
519
-
520
- hContext = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
521
- if (NULL == hContext) {
522
- rb_raise(
523
- rb_eWinevtQueryError, "Failed to create renderContext with %lu\n", GetLastError());
524
- }
525
-
526
- if (!EvtRender(hContext,
527
- hEvent,
528
- EvtRenderEventValues,
529
- dwBufferSize,
530
- pRenderedValues,
531
- &dwBufferUsed,
532
- &dwPropertyCount)) {
533
- status = GetLastError();
534
- if (ERROR_INSUFFICIENT_BUFFER == status) {
535
- dwBufferSize = dwBufferUsed;
536
- pRenderedValues = (PEVT_VARIANT)ALLOCV(vRenderedValues, dwBufferSize);
537
- if (pRenderedValues) {
538
- EvtRender(hContext,
539
- hEvent,
540
- EvtRenderEventValues,
541
- dwBufferSize,
542
- pRenderedValues,
543
- &dwBufferUsed,
544
- &dwPropertyCount);
545
- } else {
546
- EvtClose(hContext);
547
- rb_raise(rb_eRuntimeError, "Failed to malloc memory with %lu\n", status);
548
- }
549
- }
550
-
551
- status = GetLastError();
552
- if (ERROR_SUCCESS != status) {
553
- EvtClose(hContext);
554
- ALLOCV_END(vRenderedValues);
555
-
556
- rb_raise(rb_eWinevtQueryError, "EvtRender failed with %lu\n", status);
557
- }
558
- }
559
-
560
- // EVT_VARIANT value with EvtRenderContextSystem will be decomposed
561
- // as the following enum definition:
562
- // https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_system_property_id
563
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemProviderName].StringVal, -1);
564
- rb_hash_aset(hash, rb_str_new2("ProviderName"), rbstr);
565
- if (NULL != pRenderedValues[EvtSystemProviderGuid].GuidVal) {
566
- const GUID* Guid = pRenderedValues[EvtSystemProviderGuid].GuidVal;
567
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
568
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
569
- rb_hash_aset(hash, rb_str_new2("ProviderGuid"), rbstr);
570
- } else {
571
- rb_hash_aset(hash, rb_str_new2("ProviderGuid"), Qnil);
572
- }
573
-
574
- EventID = pRenderedValues[EvtSystemEventID].UInt16Val;
575
- if (preserve_qualifiers) {
576
- if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
577
- rb_hash_aset(hash, rb_str_new2("Qualifiers"),
578
- INT2NUM(pRenderedValues[EvtSystemQualifiers].UInt16Val));
579
- } else {
580
- rb_hash_aset(hash, rb_str_new2("Qualifiers"), rb_str_new2(""));
581
- }
582
-
583
- rb_hash_aset(hash, rb_str_new2("EventID"), INT2NUM(EventID));
584
- } else {
585
- if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
586
- EventID = MAKELONG(pRenderedValues[EvtSystemEventID].UInt16Val,
587
- pRenderedValues[EvtSystemQualifiers].UInt16Val);
588
- }
589
-
590
- rb_hash_aset(hash, rb_str_new2("EventID"), ULONG2NUM(EventID));
591
- }
592
-
593
- rb_hash_aset(hash,
594
- rb_str_new2("Version"),
595
- (EvtVarTypeNull == pRenderedValues[EvtSystemVersion].Type)
596
- ? INT2NUM(0)
597
- : INT2NUM(pRenderedValues[EvtSystemVersion].ByteVal));
598
- rb_hash_aset(hash,
599
- rb_str_new2("Level"),
600
- (EvtVarTypeNull == pRenderedValues[EvtSystemLevel].Type)
601
- ? INT2NUM(0)
602
- : INT2NUM(pRenderedValues[EvtSystemLevel].ByteVal));
603
- rb_hash_aset(hash,
604
- rb_str_new2("Task"),
605
- (EvtVarTypeNull == pRenderedValues[EvtSystemTask].Type)
606
- ? INT2NUM(0)
607
- : INT2NUM(pRenderedValues[EvtSystemTask].UInt16Val));
608
- rb_hash_aset(hash,
609
- rb_str_new2("Opcode"),
610
- (EvtVarTypeNull == pRenderedValues[EvtSystemOpcode].Type)
611
- ? INT2NUM(0)
612
- : INT2NUM(pRenderedValues[EvtSystemOpcode].ByteVal));
613
- _snprintf_s(buffer,
614
- _countof(buffer),
615
- _TRUNCATE,
616
- "0x%llx",
617
- pRenderedValues[EvtSystemKeywords].UInt64Val);
618
- rb_hash_aset(hash,
619
- rb_str_new2("Keywords"),
620
- (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
621
- ? Qnil
622
- : rb_str_new2(buffer));
623
-
624
- ullTimeStamp = pRenderedValues[EvtSystemTimeCreated].FileTimeVal;
625
- ft.dwHighDateTime = (DWORD)((ullTimeStamp >> 32) & 0xFFFFFFFF);
626
- ft.dwLowDateTime = (DWORD)(ullTimeStamp & 0xFFFFFFFF);
627
-
628
- FileTimeToSystemTime(&ft, &st);
629
- ullNanoseconds =
630
- (ullTimeStamp % 10000000) *
631
- 100; // Display nanoseconds instead of milliseconds for higher resolution
632
- _snprintf_s(buffer,
633
- _countof(buffer),
634
- _TRUNCATE,
635
- "%02d/%02d/%02d %02d:%02d:%02d.%llu",
636
- st.wYear,
637
- st.wMonth,
638
- st.wDay,
639
- st.wHour,
640
- st.wMinute,
641
- st.wSecond,
642
- ullNanoseconds);
643
- rb_hash_aset(hash,
644
- rb_str_new2("TimeCreated"),
645
- (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
646
- ? Qnil
647
- : rb_str_new2(buffer));
648
- _snprintf_s(buffer,
649
- _countof(buffer),
650
- _TRUNCATE,
651
- "%llu",
652
- pRenderedValues[EvtSystemEventRecordId].UInt64Val);
653
- rb_hash_aset(hash,
654
- rb_str_new2("EventRecordID"),
655
- (EvtVarTypeNull == pRenderedValues[EvtSystemEventRecordId].UInt64Val)
656
- ? Qnil
657
- : rb_str_new2(buffer));
658
-
659
- if (EvtVarTypeNull != pRenderedValues[EvtSystemActivityID].Type) {
660
- const GUID* Guid = pRenderedValues[EvtSystemActivityID].GuidVal;
661
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
662
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
663
- rb_hash_aset(hash, rb_str_new2("ActivityID"), rbstr);
664
- }
665
-
666
- if (EvtVarTypeNull != pRenderedValues[EvtSystemRelatedActivityID].Type) {
667
- const GUID* Guid = pRenderedValues[EvtSystemRelatedActivityID].GuidVal;
668
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
669
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
670
- rb_hash_aset(hash, rb_str_new2("RelatedActivityID"), rbstr);
671
- }
672
-
673
- rb_hash_aset(hash,
674
- rb_str_new2("ProcessID"),
675
- UINT2NUM(pRenderedValues[EvtSystemProcessID].UInt32Val));
676
- rb_hash_aset(hash,
677
- rb_str_new2("ThreadID"),
678
- UINT2NUM(pRenderedValues[EvtSystemThreadID].UInt32Val));
679
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemChannel].StringVal, -1);
680
- rb_hash_aset(hash, rb_str_new2("Channel"), rbstr);
681
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemComputer].StringVal, -1);
682
- rb_hash_aset(hash, rb_str_new2("Computer"), rbstr);
683
-
684
- if (EvtVarTypeNull != pRenderedValues[EvtSystemUserID].Type) {
685
- if (ConvertSidToStringSid(pRenderedValues[EvtSystemUserID].SidVal, &pwsSid)) {
686
- rbstr = rb_utf8_str_new_cstr(pwsSid);
687
- rb_hash_aset(hash, rb_str_new2("UserID"), rbstr);
688
- LocalFree(pwsSid);
689
- }
690
- }
691
-
692
- EvtClose(hContext);
693
- ALLOCV_END(vRenderedValues);
694
-
695
- return hash;
696
- }
1
+ #include <winevt_c.h>
2
+
3
+ #include <sddl.h>
4
+ #include <stdlib.h>
5
+ #include <string>
6
+ #include <vector>
7
+
8
+ VALUE
9
+ wstr_to_rb_str(UINT cp, const WCHAR* wstr, int clen)
10
+ {
11
+ VALUE vstr;
12
+ CHAR* ptr;
13
+ int len = WideCharToMultiByte(cp, 0, wstr, clen, nullptr, 0, nullptr, nullptr);
14
+ ptr = ALLOCV_N(CHAR, vstr, len);
15
+ WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, nullptr, nullptr);
16
+ VALUE str = rb_utf8_str_new_cstr(ptr);
17
+ ALLOCV_END(vstr);
18
+
19
+ return str;
20
+ }
21
+
22
+ void
23
+ raise_system_error(VALUE error, DWORD errorCode)
24
+ {
25
+ WCHAR msgBuf[256] = { 0 };
26
+ VALUE errorMessage;
27
+
28
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
29
+ NULL,
30
+ errorCode,
31
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
32
+ msgBuf,
33
+ _countof(msgBuf),
34
+ NULL);
35
+ errorMessage = wstr_to_rb_str(CP_UTF8, msgBuf, -1);
36
+
37
+ #pragma GCC diagnostic push
38
+ #pragma GCC diagnostic ignored "-Wformat="
39
+ #pragma GCC diagnostic ignored "-Wformat-extra-args"
40
+ rb_raise(error, "ErrorCode: %lu\nError: %" PRIsVALUE "\n", errorCode, errorMessage);
41
+ #pragma GCC diagnostic pop
42
+ }
43
+
44
+ VALUE
45
+ render_to_rb_str(EVT_HANDLE handle, DWORD flags)
46
+ {
47
+ VALUE vbuffer;
48
+ WCHAR* buffer;
49
+ ULONG bufferSize = 0;
50
+ ULONG bufferSizeUsed = 0;
51
+ ULONG count;
52
+ BOOL succeeded;
53
+ VALUE result;
54
+
55
+ if (flags != EvtRenderEventXml && flags != EvtRenderBookmark) {
56
+ return Qnil;
57
+ }
58
+
59
+ // Get the size of the buffer
60
+ EvtRender(nullptr, handle, flags, 0, NULL, &bufferSize, &count);
61
+
62
+ // bufferSize is in bytes, not characters
63
+ buffer = (WCHAR*)ALLOCV(vbuffer, bufferSize);
64
+
65
+ succeeded =
66
+ EvtRender(nullptr, handle, flags, bufferSize, buffer, &bufferSizeUsed, &count);
67
+ if (!succeeded) {
68
+ DWORD status = GetLastError();
69
+ ALLOCV_END(vbuffer);
70
+ raise_system_error(rb_eWinevtQueryError, status);
71
+ }
72
+
73
+ result = wstr_to_rb_str(CP_UTF8, buffer, -1);
74
+ ALLOCV_END(vbuffer);
75
+
76
+ return result;
77
+ }
78
+
79
+ EVT_HANDLE
80
+ connect_to_remote(LPWSTR computerName, LPWSTR domain, LPWSTR username, LPWSTR password,
81
+ EVT_RPC_LOGIN_FLAGS flags, DWORD *error_code)
82
+ {
83
+ EVT_HANDLE hRemote = NULL;
84
+ EVT_RPC_LOGIN Credentials;
85
+
86
+ RtlZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
87
+
88
+ Credentials.Server = computerName;
89
+ Credentials.Domain = domain;
90
+ Credentials.User = username;
91
+ Credentials.Password = password;
92
+ Credentials.Flags = flags;
93
+
94
+ hRemote = EvtOpenSession(EvtRpcLogin, &Credentials, 0, 0);
95
+ if (!hRemote) {
96
+ *error_code = GetLastError();
97
+ return hRemote;
98
+ }
99
+
100
+ SecureZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
101
+
102
+ return hRemote;
103
+ }
104
+
105
+ static std::wstring
106
+ guid_to_wstr(const GUID& guid)
107
+ {
108
+ LPOLESTR p = nullptr;
109
+ if (FAILED(StringFromCLSID(guid, &p))) {
110
+ return nullptr;
111
+ }
112
+ std::wstring s(p);
113
+ CoTaskMemFree(p);
114
+ return s;
115
+ }
116
+
117
+ static VALUE
118
+ extract_user_evt_variants(PEVT_VARIANT pRenderedValues, DWORD propCount)
119
+ {
120
+ VALUE userValues = rb_ary_new();
121
+ VALUE rbObj;
122
+
123
+ for (DWORD i = 0; i < propCount; i++) {
124
+ switch (pRenderedValues[i].Type) {
125
+ case EvtVarTypeNull:
126
+ rb_ary_push(userValues, Qnil);
127
+ break;
128
+ case EvtVarTypeString:
129
+ if (pRenderedValues[i].StringVal == nullptr) {
130
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
131
+ } else {
132
+ std::wstring wStr(pRenderedValues[i].StringVal);
133
+ rbObj = wstr_to_rb_str(CP_UTF8, &wStr[0], -1);
134
+ rb_ary_push(userValues, rbObj);
135
+ }
136
+ break;
137
+ case EvtVarTypeAnsiString:
138
+ if (pRenderedValues[i].AnsiStringVal == nullptr) {
139
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
140
+ } else {
141
+ rb_ary_push(
142
+ userValues,
143
+ rb_utf8_str_new_cstr(const_cast<char*>(pRenderedValues[i].AnsiStringVal)));
144
+ }
145
+ break;
146
+ case EvtVarTypeSByte:
147
+ rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].SByteVal));
148
+ rb_ary_push(userValues, rbObj);
149
+ break;
150
+ case EvtVarTypeByte:
151
+ rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].ByteVal));
152
+ rb_ary_push(userValues, rbObj);
153
+ break;
154
+ case EvtVarTypeInt16:
155
+ rbObj = INT2NUM(static_cast<INT32>(pRenderedValues[i].Int16Val));
156
+ rb_ary_push(userValues, rbObj);
157
+ break;
158
+ case EvtVarTypeUInt16:
159
+ rbObj = UINT2NUM(static_cast<UINT32>(pRenderedValues[i].UInt16Val));
160
+ rb_ary_push(userValues, rbObj);
161
+ break;
162
+ case EvtVarTypeInt32:
163
+ rbObj = INT2NUM(pRenderedValues[i].Int32Val);
164
+ rb_ary_push(userValues, rbObj);
165
+ break;
166
+ case EvtVarTypeUInt32:
167
+ rbObj = UINT2NUM(pRenderedValues[i].UInt32Val);
168
+ rb_ary_push(userValues, rbObj);
169
+ break;
170
+ case EvtVarTypeInt64:
171
+ rbObj = LONG2NUM(pRenderedValues[i].Int64Val);
172
+ rb_ary_push(userValues, rbObj);
173
+ break;
174
+ case EvtVarTypeUInt64:
175
+ rbObj = ULONG2NUM(pRenderedValues[i].UInt64Val);
176
+ rb_ary_push(userValues, rbObj);
177
+ break;
178
+ case EvtVarTypeSingle: {
179
+ CHAR sResult[256];
180
+ _snprintf_s(
181
+ sResult, _countof(sResult), _TRUNCATE, "%f", pRenderedValues[i].SingleVal);
182
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
183
+ break;
184
+ }
185
+ case EvtVarTypeDouble: {
186
+ CHAR sResult[256];
187
+ _snprintf_s(
188
+ sResult, _countof(sResult), _TRUNCATE, "%lf", pRenderedValues[i].DoubleVal);
189
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
190
+ break;
191
+ }
192
+ case EvtVarTypeBoolean:
193
+ rbObj = pRenderedValues[i].BooleanVal ? Qtrue : Qfalse;
194
+ rb_ary_push(userValues, rbObj);
195
+ break;
196
+ case EvtVarTypeGuid:
197
+ if (pRenderedValues[i].GuidVal != nullptr) {
198
+ const GUID guid = *pRenderedValues[i].GuidVal;
199
+ std::wstring wstr = guid_to_wstr(guid);
200
+ rbObj = wstr_to_rb_str(CP_UTF8, wstr.c_str(), -1);
201
+ rb_ary_push(userValues, rbObj);
202
+ } else {
203
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
204
+ }
205
+ break;
206
+ case EvtVarTypeSizeT:
207
+ rbObj = SIZET2NUM(pRenderedValues[i].SizeTVal);
208
+ rb_ary_push(userValues, rbObj);
209
+ break;
210
+ case EvtVarTypeFileTime: {
211
+ LARGE_INTEGER timestamp;
212
+ CHAR strTime[128];
213
+ FILETIME ft;
214
+ SYSTEMTIME st;
215
+ timestamp.QuadPart = pRenderedValues[i].FileTimeVal;
216
+ ft.dwHighDateTime = timestamp.HighPart;
217
+ ft.dwLowDateTime = timestamp.LowPart;
218
+ if (FileTimeToSystemTime(&ft, &st)) {
219
+ _snprintf_s(strTime,
220
+ _countof(strTime),
221
+ _TRUNCATE,
222
+ "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
223
+ st.wYear,
224
+ st.wMonth,
225
+ st.wDay,
226
+ st.wHour,
227
+ st.wMinute,
228
+ st.wSecond,
229
+ st.wMilliseconds);
230
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
231
+ } else {
232
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
233
+ }
234
+ break;
235
+ }
236
+ case EvtVarTypeSysTime: {
237
+ CHAR strTime[128];
238
+ SYSTEMTIME st;
239
+ if (pRenderedValues[i].SysTimeVal != nullptr) {
240
+ st = *pRenderedValues[i].SysTimeVal;
241
+ _snprintf_s(strTime,
242
+ _countof(strTime),
243
+ _TRUNCATE,
244
+ "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
245
+ st.wYear,
246
+ st.wMonth,
247
+ st.wDay,
248
+ st.wHour,
249
+ st.wMinute,
250
+ st.wSecond,
251
+ st.wMilliseconds);
252
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
253
+ } else {
254
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
255
+ }
256
+ break;
257
+ }
258
+ case EvtVarTypeSid: {
259
+ WCHAR* tmpWChar = nullptr;
260
+ if (ConvertSidToStringSidW(pRenderedValues[i].SidVal, &tmpWChar)) {
261
+ rbObj = wstr_to_rb_str(CP_UTF8, tmpWChar, -1);
262
+ rb_ary_push(userValues, rbObj);
263
+ LocalFree(tmpWChar);
264
+ } else {
265
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
266
+ }
267
+ break;
268
+ }
269
+ case EvtVarTypeHexInt32:
270
+ rbObj = rb_sprintf("%#x", pRenderedValues[i].UInt32Val);
271
+ rb_ary_push(userValues, rbObj);
272
+ break;
273
+ case EvtVarTypeHexInt64:
274
+ uint32_t high;
275
+ uint32_t low;
276
+
277
+ high = pRenderedValues[i].UInt64Val >> 32;
278
+ low = pRenderedValues[i].UInt64Val & 0x00000000FFFFFFFF;
279
+ rbObj = rb_sprintf("0x%08x%08x", high, low);
280
+ rb_ary_push(userValues, rbObj);
281
+ break;
282
+ case EvtVarTypeEvtXml:
283
+ if (pRenderedValues[i].XmlVal == nullptr) {
284
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
285
+ } else {
286
+ rbObj = wstr_to_rb_str(CP_UTF8, pRenderedValues[i].XmlVal, -1);
287
+ rb_ary_push(userValues, rbObj);
288
+ }
289
+ break;
290
+ default:
291
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
292
+ break;
293
+ }
294
+ }
295
+
296
+ return userValues;
297
+ }
298
+
299
+ VALUE
300
+ get_values(EVT_HANDLE handle)
301
+ {
302
+ VALUE vbuffer;
303
+ PEVT_VARIANT pRenderedValues;
304
+ ULONG bufferSize = 0;
305
+ ULONG bufferSizeUsed = 0;
306
+ DWORD propCount = 0;
307
+ BOOL succeeded;
308
+ VALUE userValues = Qnil;
309
+
310
+ EVT_HANDLE renderContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextUser);
311
+ if (renderContext == nullptr) {
312
+ rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
313
+ }
314
+
315
+ // Get the size of the buffer
316
+ EvtRender(
317
+ renderContext, handle, EvtRenderEventValues, 0, NULL, &bufferSize, &propCount);
318
+
319
+ // bufferSize is in bytes, not array size
320
+ pRenderedValues = (PEVT_VARIANT)ALLOCV(vbuffer, bufferSize);
321
+
322
+ succeeded = EvtRender(renderContext,
323
+ handle,
324
+ EvtRenderEventValues,
325
+ bufferSize,
326
+ pRenderedValues,
327
+ &bufferSizeUsed,
328
+ &propCount);
329
+ if (!succeeded) {
330
+ DWORD status = GetLastError();
331
+ ALLOCV_END(vbuffer);
332
+ EvtClose(renderContext);
333
+ raise_system_error(rb_eWinevtQueryError, status);
334
+ }
335
+
336
+ userValues = extract_user_evt_variants(pRenderedValues, propCount);
337
+
338
+ ALLOCV_END(vbuffer);
339
+ EvtClose(renderContext);
340
+
341
+ return userValues;
342
+ }
343
+
344
+ static std::vector<WCHAR>
345
+ get_message(EVT_HANDLE hMetadata, EVT_HANDLE handle)
346
+ {
347
+ #define BUFSIZE 4096
348
+ std::vector<WCHAR> result;
349
+ ULONG status;
350
+ ULONG bufferSizeNeeded = 0;
351
+ LPVOID lpMsgBuf;
352
+ std::vector<WCHAR> message(BUFSIZE);
353
+
354
+ if (!EvtFormatMessage(hMetadata,
355
+ handle,
356
+ 0xffffffff,
357
+ 0,
358
+ nullptr,
359
+ EvtFormatMessageEvent,
360
+ message.size(),
361
+ &message[0],
362
+ &bufferSizeNeeded)) {
363
+ status = GetLastError();
364
+
365
+ if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
366
+ switch (status) {
367
+ case ERROR_EVT_MESSAGE_NOT_FOUND:
368
+ case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
369
+ case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
370
+ case ERROR_RESOURCE_LANG_NOT_FOUND:
371
+ case ERROR_MUI_FILE_NOT_FOUND:
372
+ case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT: {
373
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
374
+ FORMAT_MESSAGE_IGNORE_INSERTS,
375
+ nullptr,
376
+ status,
377
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
378
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
379
+ 0,
380
+ nullptr) == 0)
381
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
382
+ FORMAT_MESSAGE_IGNORE_INSERTS,
383
+ nullptr,
384
+ status,
385
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
386
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
387
+ 0,
388
+ nullptr);
389
+
390
+ std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
391
+ std::copy(ret.begin(), ret.end(), std::back_inserter(result));
392
+ LocalFree(lpMsgBuf);
393
+
394
+ goto cleanup;
395
+ }
396
+ }
397
+
398
+ if (status != ERROR_INSUFFICIENT_BUFFER)
399
+ rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
400
+ }
401
+
402
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
403
+ message.resize(bufferSizeNeeded);
404
+ message.shrink_to_fit();
405
+
406
+ if (!EvtFormatMessage(hMetadata,
407
+ handle,
408
+ 0xffffffff,
409
+ 0,
410
+ nullptr,
411
+ EvtFormatMessageEvent,
412
+ message.size(),
413
+ &message.front(),
414
+ &bufferSizeNeeded)) {
415
+ status = GetLastError();
416
+
417
+ if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
418
+ switch (status) {
419
+ case ERROR_EVT_MESSAGE_NOT_FOUND:
420
+ case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
421
+ case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
422
+ case ERROR_RESOURCE_LANG_NOT_FOUND:
423
+ case ERROR_MUI_FILE_NOT_FOUND:
424
+ case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT:
425
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
426
+ FORMAT_MESSAGE_FROM_SYSTEM |
427
+ FORMAT_MESSAGE_IGNORE_INSERTS,
428
+ nullptr,
429
+ status,
430
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
431
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
432
+ 0,
433
+ nullptr) == 0)
434
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
435
+ FORMAT_MESSAGE_FROM_SYSTEM |
436
+ FORMAT_MESSAGE_IGNORE_INSERTS,
437
+ nullptr,
438
+ status,
439
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
440
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
441
+ 0,
442
+ nullptr);
443
+
444
+ std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
445
+ std::copy(ret.begin(), ret.end(), std::back_inserter(result));
446
+ LocalFree(lpMsgBuf);
447
+
448
+ goto cleanup;
449
+ }
450
+
451
+ rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
452
+ }
453
+ }
454
+ }
455
+ }
456
+
457
+ result = message;
458
+
459
+ cleanup:
460
+
461
+ return result;
462
+
463
+ #undef BUFSIZE
464
+ }
465
+
466
+ WCHAR*
467
+ get_description(EVT_HANDLE handle, LANGID langID, EVT_HANDLE hRemote)
468
+ {
469
+ #define BUFSIZE 4096
470
+ std::vector<WCHAR> buffer(BUFSIZE);
471
+ ULONG bufferSizeNeeded = 0;
472
+ ULONG status, count;
473
+ std::vector<WCHAR> result;
474
+ EVT_HANDLE hMetadata = nullptr;
475
+
476
+ static PCWSTR eventProperties[] = { L"Event/System/Provider/@Name" };
477
+ EVT_HANDLE renderContext =
478
+ EvtCreateRenderContext(1, eventProperties, EvtRenderContextValues);
479
+ if (renderContext == nullptr) {
480
+ rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
481
+ }
482
+
483
+ if (EvtRender(renderContext,
484
+ handle,
485
+ EvtRenderEventValues,
486
+ buffer.size(),
487
+ &buffer.front(),
488
+ &bufferSizeNeeded,
489
+ &count) != FALSE) {
490
+ status = ERROR_SUCCESS;
491
+ } else {
492
+ status = GetLastError();
493
+ }
494
+
495
+ if (status != ERROR_SUCCESS) {
496
+ raise_system_error(rb_eWinevtQueryError, status);
497
+ }
498
+
499
+ // Obtain buffer as EVT_VARIANT pointer. To avoid ErrorCide 87 in EvtRender.
500
+ const PEVT_VARIANT values = reinterpret_cast<PEVT_VARIANT>(&buffer.front());
501
+
502
+ // Open publisher metadata
503
+ hMetadata = EvtOpenPublisherMetadata(
504
+ hRemote,
505
+ values[0].StringVal,
506
+ nullptr,
507
+ MAKELCID(langID, SORT_DEFAULT),
508
+ 0);
509
+ if (hMetadata == nullptr) {
510
+ // When winevt_c cannot open metadata, then give up to obtain
511
+ // message file and clean up immediately.
512
+ goto cleanup;
513
+ }
514
+
515
+ result = get_message(hMetadata, handle);
516
+
517
+ #undef BUFSIZE
518
+
519
+ cleanup:
520
+
521
+ if (renderContext)
522
+ EvtClose(renderContext);
523
+
524
+ if (hMetadata)
525
+ EvtClose(hMetadata);
526
+
527
+ return _wcsdup(result.data());
528
+ }
529
+
530
+ VALUE
531
+ render_system_event(EVT_HANDLE hEvent, BOOL preserve_qualifiers)
532
+ {
533
+ DWORD status = ERROR_SUCCESS;
534
+ EVT_HANDLE hContext = NULL;
535
+ DWORD dwBufferSize = 0;
536
+ DWORD dwBufferUsed = 0;
537
+ DWORD dwPropertyCount = 0;
538
+ VALUE vRenderedValues;
539
+ PEVT_VARIANT pRenderedValues = NULL;
540
+ WCHAR wsGuid[50];
541
+ LPSTR pwsSid = NULL;
542
+ ULONGLONG ullTimeStamp = 0;
543
+ ULONGLONG ullNanoseconds = 0;
544
+ SYSTEMTIME st;
545
+ FILETIME ft;
546
+ CHAR buffer[32];
547
+ VALUE rbstr;
548
+ DWORD EventID;
549
+ VALUE hash = rb_hash_new();
550
+
551
+ hContext = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
552
+ if (NULL == hContext) {
553
+ rb_raise(
554
+ rb_eWinevtQueryError, "Failed to create renderContext with %lu\n", GetLastError());
555
+ }
556
+
557
+ if (!EvtRender(hContext,
558
+ hEvent,
559
+ EvtRenderEventValues,
560
+ dwBufferSize,
561
+ pRenderedValues,
562
+ &dwBufferUsed,
563
+ &dwPropertyCount)) {
564
+ status = GetLastError();
565
+ if (ERROR_INSUFFICIENT_BUFFER == status) {
566
+ dwBufferSize = dwBufferUsed;
567
+ pRenderedValues = (PEVT_VARIANT)ALLOCV(vRenderedValues, dwBufferSize);
568
+ if (pRenderedValues) {
569
+ EvtRender(hContext,
570
+ hEvent,
571
+ EvtRenderEventValues,
572
+ dwBufferSize,
573
+ pRenderedValues,
574
+ &dwBufferUsed,
575
+ &dwPropertyCount);
576
+ status = GetLastError();
577
+ } else {
578
+ EvtClose(hContext);
579
+ rb_raise(rb_eRuntimeError, "Failed to malloc memory with %lu\n", status);
580
+ }
581
+ }
582
+
583
+ if (ERROR_SUCCESS != status) {
584
+ EvtClose(hContext);
585
+ ALLOCV_END(vRenderedValues);
586
+
587
+ rb_raise(rb_eWinevtQueryError, "EvtRender failed with %lu\n", status);
588
+ }
589
+ }
590
+
591
+ // EVT_VARIANT value with EvtRenderContextSystem will be decomposed
592
+ // as the following enum definition:
593
+ // https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_system_property_id
594
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemProviderName].StringVal, -1);
595
+ rb_hash_aset(hash, rb_str_new2("ProviderName"), rbstr);
596
+ if (NULL != pRenderedValues[EvtSystemProviderGuid].GuidVal) {
597
+ const GUID* Guid = pRenderedValues[EvtSystemProviderGuid].GuidVal;
598
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
599
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
600
+ rb_hash_aset(hash, rb_str_new2("ProviderGuid"), rbstr);
601
+ } else {
602
+ rb_hash_aset(hash, rb_str_new2("ProviderGuid"), Qnil);
603
+ }
604
+
605
+ EventID = pRenderedValues[EvtSystemEventID].UInt16Val;
606
+ if (preserve_qualifiers) {
607
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
608
+ rb_hash_aset(hash, rb_str_new2("Qualifiers"),
609
+ INT2NUM(pRenderedValues[EvtSystemQualifiers].UInt16Val));
610
+ } else {
611
+ rb_hash_aset(hash, rb_str_new2("Qualifiers"), rb_str_new2(""));
612
+ }
613
+
614
+ rb_hash_aset(hash, rb_str_new2("EventID"), INT2NUM(EventID));
615
+ } else {
616
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
617
+ EventID = MAKELONG(pRenderedValues[EvtSystemEventID].UInt16Val,
618
+ pRenderedValues[EvtSystemQualifiers].UInt16Val);
619
+ }
620
+
621
+ rb_hash_aset(hash, rb_str_new2("EventID"), ULONG2NUM(EventID));
622
+ }
623
+
624
+ rb_hash_aset(hash,
625
+ rb_str_new2("Version"),
626
+ (EvtVarTypeNull == pRenderedValues[EvtSystemVersion].Type)
627
+ ? INT2NUM(0)
628
+ : INT2NUM(pRenderedValues[EvtSystemVersion].ByteVal));
629
+ rb_hash_aset(hash,
630
+ rb_str_new2("Level"),
631
+ (EvtVarTypeNull == pRenderedValues[EvtSystemLevel].Type)
632
+ ? INT2NUM(0)
633
+ : INT2NUM(pRenderedValues[EvtSystemLevel].ByteVal));
634
+ rb_hash_aset(hash,
635
+ rb_str_new2("Task"),
636
+ (EvtVarTypeNull == pRenderedValues[EvtSystemTask].Type)
637
+ ? INT2NUM(0)
638
+ : INT2NUM(pRenderedValues[EvtSystemTask].UInt16Val));
639
+ rb_hash_aset(hash,
640
+ rb_str_new2("Opcode"),
641
+ (EvtVarTypeNull == pRenderedValues[EvtSystemOpcode].Type)
642
+ ? INT2NUM(0)
643
+ : INT2NUM(pRenderedValues[EvtSystemOpcode].ByteVal));
644
+ _snprintf_s(buffer,
645
+ _countof(buffer),
646
+ _TRUNCATE,
647
+ "0x%llx",
648
+ pRenderedValues[EvtSystemKeywords].UInt64Val);
649
+ rb_hash_aset(hash,
650
+ rb_str_new2("Keywords"),
651
+ (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
652
+ ? Qnil
653
+ : rb_str_new2(buffer));
654
+
655
+ ullTimeStamp = pRenderedValues[EvtSystemTimeCreated].FileTimeVal;
656
+ ft.dwHighDateTime = (DWORD)((ullTimeStamp >> 32) & 0xFFFFFFFF);
657
+ ft.dwLowDateTime = (DWORD)(ullTimeStamp & 0xFFFFFFFF);
658
+
659
+ FileTimeToSystemTime(&ft, &st);
660
+ ullNanoseconds =
661
+ (ullTimeStamp % 10000000) *
662
+ 100; // Display nanoseconds instead of milliseconds for higher resolution
663
+ _snprintf_s(buffer,
664
+ _countof(buffer),
665
+ _TRUNCATE,
666
+ "%02d/%02d/%02d %02d:%02d:%02d.%llu",
667
+ st.wYear,
668
+ st.wMonth,
669
+ st.wDay,
670
+ st.wHour,
671
+ st.wMinute,
672
+ st.wSecond,
673
+ ullNanoseconds);
674
+ rb_hash_aset(hash,
675
+ rb_str_new2("TimeCreated"),
676
+ (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
677
+ ? Qnil
678
+ : rb_str_new2(buffer));
679
+ _snprintf_s(buffer,
680
+ _countof(buffer),
681
+ _TRUNCATE,
682
+ "%llu",
683
+ pRenderedValues[EvtSystemEventRecordId].UInt64Val);
684
+ rb_hash_aset(hash,
685
+ rb_str_new2("EventRecordID"),
686
+ (EvtVarTypeNull == pRenderedValues[EvtSystemEventRecordId].UInt64Val)
687
+ ? Qnil
688
+ : rb_str_new2(buffer));
689
+
690
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemActivityID].Type) {
691
+ const GUID* Guid = pRenderedValues[EvtSystemActivityID].GuidVal;
692
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
693
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
694
+ rb_hash_aset(hash, rb_str_new2("ActivityID"), rbstr);
695
+ }
696
+
697
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemRelatedActivityID].Type) {
698
+ const GUID* Guid = pRenderedValues[EvtSystemRelatedActivityID].GuidVal;
699
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
700
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
701
+ rb_hash_aset(hash, rb_str_new2("RelatedActivityID"), rbstr);
702
+ }
703
+
704
+ rb_hash_aset(hash,
705
+ rb_str_new2("ProcessID"),
706
+ UINT2NUM(pRenderedValues[EvtSystemProcessID].UInt32Val));
707
+ rb_hash_aset(hash,
708
+ rb_str_new2("ThreadID"),
709
+ UINT2NUM(pRenderedValues[EvtSystemThreadID].UInt32Val));
710
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemChannel].StringVal, -1);
711
+ rb_hash_aset(hash, rb_str_new2("Channel"), rbstr);
712
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemComputer].StringVal, -1);
713
+ rb_hash_aset(hash, rb_str_new2("Computer"), rbstr);
714
+
715
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemUserID].Type) {
716
+ if (ConvertSidToStringSid(pRenderedValues[EvtSystemUserID].SidVal, &pwsSid)) {
717
+ rbstr = rb_utf8_str_new_cstr(pwsSid);
718
+ rb_hash_aset(hash, rb_str_new2("UserID"), rbstr);
719
+ LocalFree(pwsSid);
720
+ }
721
+ }
722
+
723
+ EvtClose(hContext);
724
+ ALLOCV_END(vRenderedValues);
725
+
726
+ return hash;
727
+ }