winevt_c 0.5.1 → 0.6.0

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