Leaked source code of windows server 2003
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.
 
 
 
 
 
 

908 lines
26 KiB

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
comObjs
Abstract:
This file provides the implementation for the communcation objects used in
Calais. A communications object (CComObject and it's derivatives) is
capable of transmitting itself across a CComChannel.
Author:
Doug Barlow (dbarlow) 11/6/1996
Environment:
Win32, C++ w/ Exceptions
Notes:
?Notes?
--*/
#define __SUBROUTINE__
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <WinSCard.h>
#include <CalMsgs.h>
#include <CalCom.h>
#include <limits.h>
//
// Maximum size for the payload of a service request or response.
//
// Current value is max size of an unsigned WORD, as this is the max
// card packet size defined by ISO.
//
#define cbMAX_COM_REQUEST_RESPONSE_SIZE USHRT_MAX
const DWORD
CComObject::AUTOCOUNT = 0, // Force computing string length.
CComObject::MULTISTRING = (DWORD)(-1); // Force computing multistring len.
//
//==============================================================================
//
// CComObject and derivatives.
//
/*++
CComObject:
This is the base constructor for a CComObject. These objects assume that
they are not in charge of anything past their own internal buffers.
Therefore they won't close handles, etc, when destructing.
Arguments:
None
Return Value:
None
Throws:
None
Author:
Doug Barlow (dbarlow) 11/13/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::CComObject")
CComObject::CComObject(
void)
: m_bfRequest(),
m_bfResponse()
{
m_pbfActive = NULL;
}
/*++
ReceiveComObject:
This is a static member routine that creates the proper CComObject child
object for the data coming in on a CComChannel.
Arguments:
pChannel supplies a pointer to the CComChannel on which the transfer
structure will come in.
Return Value:
The newly created CComObject child object. This object must be cleaned up
via the delete command.
Throws:
?exceptions?
Author:
Doug Barlow (dbarlow) 11/13/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::ReceiveComObject")
CComObject *
CComObject::ReceiveComObject(
CComChannel *pChannel)
{
CComObject *pCom = NULL;
DWORD dwMinSize = (DWORD)(-1);
try
{
DWORD rgdwInData[2];
//
// See what's coming.
//
pChannel->Receive(rgdwInData, sizeof(rgdwInData));
if (sizeof(rgdwInData) > rgdwInData[1])
throw (DWORD)SCARD_F_COMM_ERROR;
switch (rgdwInData[0]) // dwCommndId
{
case EstablishContext_request:
dwMinSize = sizeof(ComEstablishContext::CObjEstablishContext_request);
pCom = new ComEstablishContext;
break;
case EstablishContext_response:
dwMinSize = sizeof(ComEstablishContext::CObjEstablishContext_response);
pCom = new ComEstablishContext;
break;
case ReleaseContext_request:
dwMinSize = sizeof(ComReleaseContext::CObjReleaseContext_request);
pCom = new ComReleaseContext;
break;
case ReleaseContext_response:
dwMinSize = sizeof(ComReleaseContext::CObjReleaseContext_response);
pCom = new ComReleaseContext;
break;
case IsValidContext_request:
dwMinSize = sizeof(ComIsValidContext::CObjIsValidContext_request);
pCom = new ComIsValidContext;
break;
case IsValidContext_response:
dwMinSize = sizeof(ComIsValidContext::CObjIsValidContext_response);
pCom = new ComIsValidContext;
break;
case ListReaders_request:
dwMinSize = sizeof(ComListReaders::CObjListReaders_request);
pCom = new ComListReaders;
break;
case ListReaders_response:
dwMinSize = sizeof(ComListReaders::CObjListReaders_response);
pCom = new ComListReaders;
break;
#if 0
case ListReaderGroups_request:
dwMinSize = sizeof(ComListReaderGroups::CObjListReaderGroups_request);
pCom = new ComListReaderGroups;
break;
case ListReaderGroups_response:
dwMinSize = sizeof(ComListReaderGroups::CObjListReaderGroups_response);
pCom = new ComListReaderGroups;
break;
case ListCards_request:
dwMinSize = sizeof(ComListCards::CObjListCards_request);
pCom = new ComListCards;
break;
case ListCards_response:
dwMinSize = sizeof(ComListCards::CObjListCards_response);
pCom = new ComListCards;
break;
case ListInterfaces_request:
dwMinSize = sizeof(ComListInterfaces::CObjListInterfaces_request);
pCom = new ComListInterfaces;
break;
case ListInterfaces_response:
dwMinSize = sizeof(ComListInterfaces::CObjListInterfaces_response);
pCom = new ComListInterfaces;
break;
case GetProviderId_request:
dwMinSize = sizeof(ComGetProviderId::CObjGetProviderId_request);
pCom = new ComGetProviderId;
break;
case GetProviderId_response:
dwMinSize = sizeof(ComGetProviderId::CObjGetProviderId_response);
pCom = new ComGetProviderId;
break;
case IntroduceReaderGroup_request:
dwMinSize = sizeof(ComIntroduceReaderGroup::CObjIntroduceReaderGroup_request);
pCom = new ComIntroduceReaderGroup;
break;
case IntroduceReaderGroup_response:
dwMinSize = sizeof(ComIntroduceReaderGroup::CObjIntroduceReaderGroup_response);
pCom = new ComIntroduceReaderGroup;
break;
case ForgetReaderGroup_request:
dwMinSize = sizeof(ComForgetReaderGroup::CObjForgetReaderGroup_request);
pCom = new ComForgetReaderGroup;
break;
case ForgetReaderGroup_response:
dwMinSize = sizeof(ComForgetReaderGroup::CObjForgetReaderGroup_response);
pCom = new ComForgetReaderGroup;
break;
case IntroduceReader_request:
dwMinSize = sizeof(ComIntroduceReader::CObjIntroduceReader_request);
pCom = new ComIntroduceReader;
break;
case IntroduceReader_response:
dwMinSize = sizeof(ComIntroduceReader::CObjIntroduceReader_response);
pCom = new ComIntroduceReader;
break;
case ForgetReader_request:
dwMinSize = sizeof(ComForgetReader::CObjForgetReader_request);
pCom = new ComForgetReader;
break;
case ForgetReader_response:
dwMinSize = sizeof(ComForgetReader::CObjForgetReader_response);
pCom = new ComForgetReader;
break;
case AddReaderToGroup_request:
dwMinSize = sizeof(ComAddReaderToGroup::CObjAddReaderToGroup_request);
pCom = new ComAddReaderToGroup;
break;
case AddReaderToGroup_response:
dwMinSize = sizeof(ComAddReaderToGroup::CObjAddReaderToGroup_response);
pCom = new ComAddReaderToGroup;
break;
case RemoveReaderFromGroup_request:
dwMinSize = sizeof(ComRemoveReaderFromGroup::CObjRemoveReaderFromGroup_request);
pCom = new ComRemoveReaderFromGroup;
break;
case RemoveReaderFromGroup_response:
dwMinSize = sizeof(ComRemoveReaderFromGroup::CObjRemoveReaderFromGroup_response);
pCom = new ComRemoveReaderFromGroup;
break;
case IntroduceCardType_request:
dwMinSize = sizeof(ComIntroduceCardType::CObjIntroduceCardType_request);
pCom = new ComIntroduceCardType;
break;
case IntroduceCardType_response:
dwMinSize = sizeof(ComIntroduceCardType::CObjIntroduceCardType_response);
pCom = new ComIntroduceCardType;
break;
case ForgetCardType_request:
dwMinSize = sizeof(ComForgetCardType::CObjForgetCardType_request);
pCom = new ComForgetCardType;
break;
case ForgetCardType_response:
dwMinSize = sizeof(ComForgetCardType::CObjForgetCardType_response);
pCom = new ComForgetCardType;
break;
case FreeMemory_request:
dwMinSize = sizeof(ComFreeMemory::CObjFreeMemory_request);
pCom = new ComFreeMemory;
break;
case FreeMemory_response:
dwMinSize = sizeof(ComFreeMemory::CObjFreeMemory_response);
pCom = new ComFreeMemory;
break;
case Cancel_request:
dwMinSize = sizeof(ComCancel::CObjCancel_request);
pCom = new ComCancel;
break;
case Cancel_response:
dwMinSize = sizeof(ComCancel::CObjCancel_response);
pCom = new ComCancel;
break;
#endif
case LocateCards_request:
dwMinSize = sizeof(ComLocateCards::CObjLocateCards_request);
pCom = new ComLocateCards;
break;
case LocateCards_response:
dwMinSize = sizeof(ComLocateCards::CObjLocateCards_response);
pCom = new ComLocateCards;
break;
case GetStatusChange_request:
dwMinSize = sizeof(ComGetStatusChange::CObjGetStatusChange_request);
pCom = new ComGetStatusChange;
break;
case GetStatusChange_response:
dwMinSize = sizeof(ComGetStatusChange::CObjGetStatusChange_response);
pCom = new ComGetStatusChange;
break;
case Connect_request:
dwMinSize = sizeof(ComConnect::CObjConnect_request);
pCom = new ComConnect;
break;
case Connect_response:
dwMinSize = sizeof(ComConnect::CObjConnect_response);
pCom = new ComConnect;
break;
case Reconnect_request:
dwMinSize = sizeof(ComReconnect::CObjReconnect_request);
pCom = new ComReconnect;
break;
case Reconnect_response:
dwMinSize = sizeof(ComReconnect::CObjReconnect_response);
pCom = new ComReconnect;
break;
case Disconnect_request:
dwMinSize = sizeof(ComDisconnect::CObjDisconnect_request);
pCom = new ComDisconnect;
break;
case Disconnect_response:
dwMinSize = sizeof(ComDisconnect::CObjDisconnect_response);
pCom = new ComDisconnect;
break;
case BeginTransaction_request:
dwMinSize = sizeof(ComBeginTransaction::CObjBeginTransaction_request);
pCom = new ComBeginTransaction;
break;
case BeginTransaction_response:
dwMinSize = sizeof(ComBeginTransaction::CObjBeginTransaction_response);
pCom = new ComBeginTransaction;
break;
case EndTransaction_request:
dwMinSize = sizeof(ComEndTransaction::CObjEndTransaction_request);
pCom = new ComEndTransaction;
break;
case EndTransaction_response:
dwMinSize = sizeof(ComEndTransaction::CObjEndTransaction_response);
pCom = new ComEndTransaction;
break;
case Status_request:
dwMinSize = sizeof(ComStatus::CObjStatus_request);
pCom = new ComStatus;
break;
case Status_response:
dwMinSize = sizeof(ComStatus::CObjStatus_response);
pCom = new ComStatus;
break;
case Transmit_request:
dwMinSize = sizeof(ComTransmit::CObjTransmit_request);
pCom = new ComTransmit;
break;
case Transmit_response:
dwMinSize = sizeof(ComTransmit::CObjTransmit_response);
pCom = new ComTransmit;
break;
case OpenReader_request:
dwMinSize = sizeof(ComOpenReader::CObjOpenReader_request);
pCom = new ComOpenReader;
break;
case OpenReader_response:
dwMinSize = sizeof(ComOpenReader::CObjOpenReader_response);
pCom = new ComOpenReader;
break;
case Control_request:
dwMinSize = sizeof(ComControl::CObjControl_request);
pCom = new ComControl;
break;
case Control_response:
dwMinSize = sizeof(ComControl::CObjControl_response);
pCom = new ComControl;
break;
case GetAttrib_request:
dwMinSize = sizeof(ComGetAttrib::CObjGetAttrib_request);
pCom = new ComGetAttrib;
break;
case GetAttrib_response:
dwMinSize = sizeof(ComGetAttrib::CObjGetAttrib_response);
pCom = new ComGetAttrib;
break;
case SetAttrib_request:
dwMinSize = sizeof(ComSetAttrib::CObjSetAttrib_request);
pCom = new ComSetAttrib;
break;
case SetAttrib_response:
dwMinSize = sizeof(ComSetAttrib::CObjSetAttrib_response);
pCom = new ComSetAttrib;
break;
default:
CalaisWarning(
__SUBROUTINE__,
DBGT("Invalid Comm Object Id on pipe"));
throw (DWORD)SCARD_F_COMM_ERROR;
}
if (NULL == pCom)
{
CalaisWarning(
__SUBROUTINE__,
DBGT("No memory for incoming comm object"));
throw (DWORD)SCARD_E_NO_MEMORY;
}
if (dwMinSize > rgdwInData[1])
throw (DWORD)SCARD_F_COMM_ERROR;
if (0 == (rgdwInData[0] & 0x01)) // Request or response?
pCom->m_pbfActive = &pCom->m_bfRequest;
else
pCom->m_pbfActive = &pCom->m_bfResponse;
//
// Pull it in.
//
if (cbMAX_COM_REQUEST_RESPONSE_SIZE < rgdwInData[1])
throw (DWORD) SCARD_E_INVALID_PARAMETER;
pCom->m_pbfActive->Resize(rgdwInData[1]);
CopyMemory(
pCom->m_pbfActive->Access(),
rgdwInData,
sizeof(rgdwInData));
pChannel->Receive(
pCom->m_pbfActive->Access(sizeof(rgdwInData)),
rgdwInData[1] - sizeof(rgdwInData));
#ifdef DBG
WriteApiLog(pCom->m_pbfActive->Access(), pCom->m_pbfActive->Length());
for (DWORD ix = 0; ix < rgdwInData[1] / sizeof(DWORD); ix += 1)
{
ASSERT(0xcdcdcdcd != *(LPDWORD)pCom->m_pbfActive->Access(
ix * sizeof(DWORD)));
}
#endif
}
catch (...)
{
if (NULL != pCom)
delete pCom;
throw;
}
return pCom;
}
/*++
Receive:
This function receives a specific com object.
Arguments:
pChannel supplies a pointer to the CComChannel on which the transfer
structure will come in.
Return Value:
None
Throws:
?exceptions?
Author:
Doug Barlow (dbarlow) 11/18/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::Receive")
CComObject::CObjGeneric_response *
CComObject::Receive(
CComChannel *pChannel)
{
DWORD rgdwInData[2];
CComObject::CObjGeneric_response *pRsp
= (CComObject::CObjGeneric_response *)Data();
pChannel->Receive(rgdwInData, sizeof(rgdwInData));
if (rgdwInData[0] != pRsp->dwCommandId)
{
CalaisWarning(
__SUBROUTINE__,
DBGT("Comm Object receive object mismatch"));
throw (DWORD)SCARD_F_COMM_ERROR;
}
if (rgdwInData[1] < sizeof(CComObject::CObjGeneric_response))
{
CalaisWarning(
__SUBROUTINE__,
DBGT("Comm Object receive object invalid"));
throw (DWORD)SCARD_F_COMM_ERROR;
}
ASSERT(m_pbfActive == ((0 == (rgdwInData[0] & 0x01)
? &m_bfRequest
: &m_bfResponse)));
//
// Pull it in.
//
m_pbfActive->Resize(rgdwInData[1]);
CopyMemory(
m_pbfActive->Access(),
rgdwInData,
sizeof(rgdwInData));
pChannel->Receive(
m_pbfActive->Access(sizeof(rgdwInData)),
rgdwInData[1] - sizeof(rgdwInData));
#ifdef DBG
for (DWORD ix = 0; ix < rgdwInData[1] / sizeof(DWORD); ix += 1)
{
ASSERT(0xcdcdcdcd != *(LPDWORD)m_pbfActive->Access(ix * sizeof(DWORD)));
}
WriteApiLog(m_pbfActive->Access(), m_pbfActive->Length());
#endif
return (CComObject::CObjGeneric_response *)m_pbfActive->Access();
}
/*++
Send:
This function sends the ComObject over the given Comm Channel.
Arguments:
pChannel supplies a pointer to the CComChannel on which the transfer
structure will be sent.
Return Value:
None
Throws:
Errors are thrown as DWORD status codes.
Remarks:
Author:
Doug Barlow (dbarlow) 8/5/1998
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::Send")
DWORD
CComObject::Send(
CComChannel *pChannel)
{
#ifdef DBG
ComObjCheck;
WriteApiLog(Data(), Length());
#endif
return pChannel->Send(Data(), Length());
}
/*++
InitStruct:
This method implements simple base class preparation to build request and
response structures.
Arguments:
dwCommandId supplies the command identifier.
dwDataOffset supplies the size of the structure to be inserted.
Return Value:
None
Throws:
?exceptions?
Author:
Doug Barlow (dbarlow) 11/13/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::InitStruct")
void
CComObject::InitStruct(
DWORD dwCommandId,
DWORD dwDataOffset,
DWORD dwExtra)
{
if (0 == (dwCommandId & 0x01))
m_pbfActive = &m_bfRequest;
else
m_pbfActive = &m_bfResponse;
ASSERT(NULL != m_pbfActive);
ASSERT(0 == dwDataOffset % sizeof(DWORD));
CObjGeneric_request *pReq =
(CObjGeneric_request *)m_pbfActive->Presize(dwDataOffset + dwExtra);
m_pbfActive->Resize(dwDataOffset, TRUE);
pReq->dwCommandId = dwCommandId;
pReq->dwTotalLength = dwDataOffset;
pReq->dwDataOffset = dwDataOffset;
}
/*++
Append:
These methods append data to the transfer structure, updating the Total
Length. Note that this action may affect the address of the structure being
appended to. This routine returns the address of that structure, in case it
changes.
Arguments:
dsc supplies the descriptor to fill in with the offset and length.
szString supplies the data to be appended as a string value.
cchLen supplies the length of the data to be appended in characters, or one
of the following special flags:
AUTOCOUNT - The string's size should be determined via lstrlen.
MULTISTRING - The string's size should be determined via mstrlen;
Return Value:
The address of the updated structure, which may have moved in memory.
Throws:
None
Author:
Doug Barlow (dbarlow) 11/13/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::Append")
LPBYTE
CComObject::Append(
CComObject::Desc &dsc,
LPCTSTR szString,
DWORD cchLen)
{
DWORD dwLen;
switch (cchLen)
{
case AUTOCOUNT:
dwLen = lstrlen(szString) + 1; // Include trailing null char.
break;
case MULTISTRING:
dwLen = MStrLen(szString); // It includes trailing null char.
break;
default:
dwLen = cchLen;
}
dwLen *= sizeof(TCHAR);
return Append(dsc, (LPCBYTE)szString, dwLen);
}
LPBYTE
CComObject::Append(
CComObject::Desc &dsc,
LPCBYTE pbData,
DWORD cbLength)
{
static const DWORD dwZero = 0;
DWORD
dwDataLength,
dwPadLen;
CObjGeneric_request *pData;
ComObjCheck;
dwPadLen = sizeof(DWORD) - cbLength % sizeof(DWORD);
if (sizeof(DWORD) == dwPadLen)
dwPadLen = 0;
dwDataLength = m_pbfActive->Length() + cbLength + dwPadLen;
dsc.dwOffset = m_pbfActive->Length();
dsc.dwLength = cbLength;
// Now we might change the address of dsc.
m_pbfActive->Presize(dwDataLength, TRUE);
m_pbfActive->Append(pbData, cbLength);
m_pbfActive->Append((LPCBYTE)&dwZero, dwPadLen);
pData = (CObjGeneric_request *)m_pbfActive->Access();
pData->dwTotalLength = dwDataLength;
return m_pbfActive->Access();
}
/*++
Parse:
This routine converts a given descriptor in the current communications
object buffer back into a pointer and optional length.
Arguments:
dsc supplies the descriptor of the current communications object to be
parsed.
pcbLen receives the length, in bytes, of the value referenced by the
descriptor. If this parameter is NULL, no length value is returned.
Return Value:
The address of the value referenced by the descriptor.
Throws:
?exceptions?
Author:
Doug Barlow (dbarlow) 12/11/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::Parse")
LPCVOID
CComObject::Parse(
Desc &dsc,
LPDWORD pcbLen)
{
CObjGeneric_request *pGen;
ComObjCheck;
pGen = (CObjGeneric_request *)m_pbfActive->Access();
ASSERT((LPCVOID)&dsc > (LPCVOID)m_pbfActive->Access());
ASSERT((LPCVOID)&dsc
< (LPCVOID)m_pbfActive->Access(m_pbfActive->Length() - 1));
ASSERT((LPCVOID)&dsc
< (LPCVOID)m_pbfActive->Access(pGen->dwDataOffset - 1));
if (dsc.dwOffset + dsc.dwLength > m_pbfActive->Length())
throw (DWORD)SCARD_F_COMM_ERROR;
if (NULL != pcbLen)
*pcbLen = dsc.dwLength;
return m_pbfActive->Access(dsc.dwOffset);
}
#ifdef DBG
/*++
dbgCheck:
This routine validates the internal structure of a CComObject.
Arguments:
None
Return Value:
None
Throws:
None, but it will assert if something is wrong.
Author:
Doug Barlow (dbarlow) 12/11/1996
--*/
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("CComObject::dbgCheck")
void
CComObject::dbgCheck(
void)
const
{
DWORD
dwCommandId,
dwDataLength,
dwDataOffset;
CObjGeneric_request *pData;
ASSERT(EstablishContext_request == 0);
ASSERT(NULL != m_pbfActive);
ASSERT(3 * sizeof(DWORD) <= m_pbfActive->Length());
pData = (CObjGeneric_request *)m_pbfActive->Access();
dwCommandId = pData->dwCommandId;
dwDataLength = pData->dwTotalLength;
dwDataOffset = pData->dwDataOffset;
ASSERT(dwDataLength == m_pbfActive->Length());
ASSERT(dwDataOffset <= dwDataLength);
ASSERT(0 == dwDataOffset % sizeof(DWORD));
ASSERT(0 == dwDataLength % sizeof(DWORD));
ASSERT(m_pbfActive
== ((0 == (dwCommandId & 0x01))
? &m_bfRequest
: &m_bfResponse));
}
typedef struct
{
SYSTEMTIME stLogTime;
DWORD dwProcId;
DWORD dwThreadId;
} LogStamp;
#undef __SUBROUTINE__
#define __SUBROUTINE__ DBGT("WriteApiLog")
void
WriteApiLog(
LPCVOID pvData,
DWORD cbLength)
{
static HANDLE hLogMutex = NULL;
BOOL fGotMutex = FALSE;
HANDLE hLogFile = INVALID_HANDLE_VALUE;
try
{
hLogFile = CreateFile(
CalaisString(CALSTR_APITRACEFILENAME),
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE != hLogFile)
{
LogStamp stamp;
DWORD dwLen;
DWORD dwSts;
BOOL fSts;
if (NULL == hLogMutex)
{
CSecurityDescriptor acl;
acl.Initialize();
acl.Allow(
&acl.SID_World,
SEMAPHORE_ALL_ACCESS);
hLogMutex = CreateMutex(
acl,
FALSE,
TEXT("Microsoft Smart Card Logging synchronization"));
}
dwSts = WaitForAnObject(hLogMutex, 1000); // One second max.
if (ERROR_SUCCESS == dwSts)
{
fGotMutex = TRUE;
dwLen = SetFilePointer(hLogFile, 0, NULL, FILE_END);
ASSERT(-1 != dwLen);
GetLocalTime(&stamp.stLogTime);
stamp.dwProcId = GetCurrentProcessId();
stamp.dwThreadId = GetCurrentThreadId();
fSts = WriteFile(
hLogFile,
&stamp,
sizeof(stamp),
&dwLen,
NULL);
ASSERT(fSts);
fSts = WriteFile(
hLogFile,
pvData,
cbLength,
&dwLen,
NULL);
ASSERT(fSts);
ASSERT(dwLen == cbLength);
}
}
}
catch (...) {}
if (fGotMutex)
ReleaseMutex(hLogMutex);
if (INVALID_HANDLE_VALUE != hLogFile)
CloseHandle(hLogFile);
}
#endif