GLOBAL.C

/*++ 

Copyright (c) 1997 Microsoft Corporation

Module Name:

sample\ip\global.c

Abstract:
Start/Stop and Global Info related functions exported by the
Sample Routing Protocol DLL.

Revision History:


--*/

#include "sampinc.h"

BOOL
DllStartup();
BOOL
DllCleanup();
VOID
InitializeProtocolState();
VOID
DeleteProtocolState();
BOOL
ValidateGlobalInfo(
PSAMPLE_PROTOCOL_GLOBAL_INFO pInfo
);
DWORD WINAPI
MainThread(
PVOID pvContext
);

PNT_IF
GetIfBlockGivenIndex(
DWORD dwIndex
);

VOID
APIENTRY
UpdateRoutes(
PVOID pvContext
);
VOID
InitializeCriticalSections();
VOID
DeleteCriticalSections();
DWORD
CreateEvents();
VOID
DestroyEvents();
DWORD
CreateHeaps();
DWORD
DestroyHeaps();
VOID
CleanupProtocol(
BOOL bWinsockLoaded
);



DWORD g_dwLogLevel;
DWORD g_dwNumIf;
DWORD g_dwTrace;
HANDLE g_hLogHandle;
HANDLE g_hPrivateHeap;
HANDLE g_hRtmHandle;
HANDLE g_hRtmEvent;
HANDLE g_hMgrNotifyEvent;
HANDLE g_hStopProtocolEvent;
HANDLE g_hSocketEvent;
DWORD g_dwProtocolState;
DWORD g_dwProtocolRefCount;
PCHAR g_ppszStateStrings[NUM_STATES];

LIST_ENTRY g_leIfListHead;
LIST_ENTRY g_leMsgQueue;
PMGR_MSG g_pmmStopMsg;

PSUPPORT_FUNCTIONS g_psfnSupportFunctions;
CRITICAL_SECTION g_csIfListLock;
CRITICAL_SECTION g_csProtocolStateLock;
CRITICAL_SECTION g_csGlobalInfoLock;
CRITICAL_SECTION g_csQueueLock;


DWORD
QueueAsyncFunction(
WORKERFUNCTION pfnFunction,
PVOID pvContext,
BOOL bAlertable
)
/*++
Routine Description
Wrapper around function that queues work to a thread

Arguments


Return Value
NO_ERROR

--*/
{
DWORD dwResult;

EnterCriticalSection(&g_csProtocolStateLock);

if (g_dwProtocolState == PROTOCOL_STATE_RUNNING)
{
g_dwProtocolRefCount++;

LeaveCriticalSection(&g_csProtocolStateLock);
}
else
{
LeaveCriticalSection(&g_csProtocolStateLock);

return ERROR_CAN_NOT_COMPLETE;
}

dwResult = QueueWorkItem(pfnFunction,
pvContext,
bAlertable);

//
// If we successfully queued the item, dont decrement the count
//

if(dwResult != NO_ERROR)
{
EnterCriticalSection(&g_csProtocolStateLock);

g_dwProtocolRefCount--;

LeaveCriticalSection(&g_csProtocolStateLock);
}

return dwResult;
}

BOOL
WINAPI
DLLMain(
IN HINSTANCE hInstance,
IN DWORD dwReason,
IN PVOID pvUnused
)
/*++
Routine Description
This is the DLL entrypoint handler. It calls DllStartup
to initialize all data structures that need to be present
even before the StartProtocol() function is called
It calls DllCleanup to free resources that are not freed when
StopProtocol() is called

Arguments
hInstance Handle of the DLL
dwReason Reason why the function is being invoked

Return Value
TRUE If the function succeeds
FALSE otherwise

--*/
{
BOOL bErr;

bErr = FALSE;

switch(dwReason)
{
case DLL_PROCESS_ATTACH:
{
//
// Since we dont need to handle the THREAD_ATTACH and THREAD_DETACH
// reasons, we ask the loader not to invoke our function when a
// thread is created or destroyed in the process. This can be a
// significant performance improvement
//

DisableThreadLibraryCalls(hInstance);

g_dwTrace = INVALID_TRACEID;
g_hLogHandle = NULL;

bErr = DllStartup();

break;
}

case DLL_PROCESS_DETACH:
{
bErr = DllCleanup();

break;
}

default:
{
bErr = TRUE;

break;
}
}

return bErr;
}

