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.
 
 
 
 
 
 

1097 lines
23 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
bridgetm.cpp
Abstract:
Implementations for the bridge terminals.
Author:
Mu Han (muhan) 11/12/99
--*/
#include "stdafx.h"
HRESULT
FindPin(
IN IBaseFilter * pIFilter,
OUT IPin ** ppIPin,
IN PIN_DIRECTION direction,
IN BOOL bFree = TRUE // param not used
)
/*++
Routine Description:
Find a input pin or output pin on a filter.
Arguments:
pIFilter - the filter that has pins.
ppIPin - the place to store the returned interface pointer.
direction - PINDIR_INPUT or PINDIR_OUTPUT.
bFree - look for a free pin or not.
Return Value:
HRESULT
--*/
{
_ASSERTE(ppIPin != NULL);
HRESULT hr;
DWORD dwFeched;
// Get the enumerator of pins on the filter.
CComPtr<IEnumPins> pIEnumPins;
if (FAILED(hr = pIFilter->EnumPins(&pIEnumPins)))
{
BGLOG((BG_ERROR, "enumerate pins on the filter %x", hr));
return hr;
}
IPin * pIPin = NULL;
// Enumerate all the pins and break on the
// first pin that meets requirement.
for (;;)
{
if (pIEnumPins->Next(1, &pIPin, &dwFeched) != S_OK)
{
BGLOG((BG_ERROR, "find pin on filter."));
return E_FAIL;
}
if (0 == dwFeched)
{
BGLOG((BG_ERROR, "get 0 pin from filter."));
return E_FAIL;
}
PIN_DIRECTION dir;
if (FAILED(hr = pIPin->QueryDirection(&dir)))
{
BGLOG((BG_ERROR, "query pin direction. %x", hr));
pIPin->Release();
return hr;
}
if (direction == dir)
{
if (!bFree)
{
break;
}
// Check to see if the pin is free.
CComPtr<IPin> pIPinConnected;
hr = pIPin->ConnectedTo(&pIPinConnected);
if (pIPinConnected == NULL)
{
break;
}
}
pIPin->Release();
}
*ppIPin = pIPin;
return S_OK;
}
CIPConfBaseTerminal::CIPConfBaseTerminal(
const GUID & ClassID,
TERMINAL_DIRECTION TerminalDirection,
TERMINAL_TYPE TerminalType,
DWORD dwMediaType
)
: m_fCritSecValid(FALSE)
, m_TerminalClassID(ClassID)
, m_TerminalDirection(TD_BIDIRECTIONAL)
, m_TerminalType(TerminalType)
, m_TerminalState(TS_NOTINUSE)
, m_dwMediaType(dwMediaType)
, m_pFTM(NULL)
, m_htAddress(NULL)
{
BGLOG((BG_TRACE, "CIPConfBaseTerminal::CIPConfBaseTerminal() called"));
m_szName[0] = TEXT('\0');
}
HRESULT CIPConfBaseTerminal::FinalConstruct()
/*++
Routine Description:
Finish the initialization of the object. If anything fails, this object
will be deleted.
Arguments:
nothing.
Return Value:
S_OK
E_OUTOFMEMORY
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::FinalConstruct");
BGLOG((BG_TRACE, "%s entered", __fxName));
m_fCritSecValid = TRUE;
__try
{
InitializeCriticalSection(&m_CritSec);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
m_fCritSecValid = FALSE;
}
if (!m_fCritSecValid)
{
BGLOG((BG_ERROR, "%s init critical section failed", __fxName));
return E_OUTOFMEMORY;
}
HRESULT hr = CoCreateFreeThreadedMarshaler(
GetControllingUnknown(), &m_pFTM
);
if ( FAILED(hr) )
{
BGLOG((BG_ERROR, "%s create ftm failed, hr=%x", __fxName, hr));
return hr;
}
return S_OK;
}
CIPConfBaseTerminal::~CIPConfBaseTerminal()
/*++
Routine Description:
This is the destructor of the base terminal.
Arguments:
Return Value:
S_OK
--*/
{
if (m_pFTM)
{
m_pFTM->Release();
}
if (m_fCritSecValid)
{
DeleteCriticalSection(&m_CritSec);
}
BGLOG((BG_TRACE,
"CIPConfBaseTerminal::~CIPConfBaseTerminal() for %ws finished", m_szName));
}
HRESULT CIPConfBaseTerminal::Initialize(
IN WCHAR * strName,
IN MSP_HANDLE htAddress,
IN DWORD dwMediaType
)
/*++
Routine Description:
This function sets the name and the address handle on the terminal.
Arguments:
strName - The name of the terminal.
htAddress - The handle that identifies the address object that this
terminal belongs to.
Return Value:
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::Initialize");
BGLOG((BG_TRACE, "%s entered", __fxName));
m_htAddress = htAddress;
lstrcpynW(m_szName, strName, MAX_PATH);
m_dwMediaType = dwMediaType;
BGLOG((BG_TRACE, "%s - exit S_OK", __fxName));
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_Name(
BSTR * pbsName
)
/*++
Routine Description:
This function return the name of the terminal.
Arguments:
pbsName - A pointer to a BSTR to receive the terminal name.
Return Value:
E_POINTER
E_OUTOFMEMORY
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_Name");
if ( IsBadWritePtr( pbsName, sizeof(BSTR) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
*pbsName = SysAllocString(m_szName);
if ( *pbsName == NULL )
{
BGLOG((BG_ERROR, "%s, out of memory for name", __fxName));
return E_OUTOFMEMORY;
}
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_State(
TERMINAL_STATE * pVal
)
/*++
Routine Description:
This function return the state of the terminal.
Arguments:
pVal - A pointer to a variable of type TERMINAL_STATE.
Return Value:
E_POINTER
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_State");
if ( IsBadWritePtr( pVal, sizeof(TERMINAL_STATE) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
*pVal = m_TerminalState;
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_TerminalType(
TERMINAL_TYPE * pVal
)
/*++
Routine Description:
This function return the type of the terminal.
Arguments:
pVal - A pointer to a variable of type TERMINAL_TYPE.
Return Value:
E_POINTER
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_TerminalType");
if ( IsBadWritePtr( pVal, sizeof(TERMINAL_TYPE) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
*pVal = m_TerminalType;
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_TerminalClass(
BSTR * pbsClassID
)
/*++
Routine Description:
This function return the class of the terminal.
Arguments:
pbsClassID - A pointer to a BSTR to receive the classID as a string.
Return Value:
E_POINTER
E_OUTOFMEMORY
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_TerminalClass");
if ( IsBadWritePtr( pbsClassID, sizeof(BSTR) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
// Convert the CLSID to an string.
WCHAR *pszName = NULL;
HRESULT hr = ::StringFromCLSID(m_TerminalClassID, &pszName);
if (FAILED(hr))
{
BGLOG((BG_ERROR, "%s, failed to convert GUID, hr = %x", __fxName, hr));
return hr;
}
// Put the string in a BSTR.
BSTR bClassID = ::SysAllocString(pszName);
// Free the OLE string.
::CoTaskMemFree(pszName);
if (bClassID == NULL)
{
BGLOG((BG_ERROR, "%s, out of mem for class ID", __fxName));
return E_OUTOFMEMORY;
}
*pbsClassID = bClassID;
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_Direction(
OUT TERMINAL_DIRECTION *pDirection
)
/*++
Routine Description:
This function return the direction of the terminal.
Arguments:
pDirection - A pointer to a variable of type TERMINAL_DIRECTION
Return Value:
E_POINTER
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_Direction");
if ( IsBadWritePtr( pDirection, sizeof(TERMINAL_DIRECTION) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
*pDirection = m_TerminalDirection;
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_MediaType(
long * plMediaType
)
/*++
Routine Description:
This function return the media type of the terminal.
Arguments:
plMediaType - A pointer to a variable of type long
Return Value:
E_POINTER
S_OK
--*/
{
ENTER_FUNCTION("CIPConfBaseTerminal::get_MediaType");
if ( IsBadWritePtr(plMediaType, sizeof(long) ) )
{
BGLOG((BG_ERROR, "%s, bad pointer", __fxName));
return E_POINTER;
}
*plMediaType = (long) m_dwMediaType;
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::get_AddressHandle(
OUT MSP_HANDLE * phtAddress
)
/*++
Routine Description:
This function return the handle of the address that created this terminal.
Arguments:
phtAddress - A pointer to a variable of type MSP_HANDLE
Return Value:
E_POINTER
S_OK
--*/
{
// this function is only called from the MSP, so only assert here.
_ASSERT(!IsBadWritePtr(phtAddress, sizeof(MSP_HANDLE)));
*phtAddress = m_htAddress;
return S_OK;
}
STDMETHODIMP
CIPConfBaseTerminal::CompleteConnectTerminal(void)
/*++
Routine Description:
This function is called after a successful ConnectTerminal so that the
terminal can do post-connection intitialization.
Arguments:
nothing
Return Value:
S_OK
--*/
{
return S_OK;
}
STDMETHODIMP CIPConfBaseTerminal::RunRenderFilter(void)
/*++
Routine Description:
start the rightmost render filter in the terminal
(needed for dynamic filter graphs)
Arguments:
Return Value:
E_NOTIMPL
--*/
{
return E_NOTIMPL;
}
STDMETHODIMP CIPConfBaseTerminal::StopRenderFilter(void)
/*++
Routine Description:
stops the rightmost render filter in the terminal
(needed for dynamic filter graphs)
Arguments:
Return Value:
E_NOTIMPL
--*/
{
return E_NOTIMPL;
}
CIPConfBridgeTerminal::CIPConfBridgeTerminal()
: CIPConfBaseTerminal(
__uuidof(IPConfBridgeTerminal),
(TD_CAPTURE),
TT_DYNAMIC,
0)
, m_pUpStreamGraph(NULL)
, m_pSinkFilter(NULL)
, m_pSinkInputPin(NULL)
, m_pDownStreamGraph(NULL)
, m_pSourceFilter(NULL)
, m_pSourceOutputPin(NULL)
{
BGLOG((BG_TRACE, "CIPConfBridgeTerminal::CIPConfBaseTerminal() called"));
}
CIPConfBridgeTerminal::~CIPConfBridgeTerminal()
/*++
Routine Description:
This is the destructor of the bridge terminal.
Arguments:
Return Value:
S_OK
--*/
{
if (m_pUpStreamGraph)
{
m_pUpStreamGraph->Release();
}
if (m_pSinkFilter)
{
m_pSinkFilter->Release();
}
if (m_pSinkInputPin)
{
m_pSinkInputPin->Release();
}
if (m_pDownStreamGraph)
{
m_pDownStreamGraph->Release();
}
if (m_pSourceFilter)
{
m_pSourceFilter->Release();
}
if (m_pSourceOutputPin)
{
m_pSourceOutputPin->Release();
}
BGLOG((BG_TRACE,
"CIPConfBridgeTerminal::~CIPConfBridgeTerminal() for %ws finished", m_szName));
}
HRESULT CIPConfBridgeTerminal::CreateTerminal(
IN DWORD dwMediaType,
IN MSP_HANDLE htAddress,
OUT ITTerminal **ppTerm
)
/*++
Routine Description:
This method creates a bridge terminal
Arguments:
dwMediaType - The media type of this terminal.
htAddress - the handle to the address object.
ppTerm - memory to store the returned terminal pointer.
Return Value:
S_OK
E_POINTER
--*/
{
ENTER_FUNCTION("CIPConfBridgeTerminal::CreateTerminal");
BGLOG((BG_TRACE, "%s, htAddress:%x", __fxName, htAddress));
_ASSERT(!IsBadWritePtr(ppTerm, sizeof(ITTerminal *)));
HRESULT hr;
//
// Create the terminal.
//
CMSPComObject<CIPConfBridgeTerminal> *pTerminal = NULL;
hr = CMSPComObject<CIPConfBridgeTerminal>::CreateInstance(&pTerminal);
if (FAILED(hr))
{
BGLOG((BG_ERROR,
"%s can't create the terminal object hr = %8x", __fxName, hr));
return hr;
}
// query for the ITTerminal interface
ITTerminal *pITTerminal;
hr = pTerminal->_InternalQueryInterface(__uuidof(ITTerminal), (void**)&pITTerminal);
if (FAILED(hr))
{
BGLOG((BG_ERROR,
"%s, query terminal interface failed, %x", __fxName, hr));
delete pTerminal;
return hr;
}
// initialize the terminal
hr = pTerminal->Initialize(
dwMediaType,
htAddress
);
if ( FAILED(hr) )
{
BGLOG((BG_ERROR,
"%s, Initialize failed; returning 0x%08x", __fxName, hr));
pITTerminal->Release();
return hr;
}
BGLOG((BG_TRACE, "%s, Bridge erminal %p created", __fxName, pITTerminal));
*ppTerm = pITTerminal;
return S_OK;
}
// max length of a bridge terminal name
#define MAX_BGTMNAME 80
HRESULT CIPConfBridgeTerminal::Initialize(
IN DWORD dwMediaType,
IN MSP_HANDLE htAddress
)
{
WCHAR pszTerminalName[MAX_BGTMNAME];
int len;
if (dwMediaType == TAPIMEDIATYPE_AUDIO)
{
len = LoadString (
_Module.GetResourceInstance (),
IDS_AUDBGNAME,
pszTerminalName,
MAX_BGTMNAME
);
}
else if (dwMediaType == TAPIMEDIATYPE_VIDEO)
{
len = LoadString (
_Module.GetResourceInstance (),
IDS_VIDBGNAME,
pszTerminalName,
MAX_BGTMNAME
);
}
else
{
LOG ((BG_ERROR, "CIPConfBridgeTerminal::Initialize receives unknown media type %d", dwMediaType));
return E_INVALIDARG;
}
if (len == 0)
{
LOG ((BG_ERROR, "Failed to load bridge terminal name, media %d, err %d",
dwMediaType, GetLastError ()));
return E_UNEXPECTED;
}
return CIPConfBaseTerminal::Initialize(
pszTerminalName, htAddress, dwMediaType
);
}
HRESULT CIPConfBridgeTerminal::CreateFilters()
/*++
Routine Description:
Create the two filters used in the terminal.
Arguments:
Return Value:
HRESULT
--*/
{
ENTER_FUNCTION("CIPConfBridgeTerminal::CreateFilters");
BGLOG((BG_TRACE, "%s entered", __fxName));
HRESULT hr;
// Create the source filter.
CComPtr <IBaseFilter> pSourceFilter;
if (m_dwMediaType == TAPIMEDIATYPE_AUDIO)
{
hr = CTAPIAudioBridgeSourceFilter::CreateInstance(&pSourceFilter);
}
else
{
hr = CTAPIVideoBridgeSourceFilter::CreateInstance(&pSourceFilter);
}
if (FAILED(hr))
{
BGLOG((BG_ERROR, "%s, Create source filter failed. hr=%x", __fxName, hr));
return hr;
}
CComPtr <IDataBridge> pIDataBridge;
hr = pSourceFilter->QueryInterface(&pIDataBridge);
// this should never fail.
_ASSERT(SUCCEEDED(hr));
// Create the sink filter.
CComPtr <IBaseFilter> pSinkFilter;
if (m_dwMediaType == TAPIMEDIATYPE_AUDIO)
{
hr = CTAPIAudioBridgeSinkFilter::CreateInstance(pIDataBridge, &pSinkFilter);
}
else
{
hr = CTAPIVideoBridgeSinkFilter::CreateInstance(pIDataBridge, &pSinkFilter);
}
if (FAILED(hr))
{
BGLOG((BG_ERROR, "%s, Create sink filter failed. hr=%x", __fxName, hr));
return hr;
}
// Find the pins.
CComPtr<IPin> pIPinOutput;
if (FAILED(hr = ::FindPin(pSourceFilter, &pIPinOutput, PINDIR_OUTPUT)))
{
BGLOG((BG_ERROR, "%s, find output pin on sink filter. hr=%x", __fxName, hr));
return hr;
}
CComPtr<IPin> pIPinInput;
if (FAILED(hr = ::FindPin(pSinkFilter, &pIPinInput, PINDIR_INPUT)))
{
BGLOG((BG_ERROR, "%s, find input pin on sink filter. hr=%x", __fxName, hr));
return hr;
}
// save the reference.
m_pSinkFilter = pSinkFilter;
m_pSinkFilter->AddRef();
m_pSinkInputPin = pIPinInput;
m_pSinkInputPin->AddRef();
m_pSourceFilter = pSourceFilter;
m_pSourceFilter->AddRef();
m_pSourceOutputPin = pIPinOutput;
m_pSourceOutputPin->AddRef();
return S_OK;
}
HRESULT CIPConfBridgeTerminal::AddFilter(
IN FILTER_TYPE FilterType,
IN IGraphBuilder * pGraph,
OUT IPin ** ppPins
)
/*++
Routine Description:
Add a filter into the graph provided by the stream and returning the pin
that can be connected at the same time.
Arguments:
FilterType - the type of the filter. Either the source or the sink.
pGraph - The filter graph.
ppPins - A pointer to the buffer that can store the IPin pointer.
Return Value:
S_OK
TAPI_E_TERMINALINUSE - the terminal is in use.
--*/
{
ENTER_FUNCTION("CIPConfBridgeTerminal::AddSourceFilter");
BGLOG((BG_TRACE, "%s entered", __fxName));
// check to see if the terminal is already in use.
if ((FilterType == SINK) && (m_pUpStreamGraph != NULL)
|| (FilterType == SOURCE) && (m_pDownStreamGraph != NULL))
{
BGLOG((BG_ERROR, "%s, terminal already in use", __fxName));
return TAPI_E_TERMINALINUSE;
}
HRESULT hr;
if (m_pSourceFilter == NULL)
{
// the filters have not been created, create them now.
hr = CreateFilters();
if (FAILED(hr))
{
BGLOG((BG_ERROR, "%s, can't Create filter, hr=%x", __fxName, hr));
return hr;
}
}
IBaseFilter *pFilter;
IPin *pPin;
if (FilterType == SINK)
{
pFilter = m_pSinkFilter;
pPin = m_pSinkInputPin;
m_pUpStreamGraph = pGraph;
m_pUpStreamGraph->AddRef();
}
else
{
pFilter = m_pSourceFilter;
pPin = m_pSourceOutputPin;
m_pDownStreamGraph = pGraph;
m_pDownStreamGraph->AddRef();
}
// add the filter to the graph.
hr = pGraph->AddFilter(pFilter, NULL);
if ( FAILED(hr) )
{
BGLOG((BG_ERROR, "%s, can't add filter to the graph hr=%x", __fxName, hr));
return hr;
}
pPin->AddRef();
*ppPins = pPin;
return S_OK;
}
STDMETHODIMP CIPConfBridgeTerminal::ConnectTerminal(
IN IGraphBuilder * pGraph,
IN DWORD dwReserved,
IN OUT DWORD * pdwNumPins,
OUT IPin ** ppPins
)
/*++
Routine Description:
This function is called by the MSP while trying to connect the filter in
the terminal to the rest of the graph in the MSP. It adds the filter into
the graph and returns the pins can be used by the MSP.
Arguments:
pGraph - The filter graph.
dwReserved - the direction for the connection.
pdwNumPins - The maxinum number of pins the msp wants.
ppPins - A pointer to the buffer that can store the IPin pointers. If it
is NULL, only the actual number of pins will be returned.
Return Value:
S_OK
TAPI_E_NOTENOUGHMEMORY - the buffer is too small.
TAPI_E_TERMINALINUSE - the terminal is in use.
--*/
{
ENTER_FUNCTION("CIPConfBridgeTerminal::ConnectTerminal");
BGLOG((BG_TRACE,
"%s entered, pGraph:%p, dwREserved:%p", __fxName, pGraph, dwReserved));
// this function is only called from the MSP, so only assert here.
_ASSERT(!IsBadReadPtr(pGraph, sizeof(IGraphBuilder)));
_ASSERT(!IsBadWritePtr(pdwNumPins, sizeof(DWORD)));
// there is only one pin on each side of the bridge.
const DWORD dwNumOfPins = 1;
//
// If ppPins is NULL, just return the number of pins and don't try to
// connect the terminal.
//
if ( ppPins == NULL )
{
BGLOG((BG_TRACE,
"%s number of exposed pins:%d", __fxName, dwNumOfPins));
*pdwNumPins = dwNumOfPins;
return S_OK;
}
//
// Otherwise, we have a pin return buffer. Check that the purported buffer
// size is big enough and that the buffer is actually writable to the size
// we need.
//
if ( *pdwNumPins < dwNumOfPins )
{
BGLOG((BG_ERROR, "%s not enough space to place pins.", __fxName));
*pdwNumPins = dwNumOfPins;
return TAPI_E_NOTENOUGHMEMORY;
}
_ASSERT(!IsBadWritePtr(ppPins, dwNumOfPins * sizeof(IPin *)));
Lock();
HRESULT hr;
hr = AddFilter((dwReserved == TD_CAPTURE) ? SOURCE : SINK, pGraph, ppPins);
if (FAILED(hr))
{
BGLOG((BG_ERROR, "%s, AddFilter failed", __fxName));
}
else
{
m_TerminalState = TS_INUSE;
*pdwNumPins = 1;
}
Unlock();
BGLOG((BG_TRACE, "CIPConfBridgeTerminal::ConnectTerminal success"));
return hr;
}
STDMETHODIMP
CIPConfBridgeTerminal::DisconnectTerminal(
IN IGraphBuilder * pGraph,
IN DWORD dwReserved
)
/*++
Routine Description:
This function is called by the MSP while trying to disconnect the filter in
the terminal from the rest of the graph in the MSP. It adds the removes the
filter from the graph and set the terminal free.
Arguments:
pGraph - The filter graph. It is used for validation, to make sure the
terminal is disconnected from the same graph that it was
originally connected to.
dwReserved - A reserved dword.
Return Value:
S_OK
E_INVALIDARG - wrong graph.
--*/
{
ENTER_FUNCTION("CIPConfBridgeTerminal::DisconnectTerminal");
BGLOG((BG_TRACE,
"%s entered, pGraph:%p, dwReserved:%d", __fxName, pGraph, dwReserved));
if (pGraph == NULL)
{
BGLOG((BG_TRACE, "%s, bad graph pointer:%p", __fxName, pGraph));
return E_INVALIDARG;
}
Lock();
HRESULT hr;
if (pGraph == m_pUpStreamGraph)
{
hr = pGraph->RemoveFilter(m_pSinkFilter);
m_pUpStreamGraph->Release();
m_pUpStreamGraph = NULL;
}
else if (pGraph == m_pDownStreamGraph)
{
hr = pGraph->RemoveFilter(m_pSourceFilter);
m_pDownStreamGraph->Release();
m_pDownStreamGraph = NULL;
}
else
{
BGLOG((BG_TRACE, "%s, wrong graph pointer:%p", __fxName, pGraph));
Unlock();
return E_INVALIDARG;
}
if ( FAILED(hr) )
{
BGLOG((BG_ERROR,
"%s, remove filter from graph failed; returning hr=%x",
__fxName, hr));
}
m_TerminalState = TS_NOTINUSE;
Unlock();
BGLOG((BG_TRACE, "%s succeeded", __fxName));
return hr;
}