1 /*
2  * PROJECT:         ReactOS Event Log Viewer
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            base/applications/mscutils/eventvwr/evtdetctl.c
5  * PURPOSE:         Event Details Control
6  * PROGRAMMERS:     Marc Piulachs (marc.piulachs at codexchange [dot] net)
7  *                  Eric Kohl
8  *                  Hermes Belusca-Maito
9  */
10 
11 #include "eventvwr.h"
12 #include "evtdetctl.h"
13 
14 // FIXME:
15 #define EVENT_MESSAGE_EVENTTEXT_BUFFER  1024*10
16 extern HWND hwndListView;
17 extern BOOL
18 GetEventMessage(IN LPCWSTR KeyName,
19                 IN LPCWSTR SourceName,
20                 IN PEVENTLOGRECORD pevlr,
21                 OUT PWCHAR EventText);
22 
23 
24 typedef struct _DETAILDATA
25 {
26     PEVENTLOGFILTER EventLogFilter;
27 
28     BOOL bDisplayWords;
29     HFONT hMonospaceFont;
30 
31     INT cxMin, cyMin;
32     INT cxOld, cyOld;
33     POINT scPos;
34 } DETAILDATA, *PDETAILDATA;
35 
36 
37 static
38 VOID
39 DisplayEvent(HWND hDlg, PEVENTLOGFILTER EventLogFilter)
40 {
41     WCHAR szEventType[MAX_PATH];
42     WCHAR szTime[MAX_PATH];
43     WCHAR szDate[MAX_PATH];
44     WCHAR szUser[MAX_PATH];
45     WCHAR szComputer[MAX_PATH];
46     WCHAR szSource[MAX_PATH];
47     WCHAR szCategory[MAX_PATH];
48     WCHAR szEventID[MAX_PATH];
49     WCHAR szEventText[EVENT_MESSAGE_EVENTTEXT_BUFFER];
50     BOOL bEventData = FALSE;
51     LVITEMW li;
52     PEVENTLOGRECORD pevlr;
53     int iIndex;
54 
55     /* Get index of selected item */
56     iIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED | LVNI_FOCUSED);
57     if (iIndex == -1)
58     {
59         MessageBoxW(hDlg,
60                     L"No Items in ListView",
61                     L"Error",
62                     MB_OK | MB_ICONINFORMATION);
63         return;
64     }
65 
66     li.mask = LVIF_PARAM;
67     li.iItem = iIndex;
68     li.iSubItem = 0;
69 
70     ListView_GetItem(hwndListView, &li);
71 
72     pevlr = (PEVENTLOGRECORD)li.lParam;
73 
74     ListView_GetItemText(hwndListView, iIndex, 0, szEventType, ARRAYSIZE(szEventType));
75     ListView_GetItemText(hwndListView, iIndex, 1, szDate, ARRAYSIZE(szDate));
76     ListView_GetItemText(hwndListView, iIndex, 2, szTime, ARRAYSIZE(szTime));
77     ListView_GetItemText(hwndListView, iIndex, 3, szSource, ARRAYSIZE(szSource));
78     ListView_GetItemText(hwndListView, iIndex, 4, szCategory, ARRAYSIZE(szCategory));
79     ListView_GetItemText(hwndListView, iIndex, 5, szEventID, ARRAYSIZE(szEventID));
80     ListView_GetItemText(hwndListView, iIndex, 6, szUser, ARRAYSIZE(szUser));
81     ListView_GetItemText(hwndListView, iIndex, 7, szComputer, ARRAYSIZE(szComputer));
82 
83     SetDlgItemTextW(hDlg, IDC_EVENTDATESTATIC, szDate);
84     SetDlgItemTextW(hDlg, IDC_EVENTTIMESTATIC, szTime);
85     SetDlgItemTextW(hDlg, IDC_EVENTUSERSTATIC, szUser);
86     SetDlgItemTextW(hDlg, IDC_EVENTSOURCESTATIC, szSource);
87     SetDlgItemTextW(hDlg, IDC_EVENTCOMPUTERSTATIC, szComputer);
88     SetDlgItemTextW(hDlg, IDC_EVENTCATEGORYSTATIC, szCategory);
89     SetDlgItemTextW(hDlg, IDC_EVENTIDSTATIC, szEventID);
90     SetDlgItemTextW(hDlg, IDC_EVENTTYPESTATIC, szEventType);
91 
92     bEventData = (pevlr->DataLength > 0);
93     EnableDlgItem(hDlg, IDC_BYTESRADIO, bEventData);
94     EnableDlgItem(hDlg, IDC_WORDRADIO, bEventData);
95 
96     // FIXME: At the moment we support only one event log in the filter
97     GetEventMessage(EventLogFilter->EventLogs[0]->LogName, szSource, pevlr, szEventText);
98     SetDlgItemTextW(hDlg, IDC_EVENTTEXTEDIT, szEventText);
99 }
100 
101 static
102 UINT
103 PrintByteDataLine(PWCHAR pBuffer, UINT uOffset, PBYTE pData, UINT uLength)
104 {
105     PWCHAR p = pBuffer;
106     UINT n, i, r = 0;
107 
108     if (uOffset != 0)
109     {
110         n = swprintf(p, L"\r\n");
111         p += n;
112         r += n;
113     }
114 
115     n = swprintf(p, L"%04lx:", uOffset);
116     p += n;
117     r += n;
118 
119     for (i = 0; i < uLength; i++)
120     {
121         n = swprintf(p, L" %02x", pData[i]);
122         p += n;
123         r += n;
124     }
125 
126     for (i = 0; i < 9 - uLength; i++)
127     {
128         n = swprintf(p, L"   ");
129         p += n;
130         r += n;
131     }
132 
133     for (i = 0; i < uLength; i++)
134     {
135         // NOTE: Normally iswprint should return FALSE for tabs...
136         n = swprintf(p, L"%c", (iswprint(pData[i]) && (pData[i] != L'\t')) ? pData[i] : L'.');
137         p += n;
138         r += n;
139     }
140 
141     return r;
142 }
143 
144 static
145 UINT
146 PrintWordDataLine(PWCHAR pBuffer, UINT uOffset, PULONG pData, UINT uLength)
147 {
148     PWCHAR p = pBuffer;
149     UINT n, i, r = 0;
150 
151     if (uOffset != 0)
152     {
153         n = swprintf(p, L"\r\n");
154         p += n;
155         r += n;
156     }
157 
158     n = swprintf(p, L"%04lx:", uOffset);
159     p += n;
160     r += n;
161 
162     for (i = 0; i < uLength / sizeof(ULONG); i++)
163     {
164         n = swprintf(p, L" %08lx", pData[i]);
165         p += n;
166         r += n;
167     }
168 
169     /* Display the remaining bytes if uLength was not a multiple of sizeof(ULONG) */
170     for (i = (uLength / sizeof(ULONG)) * sizeof(ULONG); i < uLength; i++)
171     {
172         n = swprintf(p, L" %02x", ((PBYTE)pData)[i]);
173         p += n;
174         r += n;
175     }
176 
177     return r;
178 }
179 
180 static
181 VOID
182 DisplayEventData(HWND hDlg, BOOL bDisplayWords)
183 {
184     LVITEMW li;
185     PEVENTLOGRECORD pevlr;
186     int iIndex;
187 
188     LPBYTE pData;
189     UINT i, uOffset;
190     UINT uBufferSize, uLineLength;
191     PWCHAR pTextBuffer, pLine;
192 
193     /* Get index of selected item */
194     iIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED | LVNI_FOCUSED);
195     if (iIndex == -1)
196     {
197         MessageBoxW(hDlg,
198                     L"No Items in ListView",
199                     L"Error",
200                     MB_OK | MB_ICONINFORMATION);
201         return;
202     }
203 
204     li.mask = LVIF_PARAM;
205     li.iItem = iIndex;
206     li.iSubItem = 0;
207 
208     ListView_GetItem(hwndListView, &li);
209 
210     pevlr = (PEVENTLOGRECORD)li.lParam;
211     if (pevlr->DataLength == 0)
212     {
213         SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, L"");
214         return;
215     }
216 
217     if (bDisplayWords)
218         uBufferSize = ((pevlr->DataLength / 8) + 1) * 26 * sizeof(WCHAR);
219     else
220         uBufferSize = ((pevlr->DataLength / 8) + 1) * 43 * sizeof(WCHAR);
221 
222     pTextBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uBufferSize);
223     if (!pTextBuffer)
224         return;
225 
226     pLine = pTextBuffer;
227     uOffset = 0;
228 
229     for (i = 0; i < pevlr->DataLength / 8; i++)
230     {
231         pData = (LPBYTE)((LPBYTE)pevlr + pevlr->DataOffset + uOffset);
232 
233         if (bDisplayWords)
234             uLineLength = PrintWordDataLine(pLine, uOffset, (PULONG)pData, 8);
235         else
236             uLineLength = PrintByteDataLine(pLine, uOffset, pData, 8);
237         pLine = pLine + uLineLength;
238 
239         uOffset += 8;
240     }
241 
242     if (pevlr->DataLength % 8 != 0)
243     {
244         pData = (LPBYTE)((LPBYTE)pevlr + pevlr->DataOffset + uOffset);
245 
246         if (bDisplayWords)
247             PrintWordDataLine(pLine, uOffset, (PULONG)pData, pevlr->DataLength % 8);
248         else
249             PrintByteDataLine(pLine, uOffset, pData, pevlr->DataLength % 8);
250     }
251 
252     SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, pTextBuffer);
253 
254     HeapFree(GetProcessHeap(), 0, pTextBuffer);
255 }
256 
257 static
258 HFONT
259 CreateMonospaceFont(VOID)
260 {
261     LOGFONTW tmpFont = {0};
262     HFONT hFont;
263     HDC hDC;
264 
265     hDC = GetDC(NULL);
266 
267     tmpFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72);
268     tmpFont.lfWeight = FW_NORMAL;
269     wcscpy(tmpFont.lfFaceName, L"Courier New");
270 
271     hFont = CreateFontIndirectW(&tmpFont);
272 
273     ReleaseDC(NULL, hDC);
274 
275     return hFont;
276 }
277 
278 static
279 VOID
280 CopyEventEntry(HWND hWnd)
281 {
282     WCHAR tmpHeader[512];
283     WCHAR szEventType[MAX_PATH];
284     WCHAR szSource[MAX_PATH];
285     WCHAR szCategory[MAX_PATH];
286     WCHAR szEventID[MAX_PATH];
287     WCHAR szDate[MAX_PATH];
288     WCHAR szTime[MAX_PATH];
289     WCHAR szUser[MAX_PATH];
290     WCHAR szComputer[MAX_PATH];
291     WCHAR evtDesc[EVENT_MESSAGE_EVENTTEXT_BUFFER];
292     ULONG size = 0;
293     LPWSTR output;
294     HGLOBAL hMem;
295 
296     /* Try to open the clipboard */
297     if (!OpenClipboard(hWnd))
298         return;
299 
300     /* Get the formatted text needed to place the content into */
301     size += LoadStringW(hInst, IDS_COPY, tmpHeader, ARRAYSIZE(tmpHeader));
302 
303     /* Grab all the information and get it ready for the clipboard */
304     size += GetDlgItemTextW(hWnd, IDC_EVENTTYPESTATIC, szEventType, ARRAYSIZE(szEventType));
305     size += GetDlgItemTextW(hWnd, IDC_EVENTSOURCESTATIC, szSource, ARRAYSIZE(szSource));
306     size += GetDlgItemTextW(hWnd, IDC_EVENTCATEGORYSTATIC, szCategory, ARRAYSIZE(szCategory));
307     size += GetDlgItemTextW(hWnd, IDC_EVENTIDSTATIC, szEventID, ARRAYSIZE(szEventID));
308     size += GetDlgItemTextW(hWnd, IDC_EVENTDATESTATIC, szDate, ARRAYSIZE(szDate));
309     size += GetDlgItemTextW(hWnd, IDC_EVENTTIMESTATIC, szTime, ARRAYSIZE(szTime));
310     size += GetDlgItemTextW(hWnd, IDC_EVENTUSERSTATIC, szUser, ARRAYSIZE(szUser));
311     size += GetDlgItemTextW(hWnd, IDC_EVENTCOMPUTERSTATIC, szComputer, ARRAYSIZE(szComputer));
312     size += GetDlgItemTextW(hWnd, IDC_EVENTTEXTEDIT, evtDesc, ARRAYSIZE(evtDesc));
313 
314     size++; /* Null-termination */
315     size *= sizeof(WCHAR);
316 
317     /*
318      * Consolidate the information into one big piece and
319      * sort out the memory needed to write to the clipboard.
320      */
321     hMem = GlobalAlloc(GMEM_MOVEABLE, size);
322     if (hMem == NULL) goto Quit;
323 
324     output = GlobalLock(hMem);
325     if (output == NULL)
326     {
327         GlobalFree(hMem);
328         goto Quit;
329     }
330 
331     StringCbPrintfW(output, size,
332                     tmpHeader, szEventType, szSource, szCategory, szEventID,
333                     szDate, szTime, szUser, szComputer, evtDesc);
334 
335     GlobalUnlock(hMem);
336 
337     /* We succeeded, empty the clipboard and write the data in it */
338     EmptyClipboard();
339     SetClipboardData(CF_UNICODETEXT, hMem);
340 
341 Quit:
342     /* Close the clipboard once we are done with it */
343     CloseClipboard();
344 }
345 
346 static VOID
347 OnScroll(HWND hDlg, PDETAILDATA pData, INT nBar, WORD sbCode)
348 {
349     RECT rect;
350 
351     SCROLLINFO sInfo;
352     INT oldPos, Maximum;
353     PLONG pOriginXY;
354 
355     ASSERT(nBar == SB_HORZ || nBar == SB_VERT);
356 
357     GetClientRect(hDlg, &rect);
358 
359     if (nBar == SB_HORZ)
360     {
361         Maximum = pData->cxMin - (rect.right-rect.left) /* pData->cxOld */;
362         pOriginXY = &pData->scPos.x;
363     }
364     else // if (nBar == SB_VERT)
365     {
366         Maximum = pData->cyMin - (rect.bottom-rect.top) /* pData->cyOld */;
367         pOriginXY = &pData->scPos.y;
368     }
369 
370     /* Set scrollbar sizes */
371     sInfo.cbSize = sizeof(sInfo);
372     sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
373 
374     if (!GetScrollInfo(hDlg, nBar, &sInfo))
375         return;
376 
377     oldPos = sInfo.nPos;
378 
379     switch (sbCode)
380     {
381         case SB_LINEUP:   // SB_LINELEFT:
382             sInfo.nPos--;
383             break;
384 
385         case SB_LINEDOWN: // SB_LINERIGHT:
386             sInfo.nPos++;
387             break;
388 
389         case SB_PAGEUP:   // SB_PAGELEFT:
390             sInfo.nPos -= sInfo.nPage;
391             break;
392 
393         case SB_PAGEDOWN: // SB_PAGERIGHT:
394             sInfo.nPos += sInfo.nPage;
395             break;
396 
397         case SB_THUMBTRACK:
398             sInfo.nPos = sInfo.nTrackPos;
399             break;
400 
401         case SB_THUMBPOSITION:
402             sInfo.nPos = sInfo.nTrackPos;
403             break;
404 
405         case SB_TOP:    // SB_LEFT:
406             sInfo.nPos = sInfo.nMin;
407             break;
408 
409         case SB_BOTTOM: // SB_RIGHT:
410             sInfo.nPos = sInfo.nMax;
411             break;
412 
413         default:
414             break;
415     }
416 
417     sInfo.nPos = min(max(sInfo.nPos, 0), Maximum);
418 
419     if (oldPos != sInfo.nPos)
420     {
421         POINT scOldPos = pData->scPos;
422 
423         /* We now modify pData->scPos */
424         *pOriginXY = sInfo.nPos;
425 
426         ScrollWindowEx(hDlg,
427                        (scOldPos.x - pData->scPos.x),
428                        (scOldPos.y - pData->scPos.y),
429                        NULL,
430                        NULL,
431                        NULL,
432                        NULL,
433                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
434 
435         sInfo.fMask = SIF_POS;
436         SetScrollInfo(hDlg, nBar, &sInfo, TRUE);
437 
438         // UpdateWindow(hDlg);
439     }
440 }
441 
442 static VOID
443 OnSize(HWND hDlg, PDETAILDATA pData, INT cx, INT cy)
444 {
445     LONG_PTR dwStyle;
446     INT sbVXSize, sbHYSize;
447     SCROLLINFO sInfo;
448     POINT scOldPos;
449     HDWP hdwp;
450     HWND hItemWnd;
451     RECT rect;
452     INT  y = 0;
453 
454     if (!pData)
455         return;
456 
457     dwStyle  = GetWindowLongPtrW(hDlg, GWL_STYLE);
458     sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
459     sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
460 
461     /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
462     if (dwStyle & WS_HSCROLL) cy += sbHYSize; // Window currently has a horizontal scrollbar
463     if (dwStyle & WS_VSCROLL) cx += sbVXSize; // Window currently has a vertical scrollbar
464 
465     /* Compensate for added scroll bars in window */
466     if (cx < pData->cxMin) cy -= sbHYSize; // Window will have a horizontal scroll bar
467     if (cy < pData->cyMin) cx -= sbVXSize; // Window will have a vertical scroll bar
468 
469     /* Set scrollbar sizes */
470     sInfo.cbSize = sizeof(sInfo);
471 
472     sInfo.fMask = SIF_POS;
473     if (GetScrollInfo(hDlg, SB_VERT, &sInfo))
474         scOldPos.y = sInfo.nPos;
475     else
476         scOldPos.y = pData->scPos.y;
477 
478     sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
479     sInfo.nMin = 0;
480     if (pData->cyMin > cy)
481     {
482         sInfo.nMax  = pData->cyMin - 1;
483         sInfo.nPage = cy;
484         sInfo.nPos  = pData->scPos.y;
485         SetScrollInfo(hDlg, SB_VERT, &sInfo, TRUE);
486 
487         /* Display the scrollbar if needed */
488         if (!(dwStyle & WS_VSCROLL))
489             ShowScrollBar(hDlg, SB_VERT, TRUE);
490     }
491     else
492     {
493         scOldPos.y = 0;
494 
495         sInfo.nMax  = pData->cyMin - 1;
496         sInfo.nPage = cy;
497         sInfo.nPos  = pData->scPos.y;
498         sInfo.nPos  = scOldPos.y;
499         SetScrollInfo(hDlg, SB_VERT, &sInfo, TRUE);
500 
501         ShowScrollBar(hDlg, SB_VERT, FALSE);
502 
503         rect.left   = cx - sbVXSize;
504         rect.right  = cx;
505         rect.top    = 0;
506         rect.bottom = cy;
507         InvalidateRect(hDlg, &rect, TRUE);
508     }
509 
510     sInfo.fMask = SIF_POS;
511     if (GetScrollInfo(hDlg, SB_HORZ, &sInfo))
512         scOldPos.x = sInfo.nPos;
513     else
514         scOldPos.x = pData->scPos.x;
515 
516     sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
517     sInfo.nMin = 0;
518     if (pData->cxMin > cx)
519     {
520         sInfo.nMax  = pData->cxMin - 1;
521         sInfo.nPage = cx;
522         sInfo.nPos  = pData->scPos.x;
523         SetScrollInfo(hDlg, SB_HORZ, &sInfo, TRUE);
524 
525         /* Display the scrollbar if needed */
526         if (!(dwStyle & WS_HSCROLL))
527             ShowScrollBar(hDlg, SB_HORZ, TRUE);
528     }
529     else
530     {
531         scOldPos.x = 0;
532 
533         sInfo.nMax  = pData->cxMin - 1;
534         sInfo.nPage = cx;
535         sInfo.nPos  = pData->scPos.x;
536         sInfo.nPos  = scOldPos.x;
537         SetScrollInfo(hDlg, SB_HORZ, &sInfo, TRUE);
538 
539         ShowScrollBar(hDlg, SB_HORZ, FALSE);
540 
541         rect.left   = 0;
542         rect.right  = cx;
543         rect.top    = cy - sbHYSize;
544         rect.bottom = cy;
545         InvalidateRect(hDlg, &rect, TRUE);
546     }
547 
548     if ((scOldPos.x != pData->scPos.x) || (scOldPos.y != pData->scPos.y))
549     {
550         ScrollWindowEx(hDlg,
551                        // (scOldPos.x - pData->scPos.x),
552                        (pData->scPos.x - scOldPos.x),
553                        // (scOldPos.y - pData->scPos.y),
554                        (pData->scPos.y - scOldPos.y),
555                        NULL,
556                        NULL,
557                        NULL,
558                        NULL,
559                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
560 
561         pData->scPos = scOldPos;
562     }
563 
564     // /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
565     // if ((pData->cxMin - pData->scPos.x) < cx) pData->scPos.x = pData->cxMin - cx;
566     // if ((pData->cyMin - pData->scPos.y) < cy) pData->scPos.y = pData->cyMin - cy;
567     // // InvalidateRect(GuiData->hWindow, NULL, TRUE);
568 
569     /* Forbid resizing the control smaller than its minimal size */
570     if (cx < pData->cxMin) cx = pData->cxMin;
571     if (cy < pData->cyMin) cy = pData->cyMin;
572 
573     if ((cx != pData->cxOld) || (cy != pData->cyOld))
574     {
575         hdwp = BeginDeferWindowPos(8);
576 
577         /* Move the edit boxes */
578 
579         GetWindowRect(hDlg, &rect);
580 
581         hItemWnd = GetDlgItem(hDlg, IDC_EVENTTEXTEDIT);
582         GetWindowRect(hItemWnd, &rect);
583         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
584         // OffsetRect(&rect, 0, y);
585         // y += (cy - pData->cyOld) / 2 ; // + (cy - pData->cyOld) % 2;
586         /** y += (cy - pData->cyOld) / 2 ; // + (cy - pData->cyOld) % 2; **/
587         if (cy >= pData->cyOld)
588             y += (cy - pData->cyOld) / 2 + (cy - pData->cyOld) % 2;
589         else
590             y -= (pData->cyOld - cy) / 2 + (pData->cyOld - cy) % 2;
591 
592         if (hdwp)
593             hdwp = DeferWindowPos(hdwp,
594                                   hItemWnd,
595                                   0,
596                                   rect.left, rect.top,
597                                   (rect.right - rect.left) + (cx - pData->cxOld),
598                                   (rect.bottom - rect.top) + y,
599                                   /** SWP_NOMOVE | **/ SWP_NOZORDER | SWP_NOACTIVATE);
600 
601         hItemWnd = GetDlgItem(hDlg, IDC_DETAILS_STATIC);
602         GetWindowRect(hItemWnd, &rect);
603         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
604         // OffsetRect(&rect, 0, y);
605 
606         if (hdwp)
607             hdwp = DeferWindowPos(hdwp,
608                                   hItemWnd,
609                                   0,
610                                   rect.left, rect.top + y,
611                                   0, 0,
612                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
613 
614         hItemWnd = GetDlgItem(hDlg, IDC_BYTESRADIO);
615         GetWindowRect(hItemWnd, &rect);
616         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
617         // OffsetRect(&rect, 0, y);
618 
619         if (hdwp)
620             hdwp = DeferWindowPos(hdwp,
621                                   hItemWnd,
622                                   0,
623                                   rect.left, rect.top + y,
624                                   0, 0,
625                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
626 
627         hItemWnd = GetDlgItem(hDlg, IDC_WORDRADIO);
628         GetWindowRect(hItemWnd, &rect);
629         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
630         // OffsetRect(&rect, 0, y);
631 
632         if (hdwp)
633             hdwp = DeferWindowPos(hdwp,
634                                   hItemWnd,
635                                   0,
636                                   rect.left, rect.top + y,
637                                   0, 0,
638                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
639 
640         hItemWnd = GetDlgItem(hDlg, IDC_EVENTDATAEDIT);
641         GetWindowRect(hItemWnd, &rect);
642         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
643         // OffsetRect(&rect, 0, y);
644         // // y -= (cy - pData->cyOld) % 2;
645 
646         if (hdwp)
647             hdwp = DeferWindowPos(hdwp,
648                                   hItemWnd,
649                                   0,
650                                   rect.left, rect.top + y,
651                                   (rect.right - rect.left) + (cx - pData->cxOld),
652                                   (rect.bottom - rect.top) + y,
653                                   SWP_NOZORDER | SWP_NOACTIVATE);
654 
655         /* Move the buttons */
656 
657         hItemWnd = GetDlgItem(hDlg, IDC_PREVIOUS);
658         GetWindowRect(hItemWnd, &rect);
659         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
660 
661         if (hdwp)
662             hdwp = DeferWindowPos(hdwp,
663                                   hItemWnd,
664                                   0,
665                                   rect.left + (cx - pData->cxOld),
666                                   rect.top,
667                                   0, 0,
668                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
669 
670         hItemWnd = GetDlgItem(hDlg, IDC_NEXT);
671         GetWindowRect(hItemWnd, &rect);
672         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
673 
674         if (hdwp)
675             hdwp = DeferWindowPos(hdwp,
676                                   hItemWnd,
677                                   0,
678                                   rect.left + (cx - pData->cxOld),
679                                   rect.top,
680                                   0, 0,
681                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
682 
683         hItemWnd = GetDlgItem(hDlg, IDC_COPY);
684         GetWindowRect(hItemWnd, &rect);
685         MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
686 
687         if (hdwp)
688             hdwp = DeferWindowPos(hdwp,
689                                   hItemWnd,
690                                   0,
691                                   rect.left + (cx - pData->cxOld),
692                                   rect.top,
693                                   0, 0,
694                                   SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
695 
696         if (hdwp)
697             EndDeferWindowPos(hdwp);
698 
699         pData->cxOld = cx;
700         pData->cyOld = cy;
701     }
702 }
703 
704 static
705 VOID
706 InitDetailsDlgCtrl(HWND hDlg, PDETAILDATA pData)
707 {
708     DWORD dwMask;
709 
710     HANDLE nextIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_NEXT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
711     HANDLE prevIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_PREV), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
712     HANDLE copyIcon = LoadImageW(hInst, MAKEINTRESOURCEW(IDI_COPY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
713 
714     SendDlgItemMessageW(hDlg, IDC_NEXT, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)nextIcon);
715     SendDlgItemMessageW(hDlg, IDC_PREVIOUS, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)prevIcon);
716     SendDlgItemMessageW(hDlg, IDC_COPY, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)copyIcon);
717 
718     /* Set the default read-only RichEdit color */
719     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_3DFACE));
720 
721     /* Enable RichEdit coloured and underlined links */
722     dwMask = SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_GETEVENTMASK, 0, 0);
723     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_SETEVENTMASK, 0, dwMask | ENM_LINK | ENM_MOUSEEVENTS);
724 
725     /*
726      * Activate automatic URL recognition by the RichEdit control. For more information, see:
727      * https://blogs.msdn.microsoft.com/murrays/2009/08/31/automatic-richedit-hyperlinks/
728      * https://blogs.msdn.microsoft.com/murrays/2009/09/24/richedit-friendly-name-hyperlinks/
729      * https://msdn.microsoft.com/en-us/library/windows/desktop/bb787991(v=vs.85).aspx
730      */
731     SendDlgItemMessageW(hDlg, IDC_EVENTTEXTEDIT, EM_AUTOURLDETECT, AURL_ENABLEURL /* | AURL_ENABLEEAURLS */, 0);
732 
733     /* Note that the RichEdit control never gets themed under WinXP+. One would have to write code to simulate Edit-control theming */
734 
735     SendDlgItemMessageW(hDlg, pData->bDisplayWords ? IDC_WORDRADIO : IDC_BYTESRADIO, BM_SETCHECK, BST_CHECKED, 0);
736     SendDlgItemMessageW(hDlg, IDC_EVENTDATAEDIT, WM_SETFONT, (WPARAM)pData->hMonospaceFont, (LPARAM)TRUE);
737 }
738 
739 /* Message handler for Event Details control */
740 static
741 INT_PTR CALLBACK
742 EventDetailsCtrl(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
743 {
744     PDETAILDATA pData;
745 
746     pData = (PDETAILDATA)GetWindowLongPtrW(hDlg, DWLP_USER);
747 
748     switch (uMsg)
749     {
750         case WM_INITDIALOG:
751         {
752             RECT rect;
753 
754             pData = (PDETAILDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pData));
755             if (!pData)
756             {
757                 EndDialog(hDlg, 0);
758                 return (INT_PTR)TRUE;
759             }
760             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)pData);
761 
762             pData->EventLogFilter = (PEVENTLOGFILTER)lParam;
763             pData->bDisplayWords  = FALSE;
764             pData->hMonospaceFont = CreateMonospaceFont();
765 
766             GetClientRect(hDlg, &rect);
767             pData->cxOld = pData->cxMin = rect.right - rect.left;
768             pData->cyOld = pData->cyMin = rect.bottom - rect.top;
769             pData->scPos.x = pData->scPos.y = 0;
770 
771             InitDetailsDlgCtrl(hDlg, pData);
772 
773 #if 0
774             /* Show event info on dialog box */
775             DisplayEvent(hDlg, pData->EventLogFilter);
776             DisplayEventData(hDlg, pData->bDisplayWords);
777 #endif
778 
779             // OnSize(hDlg, pData, pData->cxOld, pData->cyOld);
780             return (INT_PTR)TRUE;
781         }
782 
783         case WM_DESTROY:
784             if (pData)
785             {
786                 if (pData->hMonospaceFont)
787                     DeleteObject(pData->hMonospaceFont);
788                 HeapFree(GetProcessHeap(), 0, pData);
789             }
790             return (INT_PTR)TRUE;
791 
792         case EVT_SETFILTER:
793             pData->EventLogFilter = (PEVENTLOGFILTER)lParam;
794             return (INT_PTR)TRUE;
795 
796         case EVT_DISPLAY:
797             if (pData->EventLogFilter)
798             {
799                 /* Show event info on dialog box */
800                 DisplayEvent(hDlg, pData->EventLogFilter);
801                 DisplayEventData(hDlg, pData->bDisplayWords);
802             }
803             return (INT_PTR)TRUE;
804 
805         case WM_COMMAND:
806             switch (LOWORD(wParam))
807             {
808                 case IDC_PREVIOUS:
809                 {
810                     SendMessageW(hwndListView, WM_KEYDOWN, VK_UP, 0);
811 
812                     /* Show event info on dialog box */
813                     if (pData->EventLogFilter)
814                     {
815                         DisplayEvent(hDlg, pData->EventLogFilter);
816                         DisplayEventData(hDlg, pData->bDisplayWords);
817                     }
818                     return (INT_PTR)TRUE;
819                 }
820 
821                 case IDC_NEXT:
822                 {
823                     SendMessageW(hwndListView, WM_KEYDOWN, VK_DOWN, 0);
824 
825                     /* Show event info on dialog box */
826                     if (pData->EventLogFilter)
827                     {
828                         DisplayEvent(hDlg, pData->EventLogFilter);
829                         DisplayEventData(hDlg, pData->bDisplayWords);
830                     }
831                     return (INT_PTR)TRUE;
832                 }
833 
834                 case IDC_COPY:
835                     if (pData->EventLogFilter)
836                         CopyEventEntry(hDlg);
837                     return (INT_PTR)TRUE;
838 
839                 case IDC_BYTESRADIO:
840                     if (pData->EventLogFilter)
841                     {
842                         pData->bDisplayWords = FALSE;
843                         DisplayEventData(hDlg, pData->bDisplayWords);
844                     }
845                     return (INT_PTR)TRUE;
846 
847                 case IDC_WORDRADIO:
848                     if (pData->EventLogFilter)
849                     {
850                         pData->bDisplayWords = TRUE;
851                         DisplayEventData(hDlg, pData->bDisplayWords);
852                     }
853                     return (INT_PTR)TRUE;
854 
855                 default:
856                     break;
857             }
858             break;
859 
860         case WM_NOTIFY:
861             switch (((LPNMHDR)lParam)->code)
862             {
863                 case EN_LINK:
864                     // TODO: Act on the activated RichEdit link!
865                     break;
866             }
867             break;
868 
869         case WM_HSCROLL:
870             OnScroll(hDlg, pData, SB_HORZ, LOWORD(wParam));
871             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
872             return (INT_PTR)TRUE;
873 
874         case WM_VSCROLL:
875             OnScroll(hDlg, pData, SB_VERT, LOWORD(wParam));
876             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
877             return (INT_PTR)TRUE;
878 
879         case WM_SIZE:
880             OnSize(hDlg, pData, LOWORD(lParam), HIWORD(lParam));
881             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
882             return (INT_PTR)TRUE;
883     }
884 
885     return (INT_PTR)FALSE;
886 }
887 
888 HWND
889 CreateEventDetailsCtrl(HINSTANCE hInstance,
890                        HWND hParentWnd,
891                        LPARAM lParam)
892 {
893     return CreateDialogParamW(hInstance,
894                               MAKEINTRESOURCEW(IDD_EVENTDETAILS_CTRL),
895                               hParentWnd, EventDetailsCtrl, lParam);
896 }
897