BOOL
DllStartup()
/*++
Routine Description
Initializes the tracing and event logging handles.
Initializes the state of the protocol to begin tracking threads that
are executing code in the DLL

Arguments
None

Return Value
TRUE if function succeeds
FALSE otherwise

--*/
{
g_dwTrace = TraceRegister(SAMPLE_PROTOCOL_NAME);
g_hLogHandle = RouterLogRegister(SAMPLE_PROTOCOL_NAME);


InitializeProtocolState();

return TRUE;
}


BOOL
DllCleanup()
/*++
Routine Description
Deletes the tracing and event handles
Frees resources used to track threads in the DLL

Arguments


Return Value
TRUE

--*/
{
DeleteProtocolState();

if(g_hLogHandle)
{
RouterLogDeregister(g_hLogHandle);
}

if(g_dwTrace != INVALID_TRACEID)
{
TraceDeregister(g_dwTrace);
}

return TRUE;
}

VOID
InitializeProtocolState()
/*++
Routine Description
Since the routing protocol is a DLL that may be unloaded, we need
to track all activity occuring in the DLL

Arguments


Return Value
NO_ERROR

--*/
{
InitializeCriticalSection(&g_csProtocolStateLock);

InitializeCriticalSection(&g_csQueueLock);

g_dwProtocolState = PROTOCOL_STATE_STOPPED;
g_dwProtocolRefCount = 0;
g_dwLogLevel = PROTO_LOGGING_ERROR;
}

VOID
DeleteProtocolState()
/*++
Routine Description


Arguments


Return Value
NO_ERROR

--*/
{
ASSERT(g_dwProtocolState == PROTOCOL_STATE_STOPPED);
ASSERT(g_dwProtocolRefCount == 0);

DeleteCriticalSection(&g_csQueueLock);

DeleteCriticalSection(&g_csProtocolStateLock);
}


DWORD
APIENTRY
RegisterProtocol(
IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar,
IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar
)
/*++
Routine Description
This is the first function called by the IP Router Manager.
The Router Manager tells the routing protocol it version and capabilities
It also tells the our DLL, the ID of the protocol it expects us to
register. This allows one DLL to support multiple routing protocols.
We return the functionality we support and a pointer to our functions.

Arguments


Return Value
NO_ERROR

--*/
{
//
// The Router Manager should be calling us to register our protocol.
// The Router Manager must be atleast the version we are compiled with
// The Router Manager must support routing and demand update
//

if(pRoutingChar->dwProtocolId != SAMPLE_PROTOCOL_ROUTE_ID)
{
return ERROR_NOT_SUPPORTED;
}

if(pRoutingChar->dwVersion < MS_ROUTER_VERSION)
{
return ERROR_NOT_SUPPORTED;
}

if(pRoutingChar->fSupportedFunctionality != (ROUTING | DEMAND_UPDATE_ROUTES))
{
return ERROR_NOT_SUPPORTED;
}

//
// Since we are not a service advertiser (an IPX thing)
//

//
// We setup our characteristics and function pointers
//

pServiceChar->fSupportedFunctionality = 0;

pRoutingChar->fSupportedFunctionality = (ROUTING | DEMAND_UPDATE_ROUTES);

pRoutingChar->pfnStartProtocol = StartProtocol;
pRoutingChar->pfnStopProtocol = StopProtocol;
pRoutingChar->pfnAddInterface = AddInterface;
pRoutingChar->pfnDeleteInterface = DeleteInterface;
pRoutingChar->pfnGetEventMessage = GetEventMessage;
pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo;
pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo;
pRoutingChar->pfnBindInterface = BindInterface;
pRoutingChar->pfnUnbindInterface = UnBindInterface;
pRoutingChar->pfnEnableInterface = EnableInterface;
pRoutingChar->pfnDisableInterface = DisableInterface;
pRoutingChar->pfnGetGlobalInfo = GetGlobalInfo;
pRoutingChar->pfnSetGlobalInfo = SetGlobalInfo;
pRoutingChar->pfnMibCreateEntry = MibCreate;
pRoutingChar->pfnMibDeleteEntry = MibDelete;
pRoutingChar->pfnMibGetEntry = MibGet;
pRoutingChar->pfnMibSetEntry = MibSet;
pRoutingChar->pfnMibGetFirstEntry = MibGetFirst;
pRoutingChar->pfnMibGetNextEntry = MibGetNext;
pRoutingChar->pfnUpdateRoutes = DoUpdateRoutes;

return NO_ERROR;
}



