source: trunk/MFCtooling/userlog/userlog.cpp @ 32

Last change on this file since 32 was 32, checked in by sherbold, 14 years ago
File size: 16.8 KB
Line 
1#include "stdafx.h"
2#include "userlog.h"
3
4#include <map>
5#include <cstdio>
6
7#ifdef __USING_MTRACE__
8#define OS_WIN32
9#include "msl/merror/inc/trace.h"
10
11#ifdef __ENCODE_BASE64__
12#include  "encode.h"
13#endif
14
15const static int traceLevel = 3;
16static int level;
17#endif
18
19#ifdef __USING_COSTUMLOG__
20static std::ofstream logfile;
21#endif
22
23#ifdef __TIMING__
24static unsigned long long timing = 0;
25static unsigned long long msgCounter = 0;
26static unsigned long long totalMsgCounter = 0;
27static bool msgCounterChange = false;
28#endif
29
30static MSG lastmsg;
31
32HANDLE mutex;
33
34
35
36USERLOG_API void __cdecl InitUsagelog() {
37
38        mutex = CreateMutex(NULL, FALSE, TEXT("USAGELOGMUTEX"));
39        if( mutex == NULL ) {
40                MessageBox(0, L"MutexFailure", L"MutexFailure", 0);
41        }
42
43#ifdef __USING_COSTUMLOG__
44        InitLogfile();
45#endif
46        InitHookdata();
47        InitHooks();
48#ifdef __USING_MTRACE__
49        MTrace_AddToLevel(traceLevel,"%s<session>", LOGPREFIX);
50#endif
51#ifdef __USING_COSTUMLOG__
52        logfile << LOGPREFIX << "<session>" << std::endl;
53#endif
54}
55
56USERLOG_API void __cdecl ReleaseUsagelog() {
57        ReleaseHooks();
58#ifdef __USING_MTRACE__
59#ifdef __TIMING__
60        char * mtraceBuffer = new char[128];
61        sprintf(mtraceBuffer, "ul timing: %llu \t\t %llu \t\t %llu", timing, msgCounter, totalMsgCounter);
62        MTrace_AddToLevel(traceLevel,mtraceBuffer);
63        delete mtraceBuffer;
64        msgCounterChange = false;
65#endif
66        MTrace_AddToLevel(traceLevel,"%s</session>", LOGPREFIX);
67#endif
68#ifdef __USING_COSTUMLOG__
69        logfile << LOGPREFIX << "</session>" << std::endl;
70        CloseLogfile();
71#endif
72}
73
74#ifdef __USING_COSTUMLOG__
75void InitLogfile() {
76        logfile.open(LOGFILE, std::ios_base::app);
77        if( logfile.fail() ) {
78                MessageBox(0, L"Logfile could not be opend", L"Error", MB_OK);
79        }
80}
81#endif
82
83#ifdef __USING_COSTUMLOG__
84void CloseLogfile() {
85        logfile.close();
86}
87#endif
88
89void InitHookdata() {
90        myhookdata[CALLWNDHOOKID].nType = WH_CALLWNDPROC;
91        myhookdata[CALLWNDHOOKID].hkproc = CallWndProc;
92        myhookdata[GETMSGHOOKID].nType = WH_GETMESSAGE;
93        myhookdata[GETMSGHOOKID].hkproc = GetMsgProc;
94}
95
96void InitHooks() {
97        for( int i=0 ; i<NUMHOOKS ; i++ ) {
98                myhookdata[i].hookhandle = SetWindowsHookEx(myhookdata[i].nType, myhookdata[i].hkproc, (HINSTANCE) NULL, GetCurrentThreadId());
99                if( myhookdata[i].hookhandle!=NULL ) {
100                        myhookdata[i].active = true;
101                } else {
102                        myhookdata[i].active = false;
103                }
104        }
105}
106
107void ReleaseHooks() {
108        int counter = 0;
109        for( int i=0 ; i<NUMHOOKS ; i++ ) {
110                if( UnhookWindowsHookEx(myhookdata[i].hookhandle) ) counter++;
111        }
112}
113
114
115LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
116       
117        PCWPSTRUCT cwpMsg = (PCWPSTRUCT) lParam;
118        // Create a MSG struct from the cwpMsg struct
119        // The missing parameters are filled with dummy values
120        MSG msg;
121        msg.hwnd = cwpMsg->hwnd;
122        msg.message = cwpMsg->message;
123        msg.lParam = cwpMsg->lParam;
124        msg.wParam = cwpMsg->wParam;
125        msg.pt.x = -1;
126        msg.pt.y = -1;
127        msg.time = -1;
128        if( !MessageEquals(lastmsg, msg) ) {
129                lastmsg = msg;
130                HookProc(CALLWNDHOOKID, nCode, &msg);
131        }
132
133        return CallNextHookEx(myhookdata[CALLWNDHOOKID].hookhandle, nCode, wParam, lParam);
134}
135
136LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
137        PMSG msg = (PMSG) lParam;
138        if( !MessageEquals(*msg, lastmsg)) {
139                lastmsg = *msg;
140                HookProc(GETMSGHOOKID,nCode, msg);
141        }
142        return CallNextHookEx(myhookdata[GETMSGHOOKID].hookhandle, nCode, wParam, lParam);
143}
144
145void HookProc(int nFrom, int nCode, PMSG msg) {
146#ifdef __TIMING__
147        SYSTEMTIME systemTime;
148        GetSystemTime( &systemTime );
149        int startTime = systemTime.wMilliseconds+systemTime.wSecond*1000;
150#endif
151
152        DWORD waitResult;
153
154        // inverse filter: defined messages will be filtered, all else just passes through
155        // may be replaced with a lookup-table to improve perfomance
156        // upon completion of the rules, i.e., when it is clear which messages are required,
157        // this should be changed to a "normal" filter.
158        switch(msg->message) {
159                case WM_NULL:
160                case WM_MOVE:
161                case WM_SIZE:
162                case WM_GETTEXT:
163                case WM_GETTEXTLENGTH:
164                case WM_PAINT:
165                case WM_ERASEBKGND:
166                case WM_SHOWWINDOW:
167                case WM_CANCELMODE:
168                case WM_SETCURSOR:
169                case WM_GETMINMAXINFO:
170                case WM_GETFONT:
171                case WM_WINDOWPOSCHANGING:
172                case WM_WINDOWPOSCHANGED:
173                case WM_NOTIFY:
174                case WM_STYLECHANGING:
175                case WM_STYLECHANGED:
176                case WM_GETICON:
177                case WM_NCCREATE:
178                case WM_NCDESTROY:
179                case WM_NCCALCSIZE:
180                case WM_NCHITTEST:
181                case WM_NCPAINT:
182                case WM_GETDLGCODE:
183                case 0x0090: // WM_UAHDESTROYWINDOW
184                case 0x0091: // WM_UAHDRAWMENU
185                case 0x0092: // WM_UADRAWMENUITEM
186                case 0x0093: // WM_UAHINITMENU
187                case 0x0094: // WM_UAHMEASUREMENUITEM
188                case 0x0095: // WM_UAHNCPAINTMENUPOPUP
189                case WM_NCMOUSEMOVE:
190                case WM_TIMER:
191                case WM_ENTERIDLE:
192                case WM_CTLCOLORMSGBOX:
193                case WM_CTLCOLOREDIT:
194                case WM_CTLCOLORLISTBOX:
195                case WM_CTLCOLORBTN:
196                case WM_CTLCOLORDLG:
197                case WM_CTLCOLORSCROLLBAR:
198                case WM_CTLCOLORSTATIC:
199                case WM_MOUSEMOVE:
200                case WM_PARENTNOTIFY:
201                case WM_MDIGETACTIVE:
202                case WM_IME_NOTIFY:
203                case WM_IME_SETCONTEXT:
204                case WM_AFXFIRST:
205                case WM_AFXFIRST+1:
206                case WM_AFXFIRST+2:
207                case WM_AFXFIRST+3:
208                case WM_AFXFIRST+4:
209                case WM_AFXFIRST+5:
210                case WM_AFXFIRST+6:
211                case WM_AFXFIRST+7:
212                case WM_AFXFIRST+8:
213                case WM_AFXFIRST+9:
214                case WM_AFXFIRST+10:
215                case WM_AFXFIRST+11:
216                case WM_AFXFIRST+12:
217                case WM_AFXFIRST+13:
218                case WM_AFXFIRST+14:
219                case WM_AFXFIRST+15:
220                case WM_AFXFIRST+16:
221                case WM_AFXFIRST+17:
222                case WM_AFXFIRST+18:
223                case WM_AFXFIRST+19:
224                case WM_AFXFIRST+20:
225                case WM_AFXFIRST+21:
226                case WM_AFXFIRST+22:
227                case WM_AFXFIRST+23:
228                case WM_AFXFIRST+24:
229                case WM_AFXFIRST+25:
230                case WM_AFXFIRST+26:
231                case WM_AFXFIRST+27:
232                case WM_AFXFIRST+28:
233                case WM_AFXFIRST+29:
234                case WM_AFXFIRST+30:
235                case WM_AFXLAST:
236                case 1025:
237                case 1031:
238                case 1142:
239                case 2024:
240                case 4100:
241                case 4101:
242                case 4103:
243                case 4352:
244                case 4362:
245                case 4363:
246                case 4364:
247                case 4365:
248                case 4372:
249                case 4613:
250                        break;
251                default:
252                        // exclude messages 0xC000-0xFFFF
253                        if( !(msg->message>=0xC000 && msg->message<=0xFFFF) ) {
254                                waitResult = WaitForSingleObject(mutex, 1000);
255                                if( waitResult==WAIT_OBJECT_0 ) {
256                                        WriteLogentryWString(msg, nFrom);
257                                        ReleaseMutex(mutex);
258                                }
259
260#ifdef __TIMING__
261                                msgCounter++;
262                                msgCounterChange = true;
263#endif // __TIMING__
264                        }
265                        break;
266        }
267#ifdef __TIMING__
268        GetSystemTime( &systemTime );
269        timing += systemTime.wMilliseconds+systemTime.wSecond*1000-startTime;
270        totalMsgCounter++;
271        if( msgCounterChange && msgCounter%100==0 ) {
272#ifdef __USING_MTRACE__
273                char * mtraceBuffer = new char[128];
274                sprintf(mtraceBuffer, "ul timing: %llu \t\t %llu \t\t %llu", timing, msgCounter, totalMsgCounter);
275                MTrace_AddToLevel(traceLevel,mtraceBuffer);
276                delete mtraceBuffer;
277                msgCounterChange = false;
278#endif // __USING_MTRACE__
279        }
280#endif // __TIMING__
281}
282
283
284///////////////////////////////////////////////////////////
285// 2 Byte character functions
286///////////////////////////////////////////////////////////
287
288bool MessageEquals(const MSG & msg1, const MSG & msg2) {
289        bool retVal = false;
290        if( (msg1.time==-1 && msg1.pt.x==-1 && msg1.pt.y==-1) || (msg2.time==-1 && msg2.pt.x==-1 && msg2.pt.y==-1) ) {
291                retVal = msg1.hwnd==msg2.hwnd && msg1.message==msg2.message && msg1.lParam==msg2.lParam &&
292                        msg1.wParam==msg2.wParam;
293        } else {
294                retVal = msg1.hwnd==msg2.hwnd && msg1.message==msg2.message && msg1.lParam==msg2.lParam &&
295                        msg1.wParam==msg2.wParam && msg1.time==msg2.time && msg1.pt.x==msg2.pt.x && msg1.pt.y==msg2.pt.y;
296        }
297        return retVal;
298}
299
300int replaceWithXmlEntitiesWString(const wchar_t * source, wchar_t ** target, size_t sourceLength) {
301        size_t j=0;
302        size_t extraLength = 0;
303        size_t bufsize = 300;
304        wchar_t * tmpTarget = new wchar_t[sourceLength+bufsize];
305        size_t i;
306        for( i=0; i<sourceLength && j<sourceLength+bufsize-5; i++ ) {
307                switch (source[i]) {
308                        case L'&':
309                                tmpTarget[j] = L'&';
310                                tmpTarget[j+1]=L'a';
311                                tmpTarget[j+2]=L'm';
312                                tmpTarget[j+3]=L'p';
313                                tmpTarget[j+4]=L';';
314                                j += 5;
315                                extraLength += 4;
316                                break;
317                        case L'<':
318                                tmpTarget[j] = L'&';
319                                tmpTarget[j+1]=L'l';
320                                tmpTarget[j+2]=L't';
321                                tmpTarget[j+3]=L';';
322                                j += 4;
323                                extraLength += 3;
324                                break;
325                        case L'>':
326                                tmpTarget[j] = L'&';
327                                tmpTarget[j+1]=L'g';
328                                tmpTarget[j+2]=L't';
329                                tmpTarget[j+3]=L';';
330                                j += 4;
331                                extraLength += 3;
332                                break;
333                        case L'\"':
334                                tmpTarget[j] = L'&';
335                                tmpTarget[j+1]=L'q';
336                                tmpTarget[j+2]=L'u';
337                                tmpTarget[j+3]=L'o';
338                                tmpTarget[j+4]=L't';
339                                tmpTarget[j+5]=L';';
340                                j += 6;
341                                extraLength += 5;
342                                break;
343                        case L'\'':
344                                tmpTarget[j] = L'&';
345                                tmpTarget[j+1]=L'a';
346                                tmpTarget[j+2]=L'p';
347                                tmpTarget[j+3]=L'o';
348                                tmpTarget[j+4]=L's';
349                                tmpTarget[j+5]=L';';
350                                j += 6;
351                                extraLength += 5;
352                                break;
353                        case L'%':
354                                tmpTarget[j] = L'\\';
355                                tmpTarget[j+1] = L'%';
356                                j += 2;
357                                extraLength += 1;
358                                break;
359                        default:
360                                tmpTarget[j] = source[i];
361                                j++;
362                }
363        }
364        *target = new wchar_t[j+1];
365        memcpy(*target,tmpTarget,j*sizeof(wchar_t));
366        (*target)[j] = '\0';
367        return j;
368}
369
370
371void WriteLogentryWString(PMSG msg, int nFrom) {
372        wchar_t * messageStr = NULL;
373        wchar_t buffer[128];
374        wchar_t * newWindowText = NULL;
375        wchar_t * windowName = NULL;
376        unsigned int command = 0;
377        int sourceType = -1;
378        HWND source = NULL;
379        HWND parentHandle = NULL;
380        wchar_t * windowClass = NULL;
381        bool isPopup = false;
382        bool isModal = false;
383        bool htMenu = false;
384        HWND menuHandle = NULL;
385        int scrollPos = -1;
386        unsigned int scrollType = 0;
387        HWND scrollBarHandle = NULL;
388        int retVal = 0;
389
390        // debug vars
391       
392        retVal = GetWindowText(msg->hwnd, buffer, 128);
393        if( retVal > 0  && retVal<MAXINT ) {
394                /*
395                 * In one case at the start of MarWin, when a resource with DlgId 1049 is created,
396                 * GetWindowText returns MAXINT. This behaviour is undocumented.
397                 */
398                replaceWithXmlEntitiesWString(buffer, &windowName, retVal+1);
399        }
400        int windowResourceId = GetDlgCtrlID(msg->hwnd);
401        if( windowResourceId<0 ) {
402                windowResourceId = 0;
403        }
404
405        //**************************************
406        // Message specific variables
407        //**************************************
408
409        if( msg->message==WM_COMMAND ) {
410                command = LOWORD(msg->wParam);
411                sourceType = HIWORD(msg->wParam);
412                source = (HWND) msg->lParam;
413        }
414        if( msg->message==WM_SYSCOMMAND ) {
415                command = LOWORD(msg->wParam);
416        }
417
418        if( msg->message==WM_CREATE ) {
419                parentHandle = GetParent(msg->hwnd);
420               
421                retVal = GetClassName(msg->hwnd, buffer, 128);
422                if( retVal > 0  && retVal<MAXINT ) {
423                        replaceWithXmlEntitiesWString(buffer, &windowClass, retVal+1);
424                }
425
426                // check is dialog is modal
427                // this check is not always accurate, but the best that I could come up with
428                isModal = IsWindowEnabled(parentHandle)==false;
429        }
430
431        if( msg->message==WM_SETTEXT ) {
432                wchar_t * newWindowTextBuffer = (wchar_t*)msg->lParam;
433                if( newWindowTextBuffer!=NULL ) {
434                        size_t len = wcslen(newWindowTextBuffer);
435                        replaceWithXmlEntitiesWString(newWindowTextBuffer, &newWindowText, len+1);
436                }
437        }
438
439        if( msg->message==WM_NCLBUTTONDOWN ) {
440                if( msg->wParam==HTMENU ) {
441                        htMenu = true;
442                }
443        }
444       
445        if( msg->message==WM_INITMENU ) {
446                menuHandle = (HWND) msg->wParam;
447        }
448
449        if( msg->message==WM_HSCROLL || msg->message==WM_VSCROLL ) {
450                scrollType = LOWORD(msg->wParam);
451                scrollPos = HIWORD(msg->wParam);
452                scrollBarHandle = (HWND) msg->lParam;
453        }
454
455        /***************************************/
456        // put debugging variables here
457        /***************************************/
458
459
460        /***************************************
461         * Printing part
462         ***************************************/
463       
464        size_t bufsize = 2048;
465        wchar_t * msgBuffer = new wchar_t[bufsize];
466        size_t pos = 0;
467        //pos += swprintf_s(msgBuffer+pos, bufsize-pos,LOGPREFIXWSTRING);
468
469       
470        // print msg information
471        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<msg type=\"%i\">",msg->message);
472        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"WPARAM\" value=\"%i\"/>", msg->wParam);
473        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"LPARAM\" value=\"%i\"/>", msg->lParam);
474
475        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.hwnd\" value=\"%i\"/>", msg->hwnd);
476        if( msg->message==WM_COMMAND ) {
477                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"command\" value=\"%i\"/>",command);
478                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"sourceType\" value=\"%i\"/>",sourceType);
479                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"source\" value=\"%i\"/>",source);
480        }
481        if( msg->message==WM_SYSCOMMAND ) {
482                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"command\" value=\"%i\"/>", command);
483        }
484
485        if( msg->message==WM_LBUTTONUP || msg->message==WM_RBUTTONUP || msg->message==WM_MBUTTONUP ||
486                msg->message==WM_LBUTTONDOWN || msg->message==WM_RBUTTONDOWN || msg->message==WM_MBUTTONDOWN ||
487                msg->message==WM_LBUTTONDBLCLK || msg->message==WM_RBUTTONDBLCLK || msg->message==WM_MBUTTONDBLCLK) {
488                if( msg->time>-1 ) {
489                        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"point.x\" value=\"%i\"/>", msg->pt.x);
490                        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"point.x\" value=\"%i\"/>", msg->pt.y);
491                }
492        }
493        if( msg->message==WM_MOUSEACTIVATE ) {
494                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"toplevelwindow.hwnd\" value=\"%i\"/>", (HWND) msg->wParam);
495        }
496        if( msg->message==WM_KEYUP || msg->message==WM_KEYDOWN || msg->message==WM_SYSKEYUP || msg->message==WM_SYSKEYDOWN ) {
497                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"key\" value=\"%i\"/>", LOWORD(msg->wParam));
498        }
499        if( msg->message==WM_SETTEXT ) {
500                if( newWindowText!=NULL ) {
501                        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.newText\" value=\"%s\"/>", newWindowText);
502                }
503        }
504       
505        if( msg->message==WM_NCLBUTTONDOWN && htMenu ) {
506                pos += swprintf_s(msgBuffer+pos, bufsize-pos,L"<param name=\"isMenu\" value=\"true\"/>");
507        }
508
509        if( msg->message==WM_INITMENU ) {
510                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"menu.hwnd\" value=\"%i\"/>", menuHandle);
511        }
512
513        if( msg->message==WM_CREATE ) {
514                // print window information
515                if( windowName!=NULL ) {
516                        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.name\" value=\"%s\"/>", windowName);
517                }
518                if( windowResourceId>0 ) {
519                        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.resourceId\" value=\"%i\"/>", windowResourceId);
520                }
521                if( msg->message==WM_CREATE ) {
522                        if( parentHandle!=NULL ) {
523                                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.parent.hwnd\" value=\"%i\"/>", parentHandle);
524                        }
525                        if( windowClass!=NULL ) {
526                                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.class\" value=\"%s\"/>", windowClass);
527                        }
528                        if( isModal ) {
529                                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"window.ismodal\" value=\"true\"/>");
530                        }
531
532                }
533        }
534        if( msg->message==WM_HSCROLL || msg->message==WM_VSCROLL ) {
535                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"scrollType\" value=\"%i\"/>", scrollType);
536                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"scrollPos\" value=\"%i\"/>", scrollPos);
537                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"scrollBarHandle\" value=\"%i\"/>", scrollBarHandle);
538        }
539
540        if( msg->time!=-1 ) {
541                pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"time\" value=\"%i\"/>", msg->time);
542        }
543       
544        /***************************************/
545        // put debugging and experimental output stuff here
546        /***************************************/
547
548#ifdef __INCLUDEHOOKINFO__
549        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"<param name=\"hook\" value=\"%i\"/>", nFrom);
550#endif
551       
552
553        pos += swprintf_s(msgBuffer+pos,bufsize-pos,L"</msg>", msg->hwnd);
554#ifdef __USING_MTRACE__
555#ifdef __ENCODE_BASE64__
556        size_t arraySize = (pos+1)*2;
557        size_t encodingSize = arraySize*2;
558        char * base64Buffer = new char[encodingSize];
559
560        base64::encoder enc;
561        retVal = enc.encode((char*)msgBuffer, arraySize, base64Buffer);
562        base64Buffer[retVal] = '\0';
563
564        char * mtraceBuffer = new char[retVal+30];
565        sprintf_s(mtraceBuffer,retVal+29,"%s%s", LOGPREFIX, base64Buffer);
566        delete base64Buffer;
567#else
568        char * mtraceBuffer = new char[pos+1];
569        size_t numConverted;
570        wcstombs_s(&numConverted,mtraceBuffer, pos+1, msgBuffer, pos);
571#endif // __ENCODE_BASE64__
572        MTrace_AddToLevel(traceLevel,mtraceBuffer);
573        delete mtraceBuffer;
574#endif // __USING_MTRACE__
575#ifdef __USING_COSTUMLOG__
576        SYSTEMTIME currentTime;
577        GetSystemTime(&currentTime);
578        logfile << currentTime.wDay << "." << currentTime.wMonth << "." << currentTime.wYear << " ";
579        logfile << currentTime.wHour << ":" << currentTime.wMinute << ":" << currentTime.wSecond << ":";
580        logfile << currentTime.wMilliseconds << "\t";
581        logfile << buffer << std::endl;
582#endif
583        delete messageStr;
584        delete newWindowText;
585        delete windowName;
586        delete windowClass;
587        delete msgBuffer;
588}
Note: See TracBrowser for help on using the repository browser.