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