DWORD
APIENTRY
StartProtocol(
IN HANDLE hMgrNotifyEvent,
IN PSUPPORT_FUNCTIONS pSupportFunctions,
IN PVOID pvConfig
)
/*++
Routine Description
After the protocol has been registered, the IP Router Manager calls
this function to tell the protocol to start. Most of the startup
code is executed here

Arguments
hMgrNotifyEvent Event to Set if the IP Router Manager needs to
be notified to take any action on our behalf
pSupportFunctions Some functions exported by IP Router Manager
pvConfig Our global configuration which was setup by our
setup/admin DLL

Return Value
NO_ERROR

--*/
{
WSADATA wsaData;
DWORD dwResult, dwThread;
HANDLE hThread;
BOOL bWinsockLoaded;

PSAMPLE_PROTOCOL_GLOBAL_INFO pGlobalInfo;

EnterProtocolApi();

TraceEnter("StartProtocol");

if(pvConfig == NULL)
{
Trace0(ERR,
"StartProtocol: Called with NULL config");

TraceLeave("StartProtocol");

ExitProtocolApi();

return ERROR_INVALID_PARAMETER;
}

pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

if(!ValidateGlobalInfo(pGlobalInfo))
{
TraceLeave("StartProtocol");

ExitProtocolApi();

return ERROR_INVALID_DATA;
}

g_hPrivateHeap = NULL;
g_hRtmEvent = NULL;
g_hMgrNotifyEvent = NULL;
g_hRtmHandle = NULL;
g_hStopProtocolEvent = NULL;
g_hSocketEvent = NULL;
g_pmmStopMsg = NULL;
bWinsockLoaded = FALSE;


g_psfnSupportFunctions = pSupportFunctions;

do
{
InitializeCriticalSections();


//
// We need Winsock2.0 or better
//

dwResult = (DWORD)WSAStartup(MAKEWORD(2,0), &wsaData);

if(dwResult != 0)
{

Trace1(ERR,
"StartProtocol: Error %d starting WinSock.", dwResult);

LogErr0(WINSOCK_FAIL,
dwResult);
break;
}

bWinsockLoaded = TRUE;

dwResult = CreateHeaps();

if(dwResult != NO_ERROR)
{
break;
}

dwResult = CreateEvents();

if(dwResult != NO_ERROR)
{
break;
}



//
// The message that will be used to notify the stopping
// of this protocol. If we cant create this, we wont be
// able to stop
//

g_pmmStopMsg = HeapAlloc(g_hPrivateHeap,
0,
sizeof(MGR_MSG));


if(g_pmmStopMsg == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"StartProtocol: Couldnt allocate stop message. Error %d",
dwResult);

break;
}


//
// Register as an RTM client. We will not keep multiple routes
// to one destination so we set the flags to denote this
//

g_hRtmHandle = RtmRegisterClient(RTM_PROTOCOL_FAMILY_IP,
SAMPLE_PROTOCOL_ROUTE_ID,
g_hRtmEvent,
RTM_PROTOCOL_SINGLE_ROUTE);

if(g_hRtmHandle == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"StartProtocol: Error %d registering with RTM",
dwResult);

LogErr0(RTM_REGISTER_FAILED,
dwResult);

break;
}

