Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1208 lines
24 KiB

/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
dnsrslvr.c
Abstract:
DNS Resolver Service
Main service module.
Author:
Glenn Curtis (glennc) 25-Feb-1997
Revision History:
Jim Gilroy (jamesg) March 2000 cleanup
Jim Gilroy (jamesg) Nov 2000 rewrite
--*/
#include "local.h"
#ifdef BUILD_W2K
#include <services.h>
#else
#include <svcs.h>
#endif
//
// Service control
//
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE ServiceStatusHandle = (SERVICE_STATUS_HANDLE) 0;
#ifdef BUILD_W2K
PSVCS_GLOBAL_DATA g_pSvchostData;
#else
PSVCHOST_GLOBAL_DATA g_pSvchostData;
#endif
HANDLE g_hStopEvent;
BOOL g_StopFlag;
BOOL g_fServiceControlHandled;
//
// Service state
//
#define RES_STATUS_BEGIN 0x0cc00000
#define RES_STATUS_ZERO_INIT 0x0cc00001
#define RES_STATUS_CREATED_CS 0x0cc00002
#define RES_STATUS_CREATED_EVENT 0x0cc00003
#define RES_STATUS_READ_REGISTRY 0x0cc00004
#define RES_STATUS_ALLOC_CACHE 0x0cc00005
#define RES_STATUS_START_NOTIFY 0x0cc00006
#define RES_STATUS_START_IP_LIST 0x0cc00007
#define RES_STATUS_START_RPC 0x0cc00008
#define RES_STATUS_REG_CONTROL 0x0cc00009
#define RES_STATUS_RUNNING 0x0cc00100
#define RES_STATUS_STOPPING 0x0cc00300
#define RES_STATUS_SIGNALED_STOP 0x0cc00301
#define RES_STATUS_STOP_RPC 0x0cc00302
#define RES_STATUS_STOP_NOTIFY 0x0cc00303
#define RES_STATUS_STOP_IP_LIST 0x0cc00304
#define RES_STATUS_FREE_CACHE 0x0cc00305
#define RES_STATUS_FREE_NET_INFO 0x0cc00306
#define RES_STATUS_FREE_IP_LIST 0x0cc00307
#define RES_STATUS_FREE_SERVICE_NOTIFY 0x0cc00308
#define RES_STATUS_DEL_EVENT 0x0cc00309
#define RES_STATUS_DEL_CS 0x0cc00310
#define RES_STATUS_END 0x0cc00400
DWORD g_ResolverStatus = RES_STATUS_BEGIN;
//
// Initialization cleanup\state
//
// Track what we intialized for safer\faster cleanup
//
#define INITFLAG_CS_CREATED 0x00000001
#define INITFLAG_WINSOCK 0x00000002
#define INITFLAG_EVENTS_CREATED 0x00000004
#define INITFLAG_CACHE_CREATED 0x00000008
#define INITFLAG_NOTIFY_STARTED 0x00000010
#define INITFLAG_IP_LIST_CREATED 0x00000020
#define INITFLAG_RPC_SERVER_STARTED 0x00000100
DWORD g_InitState;
//
// Critical sections used
//
CRITICAL_SECTION CacheCritSec;
CRITICAL_SECTION NetworkListCritSec;
CRITICAL_SECTION NetworkFailureCritSec;
//
// Logging control
//
BOOL g_LogTraceInfo = TRUE;
//
// Private protos
//
DWORD
ResolverInitialize(
VOID
);
VOID
ResolverShutdown(
IN DWORD ErrorCode
);
VOID
ResolverControlHandler(
IN DWORD Opcode
);
DWORD
ResolverUpdateStatus(
VOID
);
//
// Service routines
//
//
// NT5 version -- inside services.exe
//
#ifdef BUILD_W2K
VOID
SVCS_ENTRY_POINT(
DWORD NumArgs,
LPTSTR * ArgsArray,
PSVCS_GLOBAL_DATA pSvcsGlobalData,
HANDLE SvcRefHandle
)
/*++
Routine Description:
Main main entry point of resolver service
Arguments:
NumArgs - number of arguments to service start call
ArgsArray - array of ptrs to arguments in service start call
Return Value:
None
--*/
{
//
// save pointer to service data block
//
g_pSvchostData = pSvcsGlobalData;
ResolverInitialize();
}
#else
//
// Whistler+ version -- in svchost.exe process
//
VOID
SvchostPushServiceGlobals(
PSVCHOST_GLOBAL_DATA pGlobals
)
{
g_pSvchostData = pGlobals;
}
VOID
ServiceMain(
IN DWORD NumArgs,
IN LPTSTR * ArgsArray
)
/*++
Routine Description:
Main entry point of resolver service.
Arguments:
NumArgs - number of strings specified in ArgsArray.
ArgsArray - array of ptrs to arguments in service start call
Return Value:
None
--*/
{
//
// Make sure svchost.exe gave us global data
//
ASSERT( g_pSvchostData != NULL );
//
// Startup service, then exit
//
ResolverInitialize();
}
#endif // NT4 \ Whistler+ switch
VOID
ResolverInitFailure(
IN DNS_STATUS Status,
IN DWORD EventId,
IN DWORD MemEventId,
IN PSTR pszDebugString
)
/*++
Routine Description:
Handle resolver init failure.
Function exists to avoid duplicate code.
Arguments:
Return Value:
None
--*/
{
WCHAR numberString[16];
PWSTR eventStrings[1];
DNSLOG_TIME();
DNSLOG_F1( "Resolver Init Failure" );
DNSLOG_F2( " Failure = %s", pszDebugString );
DNSLOG_F2( " Status = %d", Status );
DNSLOG_F1( "" );
DNSDBG( ANY, (
"Resolver Init FAILED!\n"
"\tname = %s\n"
"\tstatus = %d\n"
"\tevent id = %d\n"
"\tmem event = %08x\n",
pszDebugString,
Status,
EventId,
MemEventId ));
DnsDbg_PrintfToDebugger(
"ResolverInitialize - Returning status %d 0x%08x\n"
"\tname = %s\n",
Status, Status,
pszDebugString );
//
// log in memory event
//
LogEventInMemory( MemEventId, Status );
//
// log event
// - convert status to string
//
wsprintfW( numberString, L"0x%.8X", Status );
eventStrings[0] = numberString;
ResolverLogEvent(
EventId,
EVENTLOG_ERROR_TYPE,
1,
eventStrings,
Status );
// clean up
ResolverShutdown( Status );
}
DWORD
ResolverInitialize(
VOID
)
/*++
Routine Description:
This function initializes the DNS Caching Resolver service.
Arguments:
InitState - Returns a flag to indicate how far we got with
initializing the service before an error occurred.
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on failure.
--*/
{
DNS_STATUS status = NO_ERROR;
//
// init service state
//
g_ResolverStatus = RES_STATUS_BEGIN;
g_InitState = 0;
g_StopFlag = FALSE;
g_hStopEvent = NULL;
g_fServiceControlHandled = FALSE;
//
// initialize logging
//
DNSLOG_INIT();
DNSLOG_F1( "DNS Caching Resolver Service - ResolverInitialize" );
#if DBG
#if 0
Dns_StartDebug(
0,
"dnsres.flag",
NULL,
"dnsres.log",
0 );
#endif
Dns_StartDebugEx(
0, // no flag value
"dnsres.flag",
NULL, // no external flag
"dnsres.log",
0, // no wrap limit
FALSE, // don't use existing global
FALSE,
TRUE // make this file global
);
#endif
DNSDBG( INIT, ( "DNS resolver startup.\n" ));
IF_DNSDBG( START_BREAK )
{
// since resolver moved to NetworkServices permissions do
// not properly bring up ntsd; instead just give time
// to attach debugger
Sleep( 20000 );
}
//
// initialize service status block
//
ServiceStatusHandle = (SERVICE_STATUS_HANDLE) 0;
ServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 5000;
ServiceStatus.dwWin32ExitCode = NO_ERROR;
ServiceStatus.dwServiceSpecificExitCode = 0;
ResolverUpdateStatus();
//
// init globals to zero
//
ZeroInitIpListGlobals();
ZeroNetworkConfigGlobals();
g_ResolverStatus = RES_STATUS_ZERO_INIT;
//
// initialize all our critical sections as soon as we can
//
LogEventInMemory( RES_EVENT_INITCRIT_START, 0 );
InitializeCriticalSection( & CacheCritSec );
InitializeCriticalSection( & NetworkListCritSec );
InitializeCriticalSection( & NetworkFailureCritSec );
LogEventInMemory( RES_EVENT_INITCRIT_END,0 );
//
// init our dnslib heap to use dnsapi heap
//
// this is important because we currently mix and match records
// created inside dnsapi (hosts file and query) with a few that
// we roll on our own; need this to be common
//
Dns_LibHeapReset( DnsApiAlloc, DnsApiRealloc, DnsApiFree );
//
// init winsock
//
Dns_InitializeWinsock();
g_InitState |= INITFLAG_WINSOCK;
//
// shutdown event
//
g_hStopEvent = CreateEvent(
NULL, // no security descriptor
TRUE, // do not use automatic reset
FALSE, // initial state: not signalled
NULL // no name
);
if ( !g_hStopEvent )
{
status = GetLastError();
ResolverInitFailure(
status,
0,
0,
"CreateEvent() failed" );
return status;
}
g_InitState |= INITFLAG_EVENTS_CREATED;
g_ResolverStatus = RES_STATUS_CREATED_EVENT;
ResolverUpdateStatus();
//
// initialize our global registry values
// - force this just once on startup so we have the
// relevant cache params; after that read only on
// demand when building netinfo blobs
ReadRegistryConfig();
//
// Set the query timeouts to be used from defaults or registry
//
Dns_InitQueryTimeouts();
//
// init socket caching
// - improves perf and prevents socket DOS attack
// - default cache to 10 sockets
//
// DCR: create global for socket caching
//
Dns_CacheSocketInit( 10 );
#if 0
//
// start cache on demand now
//
//
// initialize cache
//
status = Cache_Initialize();
LogEventInMemory( RES_EVENT_INITCACHE, status );
if ( status != NO_ERROR )
{
ResolverInitFailure(
status,
EVENT_DNS_CACHE_START_FAILURE_NO_CONTROL,
0,
"Cache_Initialize() failed" );
return status;
}
g_InitState |= INITFLAG_CACHE_CREATED;
g_ResolverStatus = RES_STATUS_ALLOC_CACHE;
ResolverUpdateStatus();
#endif
//
// notification thread (host file and registry)
//
StartNotify();
g_InitState |= INITFLAG_NOTIFY_STARTED;
g_ResolverStatus = RES_STATUS_START_NOTIFY;
ResolverUpdateStatus();
//
// IP notification thread
//
status = InitIpListAndNotification();
if ( status != ERROR_SUCCESS )
{
ResolverInitFailure(
status,
0,
0,
"IP list init failed" );
return status;
}
g_InitState |= INITFLAG_IP_LIST_CREATED;
g_ResolverStatus = RES_STATUS_START_IP_LIST;
ResolverUpdateStatus();
//
// register control handler
// allows us to receive service requests
//
ServiceStatusHandle = RegisterServiceCtrlHandlerW(
DNS_RESOLVER_SERVICE,
ResolverControlHandler
);
if ( !ServiceStatusHandle )
{
status = GetLastError();
ResolverInitFailure(
status,
EVENT_DNS_CACHE_START_FAILURE_LOW_MEMORY,
RES_EVENT_REGISTER_SCH,
"Call to RegisterServiceCtrlHandlerW failed!"
);
return status;
}
g_ResolverStatus = RES_STATUS_REG_CONTROL;
ResolverUpdateStatus();
//
// initialize RPC interfaces
// - bump our requested stack size up to 8K
// (RPC uses 1800 bytes before we get the stack,
// the new() operator followed by the heap code uses
// another 1200 -- leaving only about a 1000 for
// DNS)
//
LogEventInMemory( RES_EVENT_STARTRPC, 0 );
#if 0
// should not be necessary
// default for all svchost instances has been increased
if ( status != NO_ERROR )
{
DNSDBG( ANY, (
"RpcMgmtSetServerStackSize( 2000 ) = %d\n",
status ));
}
#endif
status = Rpc_Initialize();
#if 0
status = g_pSvchostData->StartRpcServer(
SERVER_INTERFACE_NAME_W,
DnsResolver_ServerIfHandle );
#endif
if ( status != NO_ERROR )
{
LogEventInMemory( RES_EVENT_STATUS, status );
if ( status == RPC_S_TYPE_ALREADY_REGISTERED ||
status == RPC_NT_TYPE_ALREADY_REGISTERED )
{
DNSLOG_TIME();
DNSLOG_F1( " Call to StartRpcServer returned warning that" );
DNSLOG_F1( " the service is already running!" );
DNSLOG_F2( " RpcPipeName : %S", RESOLVER_INTERFACE_NAME_W );
DNSLOG_F1( " Going to just continue running . . ." );
DNSLOG_F1( "" );
DnsDbg_PrintfToDebugger(
"DNS Client (dnsrslvr.dll) - Call to StartRpcServer\n"
"returned warning that the service is already running!\n"
"RpcPipeName : %S"
"Going to just continue running . . .\n",
RESOLVER_INTERFACE_NAME_W );
status = NO_ERROR;
}
else
{
DNSDBG( ANY, (
"RPC init FAILED! status = %d\n"
"\tpipe name = %s\n",
status,
RESOLVER_INTERFACE_NAME_W ));
ResolverInitFailure(
status,
EVENT_DNS_CACHE_START_FAILURE_NO_RPC,
0,
"Call to StartRpcServer failed!"
);
return status;
}
}
g_ResolverStatus = RES_STATUS_START_RPC;
g_InitState |= INITFLAG_RPC_SERVER_STARTED;
//
// successful startup
// - indicate running
// - indicate what service control messages we want to get
//
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PARAMCHANGE |
SERVICE_ACCEPT_NETBINDCHANGE;
ServiceStatus.dwWaitHint = 0;
ServiceStatus.dwWin32ExitCode = NO_ERROR;
ResolverUpdateStatus();
g_ResolverStatus = RES_STATUS_RUNNING;
DNSLOG_F1( "ResolverInitialize - Successful" );
DNSLOG_F1( "" );
return NO_ERROR;
}
VOID
ResolverShutdown(
IN DWORD ErrorCode
)
/*++
Routine Description:
This function shuts down the DNS cache service.
Arguments:
ErrorCode - Supplies the error code of the failure
Return Value:
None.
--*/
{
DWORD status = NO_ERROR;
LONG existingStopFlag;
DNSLOG_TIME();
DNSLOG_F1( "DNS Caching Resolver Service - ResolverShutdown" );
DnsDbg_PrintfToDebugger( "DNS Client - ResolverShutdown!\n" );
//
// indicate shutdown
// - but interlock to avoid dual shutdown
//
existingStopFlag = InterlockedExchange(
&g_StopFlag,
(LONG) TRUE );
if ( existingStopFlag )
{
DNS_ASSERT( FALSE );
return;
}
DNS_ASSERT( g_StopFlag );
//
// indicate stop in progress
//
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
ServiceStatus.dwCheckPoint = 1;
ServiceStatus.dwWaitHint = 60000;
ResolverUpdateStatus();
g_ResolverStatus = RES_STATUS_STOPPING;
//
// wakeup threads to shut down
//
g_StopFlag = TRUE;
if ( g_hStopEvent )
{
if ( !SetEvent(g_hStopEvent) )
{
DnsDbg_PrintfToDebugger(
"DNSCACHE: Error setting g_hStopEvent %lu\n",
GetLastError());
DNS_ASSERT( FALSE );
}
}
g_ResolverStatus = RES_STATUS_SIGNALED_STOP;
//
// cleanup RPC
//
if ( g_InitState & INITFLAG_RPC_SERVER_STARTED )
{
LogEventInMemory( RES_EVENT_STOPRPC, 0 );
Rpc_Shutdown();
#if 0
//status = g_pSvchostData->StopRpcServer( DnsResolver_ServerIfHandle );
#endif
LogEventInMemory( RES_EVENT_STATUS, status );
}
g_ResolverStatus = RES_STATUS_STOP_RPC;
//
// re-signal stop within lock
//
LOCK_CACHE_NO_START();
g_StopFlag = TRUE;
if ( g_hStopEvent )
{
if ( !SetEvent(g_hStopEvent) )
{
DnsDbg_PrintfToDebugger(
"DNSCACHE: Error setting g_hStopEvent %lu\n",
GetLastError());
DNS_ASSERT( FALSE );
}
}
UNLOCK_CACHE();
//
// stop notify thread
//
if ( g_InitState & INITFLAG_NOTIFY_STARTED )
{
ShutdownNotify();
}
g_ResolverStatus = RES_STATUS_STOP_NOTIFY;
//
// stop IP notify thread
//
if ( g_InitState & INITFLAG_IP_LIST_CREATED )
{
ShutdownIpListAndNotify();
}
g_ResolverStatus = RES_STATUS_STOP_IP_LIST;
//
// cleanup cache
//
Cache_Shutdown();
g_ResolverStatus = RES_STATUS_FREE_CACHE;
//
// cleanup service notification list
//
//CleanupServiceNotification();
//g_ResolverStatus = RES_STATUS_FREE_SERVICE_NOTIFY;
//
// cleanup network info globals
//
CleanupNetworkInfo();
g_ResolverStatus = RES_STATUS_FREE_NET_INFO;
//
// cleanup winsock
// cleanup socket caching also
// - this is irrelevant for other services running in
// our process so we shouldn't leave the handles open
//
if ( g_InitState & INITFLAG_WINSOCK )
{
Dns_CacheSocketCleanup();
Dns_CleanupWinsock();
}
//
// cleanup main shutdown event
//
if ( g_InitState & INITFLAG_EVENTS_CREATED )
{
if ( g_hStopEvent )
{
CloseHandle(g_hStopEvent);
g_hStopEvent = NULL;
}
}
g_ResolverStatus = RES_STATUS_DEL_EVENT;
//
// delete critical sections
//
if ( g_InitState & INITFLAG_CS_CREATED )
{
LogEventInMemory( RES_EVENT_DELCRIT_START, 0 );
DeleteCriticalSection( &CacheCritSec );
DeleteCriticalSection( &NetworkListCritSec );
DeleteCriticalSection( &NetworkFailureCritSec );
LogEventInMemory( RES_EVENT_DELCRIT_END, 0 );
}
g_ResolverStatus = RES_STATUS_DEL_CS;
//
// cleanup complete
// tell Service Controller that we are stopped
//
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwControlsAccepted = 0;
ServiceStatus.dwWin32ExitCode = ErrorCode;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
ResolverUpdateStatus();
g_ResolverStatus = RES_STATUS_END;
DNSLOG_F1( "ResolverShutdown - Finished" );
DNSLOG_F1( "" );
}
BOOL
GetServiceControlLock(
VOID
)
/*++
Routine Description:
Get exclusive access handling service control message.
Arguments:
None
Return Value:
TRUE -- have exclusive access to handle SCM, other threads locked out
FALSE -- another thread still handling SCM
--*/
{
BOOL fresult;
//
// set handled flag, if not previously set
// if not previous set -> we have exclusive access
//
fresult = InterlockedCompareExchange(
&g_fServiceControlHandled,
(LONG) TRUE, // new value
(LONG) 0 // previous value to do exchange
);
return !fresult;
}
VOID
ReleaseServiceControlLock(
VOID
)
/*++
Routine Description:
Release service control exclusive access.
Arguments:
None
Return Value:
None
--*/
{
//
// clear handled flag
// - since GetServiceControlLock() uses CompareExchange
// we can just clear without interlock
//
DNS_ASSERT( g_fServiceControlHandled );
g_fServiceControlHandled = FALSE;
}
VOID
ResolverControlHandler(
IN DWORD Opcode
)
/*++
Routine Description:
Service control handler for DNS cache service.
Arguments:
Opcode - specifies service action
Return Value:
None.
--*/
{
LogEventInMemory( RES_EVENT_SCH, Opcode );
DNSLOG_TIME();
DNSLOG_F2( "ResolverControlHandler - Recieved opcode %d", Opcode );
DNSDBG( ANY, (
"\n\n"
"ResolverControlHandler() Opcode = %d\n",
Opcode ));
//
// handle various service control codes
//
switch( Opcode )
{
case SERVICE_CONTROL_STOP:
ResolverShutdown( NO_ERROR );
break;
case SERVICE_CONTROL_PARAMCHANGE :
DNSLOG_F1( " Handle Paramchange" );
DNSLOG_F1( "" );
if ( !GetServiceControlLock() )
{
return;
}
//
// rebuild -- with cache flush
//
HandleConfigChange(
"SC -- ParamChange",
TRUE // flush cache
);
//
// signal other services about PnP
//
// SendServiceNotifications();
ReleaseServiceControlLock();
break;
case SERVICE_CONTROL_NETBINDENABLE:
case SERVICE_CONTROL_NETBINDDISABLE:
DNSLOG_F1( " Handle NetBindEnable\\Disable" );
DNSLOG_F1( "" );
if ( !GetServiceControlLock() )
{
return;
}
//
// rebuild -- with cache flush
//
HandleConfigChange(
"SC -- NetBind",
TRUE // flush cache
);
ReleaseServiceControlLock();
break;
case SERVICE_CONTROL_INTERROGATE:
case SERVICE_CONTROL_NETBINDADD:
case SERVICE_CONTROL_NETBINDREMOVE:
default:
DNSLOG_F1( " This is an unknown opcode, ignoring ..." );
DNSLOG_F1( "" );
break;
}
//
// update service status
//
ResolverUpdateStatus();
DNSLOG_F2( "Resolver Controll Handler (opcode = %d) -- returning", Opcode );
DNSDBG( ANY, (
"Leaving ResolverControlHandler( %d )\n\n\n",
Opcode ));
}
DWORD
ResolverUpdateStatus(
VOID
)
/*++
Routine Description:
Update service controller with current service status.
Arguments:
None.
Return Value:
Return code from SetServiceStatus.
--*/
{
DWORD status = NO_ERROR;
DNSDBG( TRACE, ( "ResolverUpdateStatus()\n" ));
//
// bump the checkpoint
// if status, set it
ServiceStatus.dwCheckPoint++;
#if 0
if ( Status )
{
ServiceStatus.dwWin32ExitCode = Status;
}
#endif
//
// DCR_FIX: this doesn't do much
// usually people write something like this that sets the service
// state AND does the update
//
// QUESTION: what's up with the LogEventInMemory?
//
if ( ServiceStatusHandle == (SERVICE_STATUS_HANDLE) 0 )
{
LogEventInMemory( RES_EVENT_UPDATE_STATUS, ERROR_INVALID_HANDLE );
return ERROR_INVALID_HANDLE;
}
LogEventInMemory( RES_EVENT_UPDATE_STATUS, ServiceStatus.dwWin32ExitCode );
if ( ! SetServiceStatus( ServiceStatusHandle, &ServiceStatus ) )
{
status = GetLastError();
}
LogEventInMemory( RES_EVENT_UPDATE_STATUS, status );
return status;
}
//
// Event logging
//
VOID
ResolverLogEvent(
IN DWORD MessageId,
IN WORD EventType,
IN DWORD StringCount,
IN PWSTR * StringArray,
IN DWORD ErrorCode
)
/*++
Routine Description:
Log to eventlog.
Arguments:
MessageId -- event message id
EventType -- event type (error, warning, info, etc.)
StringCount -- string arg count
StringArray -- imbedded strings
ErrorCode -- error code for data section of event
Return Value:
None
--*/
{
HANDLE hlog;
PVOID pdata = NULL;
//
// open resolver as event source
//
// note: we don't keep log open because events are few
//
hlog = RegisterEventSourceW(
NULL,
DNS_RESOLVER_SERVICE );
if ( hlog == NULL )
{
return;
}
if ( ErrorCode != NO_ERROR )
{
pdata = &ErrorCode;
}
//
// Write to event log
//
// DCR: should get suppression technology here
//
ReportEventW(
hlog,
EventType,
0, // event category
MessageId,
(PSID) NULL,
(WORD) StringCount,
sizeof(DWORD),
StringArray,
(PVOID) pdata );
DeregisterEventSource( hlog );
}
//
// End dnsrslvr.c
//