1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #include "boinc_win.h"
19 
20 #ifdef _MSC_VER
21 #define snprintf _snprintf
22 #endif
23 
24 #include "diagnostics.h"
25 #include "error_numbers.h"
26 #include "filesys.h"
27 #include "network.h"
28 #include "prefs.h"
29 #include "str_replace.h"
30 #include "url.h"
31 #include "util.h"
32 #include "win_util.h"
33 
34 #include "client_state.h"
35 #include "log_flags.h"
36 #include "client_msgs.h"
37 #include "http_curl.h"
38 #include "sandbox.h"
39 #include "main.h"
40 #include "cs_proxy.h"
41 #include "net_stats.h"
42 
43 #include "sysmon_win.h"
44 
45 
46 static HANDLE g_hWindowsMonitorSystemPowerThread = NULL;
47 static HWND g_hWndWindowsMonitorSystemPower = NULL;
48 static HANDLE g_hWindowsMonitorSystemProxyThread = NULL;
49 
50 
51 // return true if running under remote desktop
52 // (in which case CUDA and Stream apps don't work)
53 //
is_remote_desktop()54 bool is_remote_desktop() {
55     LPTSTR pBuf = NULL;
56     DWORD dwLength;
57     USHORT usProtocol=0, usConnectionState=0;
58 
59     if (WTSQuerySessionInformation(
60         WTS_CURRENT_SERVER_HANDLE,
61         WTS_CURRENT_SESSION,
62         WTSClientProtocolType,
63         &pBuf,
64         &dwLength
65     )) {
66         usProtocol = *(USHORT*)pBuf;
67         WTSFreeMemory(pBuf);
68     }
69 
70     if (WTSQuerySessionInformation(
71         WTS_CURRENT_SERVER_HANDLE,
72         WTS_CURRENT_SESSION,
73         WTSConnectState,
74         &pBuf,
75         &dwLength
76     )) {
77         usConnectionState = *(USHORT*)pBuf;
78         WTSFreeMemory(pBuf);
79     }
80 
81     // RDP Session implies Remote Desktop
82     if (usProtocol == 2) return true;
83 
84     // Fast User Switching keeps the protocol set to the console but changes
85     // the connected state to disconnected.
86     if ((usProtocol == 0) && (usConnectionState == 4)) return true;
87 
88     return false;
89 }
90 
91 // The following 3 functions are called in a separate thread,
92 // so we can't do anything directly.
93 // Set flags telling the main thread what to do.
94 //
95 
96 // Quit operations
quit_client()97 static void quit_client() {
98     gstate.requested_exit = true;
99     while (1) {
100         boinc_sleep(1.0);
101         if (gstate.cleanup_completed) break;
102     }
103 }
104 
105 // Suspend client operations
suspend_client()106 static void suspend_client() {
107     gstate.os_requested_suspend = true;
108     gstate.os_requested_suspend_time = dtime();
109 }
110 
111 // Resume client operations
resume_client()112 static void resume_client() {
113     gstate.os_requested_suspend = false;
114 }
115 
116 // Process console messages sent by the system
console_control_handler(DWORD dwCtrlType)117 static BOOL WINAPI console_control_handler( DWORD dwCtrlType ){
118     BOOL bReturnStatus = FALSE;
119     BOINCTRACE("***** Console Event Detected *****\n");
120     switch( dwCtrlType ){
121     case CTRL_LOGOFF_EVENT:
122         BOINCTRACE("Event: CTRL-LOGOFF Event\n");
123         if (!gstate.executing_as_daemon) {
124            quit_client();
125         }
126         bReturnStatus =  TRUE;
127         break;
128     case CTRL_C_EVENT:
129     case CTRL_BREAK_EVENT:
130         BOINCTRACE("Event: CTRL-C or CTRL-BREAK Event\n");
131         quit_client();
132         bReturnStatus =  TRUE;
133         break;
134     case CTRL_CLOSE_EVENT:
135     case CTRL_SHUTDOWN_EVENT:
136         BOINCTRACE("Event: CTRL-CLOSE or CTRL-SHUTDOWN Event\n");
137         quit_client();
138         break;
139     }
140     return bReturnStatus;
141 }
142 
post_sysmon_msg(const char * msg)143 static void post_sysmon_msg(const char* msg) {
144     if (gstate.have_sysmon_msg) return;
145     safe_strcpy(gstate.sysmon_msg, msg);
146     gstate.have_sysmon_msg = true;
147 }
148 
149 // Trap events on Windows so we can clean ourselves up.
150 // NOTE: this runs in a separate thread.
151 // Be careful accessing global data structures.
152 //
WindowsMonitorSystemPowerWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)153 static LRESULT CALLBACK WindowsMonitorSystemPowerWndProc(
154     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
155 ) {
156     switch(uMsg) {
157         // If we are not installed as a service, begin the task shutdown process
158         // when we are notified that the system is going down. If we are installed
159         // as a service, wait until the service control manager tells us to shutdown.
160         //
161         // The console handler happens a little to late in the cycle and leads
162         // to BOINC attempting to start new tasks, which fail, when the OS has
163         // shutdown some of the ones that were executing.
164         //
165         case WM_QUERYENDSESSION:
166             if (!gstate.executing_as_daemon) {
167                 quit_client();
168             }
169             return TRUE;
170             break;
171 
172         // On Windows power events are broadcast via the WM_POWERBROADCAST
173         //   window message.  It has the following parameters:
174         //     PBT_APMQUERYSUSPEND
175         //     PBT_APMQUERYSUSPENDFAILED
176         //     PBT_APMSUSPEND
177         //     PBT_APMRESUMECRITICAL
178         //     PBT_APMRESUMESUSPEND
179         //     PBT_APMBATTERYLOW
180         //     PBT_APMPOWERSTATUSCHANGE
181         //     PBT_APMOEMEVENT
182         //     PBT_APMRESUMEAUTOMATIC
183         case WM_POWERBROADCAST:
184             switch(wParam) {
185                 // System is preparing to suspend.  This is valid on
186                 //   Windows versions older than Vista
187                 case PBT_APMQUERYSUSPEND:
188                     return TRUE;
189                     break;
190 
191                 // System is resuming from a failed request to suspend
192                 //   activity.  This is only valid on Windows versions
193                 //   older than Vista
194                 case PBT_APMQUERYSUSPENDFAILED:
195                     resume_client();
196                     break;
197 
198                 // System is critically low on battery power.  This is
199                 //   only valid on Windows versions older than Vista
200                 case PBT_APMBATTERYLOW:
201                     post_sysmon_msg("Critical battery alarm, Windows is suspending operations");
202                     suspend_client();
203                     break;
204 
205                 // System is suspending
206                 case PBT_APMSUSPEND:
207                     post_sysmon_msg("Windows is suspending operations");
208                     suspend_client();
209                     break;
210 
211                 // System is resuming from a normal power event
212                 case PBT_APMRESUMESUSPEND:
213                     gstate.set_now();
214                     post_sysmon_msg("Windows is resuming operations");
215 
216                     // Check for a proxy
217                     working_proxy_info.need_autodetect_proxy_settings = true;
218 
219                     resume_client();
220                     break;
221             }
222             break;
223         default:
224             break;
225     }
226     return (DefWindowProc(hWnd, uMsg, wParam, lParam));
227 }
228 
229 // Create a thread to monitor system events
WindowsMonitorSystemPowerThread(LPVOID)230 static DWORD WINAPI WindowsMonitorSystemPowerThread( LPVOID  ) {
231     WNDCLASS wc;
232     MSG msg;
233 
234     // Initialize diagnostics framework for this thread
235     //
236     diagnostics_thread_init();
237 
238     wc.style         = CS_GLOBALCLASS;
239     wc.lpfnWndProc   = (WNDPROC)WindowsMonitorSystemPowerWndProc;
240     wc.cbClsExtra    = 0;
241     wc.cbWndExtra    = 0;
242     wc.hInstance     = NULL;
243     wc.hIcon         = NULL;
244     wc.hCursor       = NULL;
245     wc.hbrBackground = NULL;
246     wc.lpszMenuName  = NULL;
247     wc.lpszClassName = "BOINCWindowsMonitorSystemPower";
248 
249     if (!RegisterClass(&wc)) {
250         log_message_error("Failed to register the WindowsMonitorSystem window class.");
251         return 1;
252     }
253 
254     g_hWndWindowsMonitorSystemPower = CreateWindow(
255         wc.lpszClassName,
256         "BOINC Monitor System (Power)",
257         WS_OVERLAPPEDWINDOW & ~WS_VISIBLE,
258         CW_USEDEFAULT,
259         CW_USEDEFAULT,
260         CW_USEDEFAULT,
261         CW_USEDEFAULT,
262         NULL,
263         NULL,
264         NULL,
265         NULL);
266 
267     if (!g_hWndWindowsMonitorSystemPower) {
268         log_message_error("Failed to create the WindowsMonitorSystem window.");
269         return 0;
270     }
271 
272     while (GetMessage(&msg, NULL, 0, 0)) {
273         TranslateMessage(&msg);
274         DispatchMessage(&msg);
275     }
276     return 0;
277 }
278 
279 // Detect any proxy configuration settings automatically.
280 //
windows_detect_autoproxy_settings()281 static void windows_detect_autoproxy_settings() {
282     if (log_flags.proxy_debug) {
283         post_sysmon_msg("[proxy] automatic proxy check in progress");
284     }
285 
286     HINTERNET                 hWinHttp = NULL;
287     WINHTTP_AUTOPROXY_OPTIONS autoproxy_options;
288     WINHTTP_PROXY_INFO        proxy_info;
289     PARSED_URL purl;
290     std::wstring              network_test_url;
291     size_t                    pos;
292 
293 
294     memset(&autoproxy_options, 0, sizeof(autoproxy_options));
295     memset(&proxy_info, 0, sizeof(proxy_info));
296 
297     autoproxy_options.dwFlags =
298         WINHTTP_AUTOPROXY_AUTO_DETECT;
299     autoproxy_options.dwAutoDetectFlags =
300         WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
301     autoproxy_options.fAutoLogonIfChallenged = TRUE;
302 
303     network_test_url = boinc_ascii_to_wide(cc_config.network_test_url).c_str();
304 
305     hWinHttp = WinHttpOpen(
306         L"BOINC client",
307         WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
308         WINHTTP_NO_PROXY_NAME,
309         WINHTTP_NO_PROXY_BYPASS,
310         NULL
311     );
312 
313     char msg[1024], buf[1024];
314     safe_strcpy(msg, "[proxy] ");
315 
316     if (WinHttpGetProxyForUrl(hWinHttp, network_test_url.c_str(), &autoproxy_options, &proxy_info)) {
317 
318         // Apparently there are some conditions where WinHttpGetProxyForUrl can return
319         //   success but where proxy_info.lpszProxy is null.  Maybe related to UPNP?
320         //
321         // For the time being check to see if proxy_info.lpszProxy is non-null.
322         //
323         if (proxy_info.lpszProxy) {
324             std::string proxy(boinc_wide_to_ascii(std::wstring(proxy_info.lpszProxy)));
325             std::string new_proxy;
326 
327             if (log_flags.proxy_debug) {
328                 safe_strcat(msg, "proxy list: ");
329                 safe_strcat(msg, proxy.c_str());
330             }
331 
332             if (!proxy.empty()) {
333                 // Trim string if more than one proxy is defined
334                 // proxy list is defined as:
335                 //   ([<scheme>=][<scheme>"://"]<server>[":"<port>])
336 
337                 // Find and erase first delimeter type.
338                 pos = proxy.find(';');
339                 if (pos != -1 ) {
340                     new_proxy = proxy.erase(pos);
341                     proxy = new_proxy;
342                 }
343 
344                 // Find and erase second delimeter type.
345                 pos = proxy.find(' ');
346                 if (pos != -1 ) {
347                     new_proxy = proxy.erase(pos);
348                     proxy = new_proxy;
349                 }
350 
351                 // Parse the remaining url
352                 parse_url(proxy.c_str(), purl);
353 
354                 // Store the results for future use.
355                 if (0 != strcmp(working_proxy_info.autodetect_server_name, purl.host)) {
356                     // Reset clients connection error detection path
357                     net_status.need_physical_connection = false;
358 
359                     working_proxy_info.autodetect_protocol = purl.protocol;
360                     safe_strcpy(working_proxy_info.autodetect_server_name, purl.host);
361                     working_proxy_info.autodetect_port = purl.port;
362                 }
363 
364                 if (log_flags.proxy_debug) {
365                     snprintf(buf, sizeof(buf), "proxy detected %s:%d", purl.host, purl.port);
366                     safe_strcat(msg, buf);
367                 }
368             }
369         }
370 
371         // Clean up
372         if (proxy_info.lpszProxy) GlobalFree(proxy_info.lpszProxy);
373         if (proxy_info.lpszProxyBypass) GlobalFree(proxy_info.lpszProxyBypass);
374     } else {
375         // We can get here if the user is switching from a network that
376         // requires a proxy to one that does not require a proxy.
377         working_proxy_info.autodetect_protocol = 0;
378         safe_strcpy(working_proxy_info.autodetect_server_name, "");
379         working_proxy_info.autodetect_port = 0;
380         if (log_flags.proxy_debug) {
381             safe_strcat(msg, "no automatic proxy detected");
382         }
383     }
384     if (hWinHttp) WinHttpCloseHandle(hWinHttp);
385     if (log_flags.proxy_debug) {
386         post_sysmon_msg(msg);
387     }
388 }
389 
WindowsMonitorSystemProxyThread(LPVOID)390 static DWORD WINAPI WindowsMonitorSystemProxyThread( LPVOID  ) {
391 
392     // Initialize diagnostics framework for this thread
393     //
394     diagnostics_thread_init();
395 
396     // notify the main client thread that detecting proxies is
397     // supported.
398     working_proxy_info.autodetect_proxy_supported = true;
399 
400     while (1) {
401 
402         if (working_proxy_info.need_autodetect_proxy_settings) {
403             working_proxy_info.have_autodetect_proxy_settings = false;
404             windows_detect_autoproxy_settings();
405             working_proxy_info.need_autodetect_proxy_settings = false;
406             working_proxy_info.have_autodetect_proxy_settings = true;
407         }
408 
409         Sleep(1000);
410     }
411 
412     return 0;
413 }
414 
415 // Setup the client software to monitor various system events
initialize_system_monitor(int,char **)416 int initialize_system_monitor(int /*argc*/, char** /*argv*/) {
417 
418     // Windows: install console controls, the service control manager will send us
419     // the needed events when we are running as a service.
420     //
421     if (!gstate.executing_as_daemon) {
422         if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_control_handler, TRUE)){
423             log_message_error("Failed to register the console control handler.");
424             return ERR_IO;
425         }
426     }
427 
428     // Create a thread to receive system power events.
429     //
430     g_hWindowsMonitorSystemPowerThread = CreateThread(
431         NULL,
432         0,
433         WindowsMonitorSystemPowerThread,
434         NULL,
435         0,
436         NULL
437     );
438 
439     if (!g_hWindowsMonitorSystemPowerThread) {
440         g_hWindowsMonitorSystemPowerThread = NULL;
441         g_hWndWindowsMonitorSystemPower = NULL;
442     }
443 
444     // Create a thread to handle proxy auto-detection.
445     //
446     if (!cc_config.proxy_info.no_autodetect) {
447         g_hWindowsMonitorSystemProxyThread = CreateThread(
448             NULL,
449             0,
450             WindowsMonitorSystemProxyThread,
451             NULL,
452             0,
453             NULL
454         );
455 
456         if (!g_hWindowsMonitorSystemProxyThread) {
457             g_hWindowsMonitorSystemProxyThread = NULL;
458         }
459     }
460 
461     return 0;
462 }
463 
464 // Cleanup the system event monitor
cleanup_system_monitor()465 int cleanup_system_monitor() {
466 
467     if (g_hWindowsMonitorSystemPowerThread) {
468         TerminateThread(g_hWindowsMonitorSystemPowerThread, 0);
469         CloseHandle(g_hWindowsMonitorSystemPowerThread);
470         g_hWindowsMonitorSystemPowerThread = NULL;
471         g_hWndWindowsMonitorSystemPower = NULL;
472     }
473 
474     if (g_hWindowsMonitorSystemProxyThread) {
475         TerminateThread(g_hWindowsMonitorSystemProxyThread, 0);
476         CloseHandle(g_hWindowsMonitorSystemProxyThread);
477         g_hWindowsMonitorSystemProxyThread = NULL;
478     }
479 
480     return 0;
481 }
482 
483 // internal variables for managing the service
484 SERVICE_STATUS          ssStatus;       // current status of the service
485 SERVICE_STATUS_HANDLE   sshStatusHandle;
486 DWORD                   dwErr = 0;
487 TCHAR                   szErr[1024];
488 
489 SERVICE_TABLE_ENTRY     service_dispatch_table[] = {
490     { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)BOINCServiceMain },
491     { NULL, NULL }
492 };
493 
494 // Inform the service control manager that the service is about to
495 // start.
initialize_service_dispatcher(int,char **)496 int initialize_service_dispatcher(int /*argc*/, char** /*argv*/) {
497     fprintf(stdout, "\nStartServiceCtrlDispatcher being called.\n");
498     fprintf(stdout, "This may take several seconds.  Please wait.\n");
499 
500     if (!StartServiceCtrlDispatcher(service_dispatch_table)) {
501         log_message_error("StartServiceCtrlDispatcher failed.");
502         return ERR_IO;
503     }
504     return 0;
505 }
506 
507 //
508 //  FUNCTION: BOINCServiceMain
509 //
510 //  PURPOSE: To perform actual initialization of the service
511 //
512 //  PARAMETERS:
513 //    dwArgc   - number of command line arguments
514 //    lpszArgv - array of command line arguments
515 //
516 //  RETURN VALUE:
517 //    none
518 //
519 //  COMMENTS:
520 //    This routine performs the service initialization and then calls
521 //    the user defined main() routine to perform majority
522 //    of the work.
523 //
BOINCServiceMain(DWORD,LPTSTR *)524 void WINAPI BOINCServiceMain(DWORD /*dwArgc*/, LPTSTR * /*lpszArgv*/) {
525     // SERVICE_STATUS members that don't change in example
526     //
527     ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
528     ssStatus.dwControlsAccepted = SERVICE_ACCEPTED_ACTIONS;
529     ssStatus.dwServiceSpecificExitCode = 0;
530 
531 
532     // register our service control handler:
533     //
534     sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), BOINCServiceCtrl);
535     if (!sshStatusHandle) {
536         goto cleanup;
537     }
538 
539     if (!ReportStatus(
540         SERVICE_RUNNING,       // service state
541         ERROR_SUCCESS,         // exit code
542         0)                    // wait hint
543     ){
544         goto cleanup;
545     }
546 
547     dwErr = boinc_main_loop();
548 
549 cleanup:
550 
551     // try to report the stopped status to the service control manager.
552     //
553     if (sshStatusHandle) {
554         (VOID)ReportStatus(
555             SERVICE_STOPPED,
556             dwErr,
557             0
558         );
559     }
560 }
561 
562 
563 //
564 //  FUNCTION: BOINCServiceCtrl
565 //
566 //  PURPOSE: This function is called by the SCM whenever
567 //           ControlService() is called on this service.
568 //
569 //  PARAMETERS:
570 //    dwCtrlCode - type of control requested
571 //
572 //  RETURN VALUE:
573 //    none
574 //
575 //  COMMENTS:
576 //
BOINCServiceCtrl(DWORD dwCtrlCode)577 VOID WINAPI BOINCServiceCtrl(DWORD dwCtrlCode) {
578     // Handle the requested control code.
579     //
580     switch(dwCtrlCode) {
581         // Stop the service.
582         //
583         // SERVICE_STOP_PENDING should be reported before
584         // setting the Stop Event - hServerStopEvent - in
585         // ServiceStop().  This avoids a race condition
586         // which may result in a 1053 - The Service did not respond...
587         // error.
588         case SERVICE_CONTROL_STOP:
589         case SERVICE_CONTROL_SHUTDOWN:
590             ReportStatus(SERVICE_STOP_PENDING, ERROR_SUCCESS, 30000);
591             quit_client();
592             return;
593 
594         // Pause the service.
595         //
596         case SERVICE_CONTROL_PAUSE:
597             ReportStatus(SERVICE_PAUSE_PENDING, ERROR_SUCCESS, 10000);
598             suspend_client();
599             ReportStatus(SERVICE_PAUSED, ERROR_SUCCESS, 10000);
600             return;
601 
602         // Continue the service.
603         //
604         case SERVICE_CONTROL_CONTINUE:
605             ReportStatus(SERVICE_CONTINUE_PENDING, ERROR_SUCCESS, 10000);
606             resume_client();
607             ReportStatus(SERVICE_RUNNING, ERROR_SUCCESS, 10000);
608             return;
609 
610         // Update the service status.
611         //
612         case SERVICE_CONTROL_INTERROGATE:
613             break;
614 
615         // invalid control code
616         //
617         default:
618             break;
619 
620     }
621 
622     ReportStatus(ssStatus.dwCurrentState, ERROR_SUCCESS, 1000);
623 }
624 
625 
626 //
627 //  FUNCTION: ReportStatus()
628 //
629 //  PURPOSE: Sets the current status of the service and
630 //           reports it to the Service Control Manager
631 //
632 //  PARAMETERS:
633 //    dwCurrentState - the state of the service
634 //    dwWin32ExitCode - error code to report
635 //    dwWaitHint - worst case estimate to next checkpoint
636 //
637 //  RETURN VALUE:
638 //    TRUE  - success
639 //    FALSE - failure
640 //
641 //  COMMENTS:
642 //
ReportStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint)643 BOOL ReportStatus(
644     DWORD dwCurrentState,
645     DWORD dwWin32ExitCode,
646     DWORD dwWaitHint
647 ) {
648     static DWORD dwCheckPoint = 1;
649     BOOL fResult = TRUE;
650 
651     if (dwCurrentState == SERVICE_START_PENDING) {
652         ssStatus.dwControlsAccepted = 0;
653     } else {
654         ssStatus.dwControlsAccepted = SERVICE_ACCEPTED_ACTIONS;
655     }
656 
657     ssStatus.dwCurrentState = dwCurrentState;
658     ssStatus.dwWin32ExitCode = dwWin32ExitCode;
659     ssStatus.dwWaitHint = dwWaitHint;
660 
661     if ( ( dwCurrentState == SERVICE_RUNNING ) ||
662             ( dwCurrentState == SERVICE_STOPPED )
663     ) {
664         ssStatus.dwCheckPoint = 0;
665     } else {
666         ssStatus.dwCheckPoint = dwCheckPoint++;
667     }
668 
669 
670     // Report the status of the service to the service control manager.
671     //
672     fResult = SetServiceStatus( sshStatusHandle, &ssStatus);
673     if (!fResult) {
674         LogEventErrorMessage(TEXT("SetServiceStatus"));
675     }
676     return fResult;
677 }
678 
679 
680 
681 //
682 //  FUNCTION: LogEventErrorMessage(LPTSTR lpszMsg)
683 //
684 //  PURPOSE: Allows any thread to log an error message
685 //
686 //  PARAMETERS:
687 //    lpszMsg - text for message
688 //
689 //  RETURN VALUE:
690 //    none
691 //
692 //  COMMENTS:
693 //
LogEventErrorMessage(LPTSTR lpszMsg)694 VOID LogEventErrorMessage(LPTSTR lpszMsg) {
695     TCHAR   szMsg[1024];
696     HANDLE  hEventSource;
697     LPTSTR  lpszStrings[2];
698 
699     dwErr = GetLastError();
700 
701     // Use event logging to log the error.
702     //
703     hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
704 
705     _stprintf_s(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
706     lpszStrings[0] = szMsg;
707     lpszStrings[1] = lpszMsg;
708 
709     if (hEventSource != NULL) {
710         ReportEvent(hEventSource, // handle of event source
711             EVENTLOG_ERROR_TYPE,  // event type
712             0,                    // event category
713             1,                    // event ID
714             NULL,                 // current user's SID
715             2,                    // strings in lpszStrings
716             0,                    // no bytes of raw data
717             (LPCSTR*)lpszStrings, // array of error strings
718             NULL                  // no raw data
719         );
720 
721         (VOID) DeregisterEventSource(hEventSource);
722     }
723 }
724 
725 
726 //
727 //  FUNCTION: LogEventWarningMessage(LPTSTR lpszMsg)
728 //
729 //  PURPOSE: Allows any thread to log an warning message
730 //
731 //  PARAMETERS:
732 //    lpszMsg - text for message
733 //
734 //  RETURN VALUE:
735 //    none
736 //
737 //  COMMENTS:
738 //
LogEventWarningMessage(LPTSTR lpszMsg)739 VOID LogEventWarningMessage(LPTSTR lpszMsg) {
740     HANDLE  hEventSource;
741     LPTSTR  lpszStrings[2];
742 
743     // Use event logging to log the error.
744     //
745     hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
746 
747     lpszStrings[0] = lpszMsg;
748     lpszStrings[1] = '\0';
749 
750     if (hEventSource != NULL) {
751         ReportEvent(hEventSource, // handle of event source
752             EVENTLOG_WARNING_TYPE,// event type
753             0,                    // event category
754             1,                    // event ID
755             NULL,                 // current user's SID
756             2,                    // strings in lpszStrings
757             0,                    // no bytes of raw data
758             (LPCSTR*)lpszStrings, // array of error strings
759             NULL                  // no raw data
760         );
761 
762         (VOID) DeregisterEventSource(hEventSource);
763     }
764 }
765 
766 
767 //
768 //  FUNCTION: LogEventInfoMessage(LPTSTR lpszMsg)
769 //
770 //  PURPOSE: Allows any thread to log an info message
771 //
772 //  PARAMETERS:
773 //    lpszMsg - text for message
774 //
775 //  RETURN VALUE:
776 //    none
777 //
778 //  COMMENTS:
779 //
LogEventInfoMessage(LPTSTR lpszMsg)780 VOID LogEventInfoMessage(LPTSTR lpszMsg) {
781     HANDLE  hEventSource;
782     LPTSTR  lpszStrings[2];
783 
784     // Use event logging to log the error.
785     //
786     hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
787 
788     lpszStrings[0] = lpszMsg;
789     lpszStrings[1] = '\0';
790 
791     if (hEventSource != NULL) {
792         ReportEvent(hEventSource, // handle of event source
793             EVENTLOG_INFORMATION_TYPE,// event type
794             0,                    // event category
795             1,                    // event ID
796             NULL,                 // current user's SID
797             2,                    // strings in lpszStrings
798             0,                    // no bytes of raw data
799             (LPCSTR*)lpszStrings, // array of error strings
800             NULL                  // no raw data
801         );
802 
803         (VOID) DeregisterEventSource(hEventSource);
804     }
805 }
806