//
// Create the main thread. This executes the MainThread() function
//

hThread = CreateThread(NULL, 0,
MainThread,
NULL,
0,
&dwThread);

if (hThread == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"StartProtocol: Error %d starting main thread",
dwResult);

LogErr0(CREATE_THREAD_FAILED,
dwResult);

break;
}

//
// We will never refer to the thread using the handle
// so close it and avoid a leak
//

CloseHandle(hThread);

}while(FALSE);

if(dwResult != NO_ERROR)
{
//
// Something bad happened
//

if(g_pmmStopMsg)
{
HeapFree(g_hPrivateHeap,
0,
g_pmmStopMsg);
}

CleanupProtocol(bWinsockLoaded);

}
else
{
g_hMgrNotifyEvent = hMgrNotifyEvent;

g_dwProtocolState = PROTOCOL_STATE_RUNNING;
}

TraceLeave("StartProtocol");

ExitProtocolApi();

return NO_ERROR;
}


DWORD
APIENTRY
StopProtocol()
/*++
Routine Description
This function is called by the IP Router Manager to tell the protocol
to stop. We set the protocol state to prevent us from servicing
any more requests. Then we notify the MainThread to stop and
return PENDING to the IP Router Manager. If we had no thread and no
work functions in our implementation, we could have stopped
synchronously.

Arguments


Return Value
PENDING

--*/
{
EnterCriticalSection(&g_csProtocolStateLock);

TraceEnter("StopProtocol");

//
// Cannot stop if already stopped
//

if(g_dwProtocolState != PROTOCOL_STATE_RUNNING)
{
Trace1(GLOBAL,
"StopProtocol: Protocol state is %s",
g_ppszStateStrings[g_dwProtocolState]);

TraceLeave("StopProtocol");

LeaveCriticalSection(&g_csProtocolStateLock);

ExitProtocolApi();

return ERROR_CAN_NOT_COMPLETE;
}

//
// Dont release the state lock, so that the other threads
// won't cleanup under us
//

//
// Tell the main thread to stop. That will clean out everything
//

Trace0(GLOBAL,
"StopProtocol: Signalling main thread to stop");

SetEvent(g_hStopProtocolEvent);

//
// Set state to STOPPING;
//

g_dwProtocolState = PROTOCOL_STATE_STOPPING;

Trace1(GLOBAL,
"StopProtocol: %d threads are still active",
g_dwProtocolRefCount);


TraceLeave("StopProtocol");

LeaveCriticalSection(&g_csProtocolStateLock);

return ERROR_PROTOCOL_STOP_PENDING;
}


DWORD
APIENTRY
GetGlobalInfo(
IN OUT PVOID pvConfig,
IN OUT PDWORD pdwSize
)
/*++
Routine Description
The function is called by the IP Router Manager, usually in because of
a query by the admin utility. We see if we have space enough to
return our global config. If we do we return it, otherwise we return the
size needed

Arguments
pvConfig Pointer to allocated buffer to store our config
pdwSize Size of config.

Return Value
ERROR_INSUFFICIENT_BUFFER If the size of the buffer is too small
ERROR_INVALID_PARAMETER
ERROR_INVALID_DATA
NO_ERROR

--*/
{
PSAMPLE_PROTOCOL_GLOBAL_INFO pGlobalInfo;

EnterProtocolApi();

TraceEnter("GetGlobalInfo");

if(pdwSize == NULL)
{
Trace0(ERR,
"GetGlobalInfo: Router Manager called us with NULL size");

TraceLeave("GetGlobalInfo");

ExitProtocolApi();

return ERROR_INVALID_PARAMETER;
}


if((*pdwSize < sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO))||
(pvConfig == NULL))
{
//
// Either the size was too small or there was no
// storage
//

*pdwSize = sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO);

Trace3(GLOBAL,
"GetGlobalInfo: Router Manager called us with size %d and info %x. Info size should be %d",
*pdwSize,
pvConfig,
sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO));

TraceLeave("GetGlobalInfo");

ExitProtocolApi();

return ERROR_INSUFFICIENT_BUFFER;
}

*pdwSize = sizeof(SAMPLE_PROTOCOL_GLOBAL_INFO);

//
// Ok, so we have a good buffer to write our info into
//

pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

//
// Currently that is all the info we have
//

EnterCriticalSection(&g_csGlobalInfoLock);

pGlobalInfo->dwLogLevel = g_dwLogLevel;

LeaveCriticalSection(&g_csGlobalInfoLock);

TraceLeave("GetGlobalInfo");

ExitProtocolApi();

return NO_ERROR;
}



DWORD
APIENTRY
SetGlobalInfo(
IN PVOID pvConfig
)
/*++
Routine Description
Called by the IP Router Manager usually in response to an admin
utility changing the config. We verify the parameters and the
info and set it

Arguments


Return Value
ERROR_INVALID_PARAMETER
ERROR_INVALID_DATA
NO_ERROR

--*/
{
PSAMPLE_PROTOCOL_GLOBAL_INFO pGlobalInfo;

EnterProtocolApi();

TraceEnter("SetGlobalInfo");

if(pvConfig == NULL)
{
Trace0(ERR,
"SetGlobalInfo: Router Manager called us with NULL info");

TraceLeave("SetGlobalInfo");

ExitProtocolApi();

return ERROR_INVALID_PARAMETER;
}


pGlobalInfo = (PSAMPLE_PROTOCOL_GLOBAL_INFO)pvConfig;

if(!ValidateGlobalInfo(pGlobalInfo))
{
//
// Bad global info
//

TraceLeave("SetGlobalInfo");

ExitProtocolApi();

return ERROR_INVALID_DATA;
}

//
// Good info
//

EnterCriticalSection(&g_csGlobalInfoLock);

g_dwLogLevel = pGlobalInfo->dwLogLevel;

LeaveCriticalSection(&g_csGlobalInfoLock);

TraceLeave("SetGlobalInfo");

ExitProtocolApi();

return ERROR_INVALID_DATA;

}

DWORD
APIENTRY
DoUpdateRoutes(
IN DWORD dwIndex
)
/*++
Routine Description
This function is called by the IP Router Manger to ask us to
update routes over a Demand Dial link. The link has already been
brought up so should be in ENABLED-BOUND state. After we are done
we need to set the g_hMgrNotifyEvent to inform the Router Manager
that we have a message in our queue to be delivered to it. The
Router Manager will call our GetEventMessage() function in which
we will inform it that we are done with update routes (and the routes
have been stored in RTM). The Router Manager will "freeze" these routes
by converting them to AUTOSTATIC.

Arguments


Return Value
NO_ERROR/PENDING For Success

--*/
{
DWORD dwResult;

//
// We will fire off a worker function to do the "real work"
// That will set the event
//

dwResult = QueueAsyncFunction(UpdateRoutes,
(PVOID)dwIndex,
TRUE);


return dwResult;
}


BOOL
ValidateGlobalInfo(
PSAMPLE_PROTOCOL_GLOBAL_INFO pInfo
)
/*++
Routine Description
Checks to see if the global info is OK. It is good practice to do this
because a corrupt registry can change configuration causing all sorts of
debugging headaches if it is not found early

Arguments
pInfo The Global Info to check

Return Value
TRUE If the info is good
FALSE otherwise

--*/
{
//
// Ensure that the logging level is within bounds
//

if((pInfo->dwLogLevel <= PROTO_LOGGING_INFO) &&
(pInfo->dwLogLevel >= PROTO_LOGGING_NONE))
{
return TRUE;
}

return FALSE;
}

DWORD
APIENTRY
GetEventMessage(
OUT ROUTING_PROTOCOL_EVENTS *pEvent,
OUT PMESSAGE pResult
)
/*++
Routine Description
This is called by the IP Router Manager if we indicate that we have
a message in our queue to be delivered to it (by setting the
g_hMgrNotifyEvent)

Arguments


Return Value
NO_ERROR

--*/
{
PLIST_ENTRY pleNode;
PMGR_MSG pmmMsg;

//
// IMPORTANT: This can be called after the protocol is stopped
// so we dont do ref counting for this API.
//

EnterCriticalSection(&g_csQueueLock);

if(IsListEmpty(&g_leMsgQueue))
{
LeaveCriticalSection(&g_csQueueLock);

return ERROR_NO_MORE_ITEMS;
}

pleNode = RemoveHeadList(&g_leMsgQueue);

LeaveCriticalSection(&g_csQueueLock);

pmmMsg = CONTAINING_RECORD(pleNode,
MGR_MSG,
leMsgLink);

*pEvent = pmmMsg->rreEvent;
*pResult = pmmMsg->mResult;


HeapFree(g_hPrivateHeap,
0,
pmmMsg);

return NO_ERROR;
}



VOID
APIENTRY
UpdateRoutes(
PVOID pvContext
)
/*++
Routine Description

Arguments


Return Value
NO_ERROR

--*/
{
DWORD dwIndex;
PNT_IF pIf;
PMGR_MSG pmmMsg;


//
// We dont EnterProtocolApi() here since it has been done for us
// when we were queued for execution
//

TraceEnter("UpdateRoutes");

dwIndex = (DWORD)pvContext;

EnterCriticalSection(&g_csIfListLock);

pIf = GetIfBlockGivenIndex(dwIndex);

if(pIf == NULL)
{
//
// This can happen because the interface could have been deleted
// between the now and the time this function was queued for
// execution
//

LeaveCriticalSection(&g_csIfListLock);

TraceLeave("UpdateRoutes");

//
// We must decrement the ref count for the protocol
// though
//

ExitProtocolApi();

return;
}

//
// Here we do the protocol processing stuff
// In the sample we wont do anything
//

LeaveCriticalSection(&g_csIfListLock);

//
// We allocate the memory for a message here and free it in
// GetEventMessage()
//

pmmMsg = HeapAlloc(g_hPrivateHeap,
0,
sizeof(MGR_MSG));

if(pmmMsg == NULL)
{
Trace1(ERR,
"UpdateRoutes: Unable to allocate memory for message. Error %d",
GetLastError());

TraceLeave("UpdateRoutes");

ExitProtocolApi();

return;
}


pmmMsg->rreEvent = UPDATE_COMPLETE;

pmmMsg->mResult.UpdateCompleteMessage.InterfaceIndex = dwIndex;
pmmMsg->mResult.UpdateCompleteMessage.UpdateType = DEMAND_UPDATE_ROUTES;
pmmMsg->mResult.UpdateCompleteMessage.UpdateStatus = NO_ERROR;

EnterCriticalSection(&g_csQueueLock);

InsertHeadList(&g_leMsgQueue,
&pmmMsg->leMsgLink);

LeaveCriticalSection(&g_csQueueLock);

SetEvent(g_hMgrNotifyEvent);

TraceLeave("UpdateRoutes");

ExitProtocolApi();

return;
}

VOID
InitializeCriticalSections()
/*++
Routine Description


Arguments


Return Value
NO_ERROR

--*/
{
InitializeCriticalSection(&g_csIfListLock);
InitializeCriticalSection(&g_csProtocolStateLock);
InitializeCriticalSection(&g_csGlobalInfoLock);
}

VOID
DeleteCriticalSections()
/*++
Routine Description


Arguments


Return Value
NO_ERROR

--*/
{
DeleteCriticalSection(&g_csIfListLock);
DeleteCriticalSection(&g_csProtocolStateLock);
DeleteCriticalSection(&g_csGlobalInfoLock);
}

DWORD
CreateEvents()
/*++
Routine Description
Create all the events necessary

Arguments


Return Value
None

--*/
{
DWORD dwResult;


//
// Create the event used to get notifications from RTM about new routes
// It has default security, is auto reset since only one thread will wait
// on it. Initially set to FALSE. For debug versions we name the event
// This helps in detecting leaks
//


#if DBG
#define EVENT_NAME "RtmEventForSampleProtocol"
#else
#define EVENT_NAME NULL
#endif

g_hRtmEvent = CreateEvent(NULL,
FALSE,
FALSE,
EVENT_NAME);

#undef EVENT_NAME

if(g_hRtmEvent == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"CreateEvents: Error %d creating RTM Event",
dwResult);

LogErr0(EVENT_FAIL,dwResult);

return dwResult;
}

#if DBG
#define EVENT_NAME "SocketEventForSampleProtocol"
#else
#define EVENT_NAME NULL
#endif

g_hSocketEvent = CreateEvent(NULL,
FALSE,
FALSE,
EVENT_NAME);

#undef EVENT_NAME

if(g_hSocketEvent == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"CreateEvents: Error %d creating Socket Event",
dwResult);

LogErr0(EVENT_FAIL,dwResult);

return dwResult;
}

#if DBG
#define EVENT_NAME "StopEventForSampleProtocol"
#else
#define EVENT_NAME NULL
#endif

g_hStopProtocolEvent = CreateEvent(NULL,
FALSE,
FALSE,
EVENT_NAME);

#undef EVENT_NAME

if(g_hStopProtocolEvent == NULL)
{
dwResult = GetLastError();

Trace1(ERR,
"CreateEvents: Error %d creating Stop Protocol Event",
dwResult);

LogErr0(EVENT_FAIL,dwResult);

return dwResult;
}

return NO_ERROR;
}

VOID
DestroyEvents()
/*++
Routine Description
Close all the event handles

Arguments


Return Value
NO_ERROR

--*/
{
if(g_hRtmEvent != NULL)
{
CloseHandle(g_hRtmEvent);
}

if(g_hStopProtocolEvent != NULL)
{
CloseHandle(g_hStopProtocolEvent);
}

if(g_hSocketEvent != NULL)
{
CloseHandle(g_hSocketEvent);
}
}

DWORD
CreateHeaps()
/*++
Routine Description


Arguments


Return Value
NO_ERROR

--*/
{
DWORD dwResult;

g_hPrivateHeap = HeapCreate(0,0,0);

if(g_hPrivateHeap == NULL)

{ 
dwResult = GetLastError();

Trace1(ERR,
"CreateHeaps: Error %d creating heap",
dwResult);

return dwResult;
}

return NO_ERROR;
}

DWORD
DestroyHeaps()
/*++
Routine Description


Arguments


Return Value
NO_ERROR

--*/
{
if(g_hPrivateHeap != NULL)
{
HeapDestroy(g_hPrivateHeap);
}

return NO_ERROR;
}


VOID
CleanupProtocol(
BOOL bWinsockLoaded
)
/*++
Routine Description


Locks


Arguments


Return Value
NO_ERROR

--*/
{

if(bWinsockLoaded)
{
if(WSACleanup() != NO_ERROR)
{
Trace1(ERR,
"CleanupProtocol: Error %d cleaning up WinSock",
WSAGetLastError());
}
}

if(g_hRtmHandle)
{
RtmDeregisterClient(g_hRtmHandle);
}


DestroyEvents();
DeleteCriticalSections();
DestroyHeaps();

return;
}