windows-nt/Source/XPSP1/NT/net/upnp/host/upnphost/automationproxy/automationproxy.cpp
2020-09-26 16:20:57 +08:00

3092 lines
94 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: A U T O M A T I O N P R O X Y . C P P
//
// Contents: Implementation of the Automation Proxy class
//
// Notes:
//
// Author: spather 2000/09/25
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include <msxml2.h>
#include "uhbase.h"
#include "AutomationProxy.h"
#include "ncstring.h"
#include "ncxml.h"
#include "ComUtility.h"
#include "uhcommon.h"
CUPnPAutomationProxy::CUPnPAutomationProxy()
{
m_fInitialized = FALSE;
m_pdispService = NULL;
m_cVariables = 0;
m_cEventedVariables = 0;
m_rgVariables = NULL;
m_cActions = 0;
m_rgActions = NULL;
m_wszServiceType = NULL;
}
CUPnPAutomationProxy::~CUPnPAutomationProxy()
{
if (m_pdispService)
{
m_pdispService->Release();
}
FreeVariableTable();
FreeActionTable();
if (m_wszServiceType)
{
delete[] m_wszServiceType;
}
m_fInitialized = FALSE;
}
// ATL methods
HRESULT
CUPnPAutomationProxy::FinalConstruct()
{
return S_OK;
}
HRESULT
CUPnPAutomationProxy::FinalRelease()
{
return S_OK;
}
STDMETHODIMP
CUPnPAutomationProxy::Initialize(
/*[in]*/ IUnknown * punkSvcObject,
/*[in]*/ LPWSTR pszSvcDescription,
/*[in]*/ LPWSTR pszSvcType,
/*[in]*/ BOOL bRunning)
{
HRESULT hr = S_OK;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(!m_fInitialized);
Assert(pszSvcType);
if (punkSvcObject)
{
m_wszServiceType = WszDupWsz(pszSvcType);
if (m_wszServiceType)
{
hr = punkSvcObject->QueryInterface(IID_IDispatch,
(void **) &m_pdispService);
if(SUCCEEDED(hr))
{
if(bRunning)
{
hr = HrCopyProxyIdentity(m_pdispService, punkSvcObject);
}
}
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::Initialize(): "
"Successfully obtained IDispatch pointer on service");
hr = HrProcessServiceDescription(pszSvcDescription);
}
if (SUCCEEDED(hr))
{
m_fInitialized = TRUE;
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::Initialize - Unable to allocate service type!",
hr);
}
}
else
{
hr = E_INVALIDARG;
TraceError("CUPnPAutomationProxy::Initialize - NULL service object!",
hr);
}
Cleanup:
TraceError("CUPnPAutomationProxy::Initialize(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::GetDispIdsOfEventedVariables(
/*[out]*/ DWORD * pcEventedVars,
/*[out]*/ DISPID ** prgdispidEventedVars)
{
HRESULT hr = S_OK;
DISPID * rgdispidEventedVars = NULL;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(m_fInitialized);
if (prgdispidEventedVars && (m_cEventedVariables > 0))
{
rgdispidEventedVars = (DISPID *) CoTaskMemAlloc(m_cEventedVariables *
sizeof(DISPID));
if (rgdispidEventedVars)
{
ZeroMemory(rgdispidEventedVars,
m_cEventedVariables * sizeof(DISPID));
for (DWORD i = 0, j = 0; i < m_cVariables; i++)
{
if (FALSE == m_rgVariables[i].fNonEvented)
{
rgdispidEventedVars[j] = m_rgVariables[i].dispid;
j++;
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::GetDispIdsOfEventedVariables(): "
"Could not allocate array of dispids",
hr);
}
}
if (SUCCEEDED(hr))
{
if (pcEventedVars)
{
*pcEventedVars = m_cEventedVariables;
}
if (prgdispidEventedVars)
{
*prgdispidEventedVars = rgdispidEventedVars;
}
}
Cleanup:
TraceError("CUPnPAutomationProxy::GetDispIdsOfEventedVariables(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::QueryStateVariablesByDispIds(
/*[in]*/ DWORD cDispIds,
/*[in]*/ DISPID * rgDispIds,
/*[out]*/ DWORD * pcVariables,
/*[out]*/ LPWSTR ** prgszVariableNames,
/*[out]*/ VARIANT ** prgvarVariableValues,
/*[out]*/ LPWSTR ** prgszVariableDataTypes)
{
HRESULT hr = S_OK;
DWORD cDispIdsToLookUp;
DISPID * rgDispIdsToLookUp;
LPWSTR * rgszVariableNames = NULL;
VARIANT * rgvarVariableValues = NULL;
LPWSTR * rgszVariableDataTypes = NULL;
DWORD cVariables = 0;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(m_fInitialized);
if (0 == cDispIds)
{
// This means return all variables. Make an array of all our dispids.
cDispIdsToLookUp = m_cVariables;
rgDispIdsToLookUp = new DISPID[cDispIdsToLookUp];
if (rgDispIdsToLookUp)
{
for (DWORD i = 0; i < cDispIdsToLookUp; i++)
{
rgDispIdsToLookUp[i] = m_rgVariables[i].dispid;
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"Could not allocate array of dispids",
hr);
}
}
else
{
cDispIdsToLookUp = cDispIds;
rgDispIdsToLookUp = rgDispIds;
}
if (SUCCEEDED(hr))
{
// Allocate output arrays of size cDispIds.
rgszVariableNames = (LPWSTR *)CoTaskMemAlloc(
cDispIdsToLookUp * sizeof(LPWSTR));
rgvarVariableValues = (VARIANT *)CoTaskMemAlloc(
cDispIdsToLookUp * sizeof(VARIANT));
rgszVariableDataTypes = (LPWSTR *)CoTaskMemAlloc(
cDispIdsToLookUp * sizeof(LPWSTR));
// Fill in values.
if (rgszVariableNames && rgvarVariableValues && rgszVariableDataTypes)
{
ZeroMemory(rgszVariableNames, cDispIdsToLookUp * sizeof(LPWSTR));
ZeroMemory(rgvarVariableValues, cDispIdsToLookUp * sizeof(VARIANT));
ZeroMemory(rgszVariableDataTypes, cDispIdsToLookUp * sizeof(LPWSTR));
for (DWORD i = 0; SUCCEEDED(hr) && (i < cDispIdsToLookUp); i++)
{
UPNP_STATE_VARIABLE * pVariable = NULL;
cVariables++;
pVariable = LookupVariableByDispID(rgDispIdsToLookUp[i]);
if (pVariable)
{
rgszVariableNames[i] = COMSzFromWsz(pVariable->bstrName);
rgszVariableDataTypes[i] = COMSzFromWsz(pVariable->bstrDataType);
if (rgszVariableNames[i] && rgszVariableDataTypes[i])
{
UINT uArgErr = 0;
DISPPARAMS dispparamsEmpty = {NULL, NULL, 0, 0};
hr = m_pdispService->Invoke(rgDispIdsToLookUp[i],
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET,
&dispparamsEmpty,
&rgvarVariableValues[i],
NULL,
&uArgErr);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"Successfully obtained value for "
"dispid %d",
rgDispIdsToLookUp[i]);
}
else
{
TraceError("CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"IDispatch::Invoke failed",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"Could not allocate name/data type strings",
hr);
}
}
else
{
hr = DISP_E_MEMBERNOTFOUND;
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"Could not allocate output arrays",
hr);
}
}
// Copy the output arrays to the out parameters.
if (SUCCEEDED(hr))
{
*pcVariables = cVariables;
*prgszVariableNames = rgszVariableNames;
*prgvarVariableValues = rgvarVariableValues;
*prgszVariableDataTypes = rgszVariableDataTypes;
}
else
{
// Clean up. Assume cVariables accurately describes the number
// of initialized items in the arrays.
if (rgszVariableNames)
{
for (DWORD i = 0; i < cVariables; i++)
{
if (rgszVariableNames[i])
{
CoTaskMemFree(rgszVariableNames[i]);
rgszVariableNames[i] = NULL;
}
}
CoTaskMemFree(rgszVariableNames);
rgszVariableNames = NULL;
}
if (rgvarVariableValues)
{
for (DWORD i = 0; i < cVariables; i++)
{
VariantClear(&rgvarVariableValues[i]);
}
CoTaskMemFree(rgvarVariableValues);
rgvarVariableValues = NULL;
}
if (rgszVariableDataTypes)
{
for (DWORD i = 0; i < cVariables; i++)
{
if (rgszVariableDataTypes[i])
{
CoTaskMemFree(rgszVariableDataTypes[i]);
rgszVariableDataTypes[i] = NULL;
}
}
CoTaskMemFree(rgszVariableDataTypes);
rgszVariableDataTypes = NULL;
}
}
// Clean up custom array of dispIds if we have one.
if (rgDispIdsToLookUp != rgDispIds)
{
delete [] rgDispIdsToLookUp;
rgDispIdsToLookUp = NULL;
cDispIdsToLookUp = 0;
}
Cleanup:
TraceError("CUPnPAutomationProxy::"
"QueryStateVariablesByDispIds(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::ExecuteRequest(
/*[in]*/ UPNP_CONTROL_REQUEST * pucreq,
/*[out]*/ UPNP_CONTROL_RESPONSE * pucresp)
{
HRESULT hr = S_OK;
Assert(m_fInitialized);
if (lstrcmpW(pucreq->bstrActionName, L"QueryStateVariable") == 0)
{
hr = HrQueryStateVariable(pucreq, pucresp);
}
else
{
hr = HrInvokeAction(pucreq, pucresp);
}
TraceError("CUPnPAutomationProxy::ExecuteRequest(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::GetVariableType(
/*[in]*/ LPWSTR pszVarName,
/*[out]*/ BSTR * pbstrType)
{
HRESULT hr = S_OK;
BSTR bstrType = NULL;
UPNP_STATE_VARIABLE * pusv = NULL;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(m_fInitialized);
pusv = LookupVariableByName(pszVarName);
if (pusv)
{
Assert(pusv->bstrDataType);
bstrType = SysAllocString(pusv->bstrDataType);
}
else
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
*pbstrType = bstrType;
}
else
{
if (bstrType)
{
SysFreeString(bstrType);
bstrType = NULL;
}
}
Cleanup:
TraceError("CUPnPAutomationProxy::GetVariableType(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::GetServiceType(
/*[out]*/ LPWSTR * pszServiceType)
{
HRESULT hr = S_OK;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
*pszServiceType = (LPWSTR)CoTaskMemAlloc(CbOfSzAndTerm(m_wszServiceType));
if (*pszServiceType)
{
lstrcpy(*pszServiceType, m_wszServiceType);
}
else
{
hr = E_OUTOFMEMORY;
}
Cleanup:
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::GetInputArgumentNamesAndTypes(
/*[in]*/ LPWSTR pszActionName,
/*[out]*/ DWORD * pcInArguments,
/*[out]*/ BSTR ** prgbstrNames,
/*[out]*/ BSTR ** prgbstrTypes)
{
HRESULT hr = S_OK;
DWORD cInArguments = 0;
BSTR * rgbstrNames = NULL;
BSTR * rgbstrTypes = NULL;
UPNP_ACTION * pua = NULL;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(m_fInitialized);
pua = LookupActionByName(pszActionName);
if (pua)
{
// Allocate arrays for the names and data types.
cInArguments = pua->cInArgs;
if (cInArguments > 0)
{
rgbstrNames = (BSTR *) CoTaskMemAlloc(cInArguments * sizeof(BSTR));
rgbstrTypes = (BSTR *) CoTaskMemAlloc(cInArguments * sizeof(BSTR));
if (rgbstrNames && rgbstrTypes)
{
for (DWORD i = 0; SUCCEEDED(hr) && (i < cInArguments); i++)
{
UPNP_STATE_VARIABLE * pusvRelated = NULL;
rgbstrNames[i] = SysAllocString(pua->rgInArgs[i].bstrName);
pusvRelated = pua->rgInArgs[i].pusvRelated;
Assert(pusvRelated);
rgbstrTypes[i] = SysAllocString(pusvRelated->bstrDataType);
if (rgbstrNames[i] && rgbstrTypes[i])
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"GetInputArgumentNamesAndTypes(): "
"Successfully copied input argument %S "
"of type %S",
rgbstrNames[i],
rgbstrTypes[i]);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"GetInputArgumentNamesAndTypes(): "
"Failed to allocate argument name and/or type",
hr);
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"GetInputArgumentNamesAndTypes(): "
"Failed to allocate output arrays",
hr);
}
}
}
else
{
hr = E_INVALIDARG;
TraceError("CUPnPAutomationProxy::"
"GetIntputArgumentNamesAndTypes(): "
"No such action",
hr);
}
// If successful, copy to the output, otherwise, clean up.
if (SUCCEEDED(hr))
{
*pcInArguments = cInArguments;
*prgbstrNames = rgbstrNames;
*prgbstrTypes = rgbstrTypes;
}
else
{
if (rgbstrNames)
{
for (DWORD i = 0; i < cInArguments; i++)
{
if (rgbstrNames[i])
{
SysFreeString(rgbstrNames[i]);
rgbstrNames[i] = NULL;
}
}
CoTaskMemFree(rgbstrNames);
rgbstrNames = NULL;
}
if (rgbstrTypes)
{
for (DWORD i = 0; i < cInArguments; i++)
{
if (rgbstrTypes[i])
{
SysFreeString(rgbstrTypes[i]);
rgbstrTypes[i] = NULL;
}
}
CoTaskMemFree(rgbstrTypes);
rgbstrTypes = NULL;
}
cInArguments = 0;
}
Cleanup:
TraceError("CUPnPAutomationProxy::GetInputArgumentNamesAndTypes(): "
"Exiting",
hr);
return hr;
}
STDMETHODIMP
CUPnPAutomationProxy::GetOutputArgumentNamesAndTypes(
/*[in]*/ LPWSTR pszActionName,
/*[out]*/ DWORD * pcOutArguments,
/*[out]*/ BSTR ** prgbstrNames,
/*[out]*/ BSTR ** prgbstrTypes)
{
HRESULT hr = S_OK;
DWORD cOutArguments = 0;
BSTR * rgbstrNames = NULL;
BSTR * rgbstrTypes = NULL;
UPNP_ACTION * pua = NULL;
hr = HrIsAllowedCOMCallLocality((CALL_LOCALITY) CALL_LOCALITY_INPROC );
if (FAILED(hr))
{
TraceError("HrIsAllowedCOMCallLocality failed !",hr);
goto Cleanup;
}
Assert(m_fInitialized);
pua = LookupActionByName(pszActionName);
if (pua)
{
// Allocate arrays for the names and data types.
cOutArguments = pua->cOutArgs;
if (cOutArguments > 0)
{
rgbstrNames = (BSTR *) CoTaskMemAlloc(cOutArguments * sizeof(BSTR));
rgbstrTypes = (BSTR *) CoTaskMemAlloc(cOutArguments * sizeof(BSTR));
if (rgbstrNames && rgbstrTypes)
{
for (DWORD i = 0; SUCCEEDED(hr) && (i < cOutArguments); i++)
{
UPNP_STATE_VARIABLE * pusvRelated = NULL;
rgbstrNames[i] = SysAllocString(pua->rgOutArgs[i].bstrName);
pusvRelated = pua->rgOutArgs[i].pusvRelated;
Assert(pusvRelated);
rgbstrTypes[i] = SysAllocString(pusvRelated->bstrDataType);
if (rgbstrNames[i] && rgbstrTypes[i])
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"GetOutputArgumentNamesAndTypes(): "
"Successfully copied output argument %S "
"of type %S",
rgbstrNames[i],
rgbstrTypes[i]);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"GetOutputArgumentNamesAndTypes(): "
"Failed to allocate argument name and/or type",
hr);
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"GetOutputArgumentNamesAndTypes(): "
"Failed to allocate output arrays",
hr);
}
}
}
else
{
hr = E_INVALIDARG;
TraceError("CUPnPAutomationProxy::"
"GetOutputArgumentNamesAndTypes(): "
"No such action",
hr);
}
// If successful, copy to the output, otherwise, clean up.
if (SUCCEEDED(hr))
{
*pcOutArguments = cOutArguments;
*prgbstrNames = rgbstrNames;
*prgbstrTypes = rgbstrTypes;
}
else
{
if (rgbstrNames)
{
for (DWORD i = 0; i < cOutArguments; i++)
{
if (rgbstrNames[i])
{
SysFreeString(rgbstrNames[i]);
rgbstrNames[i] = NULL;
}
}
CoTaskMemFree(rgbstrNames);
rgbstrNames = NULL;
}
if (rgbstrTypes)
{
for (DWORD i = 0; i < cOutArguments; i++)
{
if (rgbstrTypes[i])
{
SysFreeString(rgbstrTypes[i]);
rgbstrTypes[i] = NULL;
}
}
CoTaskMemFree(rgbstrTypes);
rgbstrTypes = NULL;
}
cOutArguments = 0;
}
TraceError("CUPnPAutomationProxy::GetOutputArgumentNamesAndTypes(): "
"Exiting",
hr);
Cleanup:
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPAutomationProxy::FreeVariable
//
// Purpose: Frees resources used by a state variable structure
//
// Arguments:
// pVariable [in] Address of the structure to free
//
// Returns:
// (none)
//
// Author: spather 2000/09/26
//
// Notes:
// This frees only the memory used by the fields of the structure, not
// the structure itself.
//
VOID
CUPnPAutomationProxy::FreeVariable(UPNP_STATE_VARIABLE * pVariable)
{
if (pVariable->bstrName)
{
SysFreeString(pVariable->bstrName);
pVariable->bstrName = NULL;
}
if (pVariable->bstrDataType)
{
SysFreeString(pVariable->bstrDataType);
pVariable->bstrDataType = NULL;
}
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPAutomationProxy::FreeAction
//
// Purpose: Frees resources used by an action structure
//
// Arguments:
// pAction [in] Address of the structure to free
//
// Returns:
// (none)
//
// Author: spather 2000/09/26
//
// Notes:
// This frees only the memory used by the fields of the structure, not
// the structure itself.
//
VOID
CUPnPAutomationProxy::FreeAction(UPNP_ACTION * pAction)
{
if (pAction->bstrName)
{
SysFreeString(pAction->bstrName);
pAction->bstrName = NULL;
}
if (pAction->rgInArgs)
{
for (DWORD i = 0; i < pAction->cInArgs; i++)
{
FreeArgument(&pAction->rgInArgs[i]);
}
delete [] pAction->rgInArgs;
pAction->rgInArgs = NULL;
}
pAction->cInArgs = 0;
if (pAction->rgOutArgs)
{
for (DWORD i = 0; i < pAction->cOutArgs; i++)
{
FreeArgument(&pAction->rgOutArgs[i]);
}
delete [] pAction->rgOutArgs;
pAction->rgOutArgs = NULL;
}
pAction->cOutArgs = 0;
pAction->puaRetVal = NULL;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPAutomationProxy::FreeArgument
//
// Purpose: Frees resources used by an argument structure
//
// Arguments:
// pArg [in] Address of the structure to free
//
// Returns:
// (none)
//
// Author: spather 2000/09/26
//
// Notes:
// This frees only the memory used by the fields of the structure, not
// the structure itself.
//
VOID
CUPnPAutomationProxy::FreeArgument(UPNP_ARGUMENT * pArg)
{
if (pArg->bstrName)
{
SysFreeString(pArg->bstrName);
pArg->bstrName = NULL;
}
pArg->pusvRelated = NULL;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPAutomationProxy::FreeVariableTable
//
// Purpose: Frees the Automation Proxy object's variable table.
//
// Arguments:
// (none)
//
// Returns:
// (none)
//
// Author: spather 2000/09/26
//
// Notes:
//
VOID
CUPnPAutomationProxy::FreeVariableTable()
{
for (DWORD i = 0; i < m_cVariables; i++)
{
FreeVariable(&m_rgVariables[i]);
}
delete [] m_rgVariables;
m_rgVariables = NULL;
m_cVariables = 0;
m_cEventedVariables = 0;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPAutomationProxy::FreeVariableTable
//
// Purpose: Frees the Automation Proxy object's action table.
//
// Arguments:
// (none)
//
// Returns:
// (none)
//
// Author: spather 2000/09/26
//
// Notes:
//
VOID
CUPnPAutomationProxy::FreeActionTable()
{
for (DWORD i = 0; i < m_cActions; i++)
{
FreeAction(&m_rgActions[i]);
}
delete [] m_rgActions;
m_rgActions = NULL;
m_cActions = 0;
}
VOID
CUPnPAutomationProxy::FreeControlResponse(
UPNP_CONTROL_RESPONSE * pucresp)
{
if (pucresp->bstrActionName)
{
SysFreeString(pucresp->bstrActionName);
pucresp->bstrActionName = NULL;
}
UPNP_CONTROL_RESPONSE_DATA * pucrd = &pucresp->ucrData;
if (pucresp->fSucceeded)
{
if (pucrd->Success.rgvarOutputArgs)
{
for (DWORD i = 0; i < pucrd->Success.cOutputArgs; i++)
{
VariantClear(&pucrd->Success.rgvarOutputArgs[i]);
}
CoTaskMemFree(pucrd->Success.rgvarOutputArgs);
pucrd->Success.rgvarOutputArgs = NULL;
pucrd->Success.cOutputArgs = 0;
}
}
else
{
if (pucrd->Fault.bstrFaultCode)
{
SysFreeString(pucrd->Fault.bstrFaultCode);
pucrd->Fault.bstrFaultCode = NULL;
}
if (pucrd->Fault.bstrFaultString)
{
SysFreeString(pucrd->Fault.bstrFaultString);
pucrd->Fault.bstrFaultString = NULL;
}
if (pucrd->Fault.bstrUPnPErrorCode)
{
SysFreeString(pucrd->Fault.bstrUPnPErrorCode);
pucrd->Fault.bstrUPnPErrorCode = NULL;
}
if (pucrd->Fault.bstrUPnPErrorString)
{
SysFreeString(pucrd->Fault.bstrUPnPErrorString);
pucrd->Fault.bstrUPnPErrorString = NULL;
}
}
}
HRESULT
CUPnPAutomationProxy::HrProcessServiceDescription(
IN LPWSTR pszSvcDescription)
{
HRESULT hr = S_OK;
IXMLDOMDocument * pxddSvcDesc = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Processing service description:\n"
"%S",
pszSvcDescription);
hr = CoCreateInstance(CLSID_DOMDocument30,
NULL,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument,
(void **) &pxddSvcDesc);
if (SUCCEEDED(hr))
{
BSTR bstrSvcDesc = NULL;
Assert(pxddSvcDesc);
bstrSvcDesc = SysAllocString(pszSvcDescription);
if (bstrSvcDesc)
{
hr = pxddSvcDesc->put_async(VARIANT_FALSE);
if (SUCCEEDED(hr))
{
VARIANT_BOOL vbSuccess = VARIANT_FALSE;
pxddSvcDesc->put_resolveExternals(VARIANT_FALSE);
hr = pxddSvcDesc->loadXML(bstrSvcDesc, &vbSuccess);
if (SUCCEEDED(hr) && (VARIANT_TRUE == vbSuccess))
{
IXMLDOMElement * pxdeRoot = NULL;
hr = pxddSvcDesc->get_documentElement(&pxdeRoot);
if (S_OK == hr)
{
Assert(pxdeRoot);
hr = HrValidateServiceDescription(pxdeRoot);
if (SUCCEEDED(hr))
{
hr = HrBuildTablesFromServiceDescription(pxdeRoot);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Successfully built tables from "
"service description",
hr);
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Could not build tables from "
"service description",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Could not validate service "
"description",
hr);
}
pxdeRoot->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Could not get document element",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Failed to load XML",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrProcessServiceDescription(): "
"Could not set async property",
hr);
}
SysFreeString(bstrSvcDesc);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrProcessServiceDescription(): "
"Could not allocate BSTR service description",
hr);
}
pxddSvcDesc->Release();
}
else
{
TraceError("CUPnPAutomationProxy::HrProcessServiceDescription(): "
"Could not create DOM document",
hr);
}
TraceError("CUPnPAutomationProxy::HrProcessServiceDescription(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrValidateServiceDescription(
IXMLDOMElement * pxdeRoot)
{
return S_OK;
}
HRESULT
CUPnPAutomationProxy::HrBuildTablesFromServiceDescription(
IXMLDOMElement * pxdeRoot)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnRoot = NULL;
hr = pxdeRoot->QueryInterface(IID_IXMLDOMNode,
(void **) &pxdnRoot);
if (SUCCEEDED(hr))
{
IXMLDOMNode * pxdnSST = NULL;
LPCWSTR arypszTokens[] = {L"serviceStateTable"};
Assert(pxdnRoot);
// Get the <serviceStateTable> element and use it's children to
// build the variable table.
hr = HrGetNestedChildElement(pxdnRoot,
arypszTokens,
1,
&pxdnSST);
if (SUCCEEDED(hr))
{
IXMLDOMNodeList * pxdnlStateVars = NULL;
Assert(pxdnSST);
// Get the list of <stateVariable> nodes.
hr = pxdnSST->get_childNodes(&pxdnlStateVars);
if (SUCCEEDED(hr))
{
hr = HrBuildVariableTable(pxdnlStateVars);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Successfully built variable table");
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to build variable table",
hr);
}
pxdnlStateVars->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to get <stateVariable> elements",
hr);
}
pxdnSST->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to get <serviceStateTable> element",
hr);
}
// If the above succeeded, we'll now build the action
// table.
if (SUCCEEDED(hr))
{
IXMLDOMNode * pxdnActionList = NULL;
LPCWSTR arypszALTokens[] = {L"actionList"};
Assert(pxdnRoot);
// Get the <actionList> element and use it's children to
// build the action table.
hr = HrGetNestedChildElement(pxdnRoot,
arypszALTokens,
1,
&pxdnActionList);
if (SUCCEEDED(hr) && hr != S_FALSE)
{
IXMLDOMNodeList * pxdnlActions = NULL;
Assert(pxdnActionList);
// Get the list of <action> nodes.
hr = pxdnActionList->get_childNodes(&pxdnlActions);
if (SUCCEEDED(hr))
{
hr = HrBuildActionTable(pxdnlActions);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Successfully built action table");
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to build action table",
hr);
}
pxdnlActions->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to get <action> elements",
hr);
}
pxdnActionList->Release();
}
else
{
TraceErrorOptional("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Failed to get <actionList> element",
hr, (S_FALSE == hr));
}
}
pxdnRoot->Release();
}
TraceError("CUPnPAutomationProxy::"
"HrBuildTablesFromServiceDescription(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrBuildVariableTable(
IXMLDOMNodeList * pxdnlStateVars)
{
HRESULT hr = S_OK;
LONG listLength = 0;
hr = pxdnlStateVars->get_length(&listLength);
if (SUCCEEDED(hr))
{
Assert(listLength > 0);
m_rgVariables = new UPNP_STATE_VARIABLE[listLength];
if (m_rgVariables)
{
ZeroMemory(m_rgVariables,
listLength * sizeof(UPNP_STATE_VARIABLE));
m_cVariables = 0;
m_cEventedVariables = 0;
for (long i = 0; SUCCEEDED(hr) && (i < listLength); i++)
{
IXMLDOMNode * pxdnStateVar = NULL;
hr = pxdnlStateVars->get_item(i, &pxdnStateVar);
if (SUCCEEDED(hr))
{
LPCWSTR rgszNameTokens[] = {L"name"};
Assert(pxdnStateVar);
// Get the "name" and "dataType" values.
hr = HrGetTextValueFromChildElement(pxdnStateVar,
rgszNameTokens,
1,
&(m_rgVariables[i].bstrName));
if (SUCCEEDED(hr))
{
LPCWSTR rgszDataTypeTokens[] = {L"dataType"};
hr = HrGetTextValueFromChildElement(pxdnStateVar,
rgszDataTypeTokens,
1,
&(m_rgVariables[i].bstrDataType));
if (SUCCEEDED(hr))
{
BSTR bstrSendEvents = NULL;
TraceTag(ttidAutomationProxy,
"HrBuildVariableTable(): "
"Variable name %S and data type %S",
m_rgVariables[i].bstrName,
m_rgVariables[i].bstrDataType);
hr = HrGetTextValueFromAttribute(pxdnStateVar,
L"sendEvents",
&bstrSendEvents);
if (SUCCEEDED(hr))
{
if (NULL == bstrSendEvents)
{
hr = S_OK;
m_rgVariables[i].fNonEvented = FALSE;
m_cEventedVariables++;
TraceTag(ttidAutomationProxy,
"HrBuildVariableTable(): "
"Variable %S did not have a "
"sendEvents attribute - treating "
"it as evented",
m_rgVariables[i].bstrName);
}
else
{
if (0 == lstrcmpW(bstrSendEvents, L"yes"))
{
m_rgVariables[i].fNonEvented = FALSE;
m_cEventedVariables++;
TraceTag(ttidAutomationProxy,
"HrBuildVariableTable(): "
"Variable %S is evented ",
m_rgVariables[i].bstrName);
}
else if (0 == lstrcmpW(bstrSendEvents, L"no"))
{
m_rgVariables[i].fNonEvented = TRUE;
TraceTag(ttidAutomationProxy,
"HrBuildVariableTable(): "
"Variable %S is non-evented ",
m_rgVariables[i].bstrName);
}
SysFreeString(bstrSendEvents);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get sendEvents attribute",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get variable data type",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get variable name",
hr);
}
pxdnStateVar->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get list item",
hr);
}
if (SUCCEEDED(hr))
{
m_cVariables++;
}
else
{
FreeVariable(&m_rgVariables[i]);
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to allocate variable table",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get list length",
hr);
}
// Got the names and data types, now just need to get the DISPIDs.
if (SUCCEEDED(hr))
{
for (DWORD i = 0; SUCCEEDED(hr) && (i < m_cVariables); i++)
{
hr = m_pdispService->GetIDsOfNames(IID_NULL,
&m_rgVariables[i].bstrName,
1,
LOCALE_SYSTEM_DEFAULT,
&m_rgVariables[i].dispid);
if (SUCCEEDED(hr))
{
Assert(DISPID_UNKNOWN != m_rgVariables[i].dispid);
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Variable %S has dispID %d",
m_rgVariables[i].bstrName,
m_rgVariables[i].dispid);
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Failed to get dispId",
hr);
}
}
}
TraceError("CUPnPAutomationProxy::"
"HrBuildVariableTable(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrBuildActionTable(
IXMLDOMNodeList * pxdnlActions)
{
HRESULT hr = S_OK;
LONG listLength = 0;
hr = pxdnlActions->get_length(&listLength);
if (SUCCEEDED(hr))
{
Assert(listLength > 0);
m_rgActions = new UPNP_ACTION[listLength];
if (m_rgActions)
{
ZeroMemory(m_rgActions,
listLength * sizeof(UPNP_ACTION));
m_cActions = 0;
for (long i = 0; SUCCEEDED(hr) && (i < listLength); i++)
{
IXMLDOMNode * pxdnAction = NULL;
hr = pxdnlActions->get_item(i, &pxdnAction);
if (SUCCEEDED(hr))
{
LPCWSTR rgszNameTokens[] = {L"name"};
Assert(pxdnAction);
// Get the "name" value.
hr = HrGetTextValueFromChildElement(pxdnAction,
rgszNameTokens,
1,
&(m_rgActions[i].bstrName));
if (SUCCEEDED(hr))
{
// Initialize arguments.
hr = HrBuildArgumentLists(pxdnAction,
&m_rgActions[i]);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"HrBuildActionTable(): "
"Action %S initialized",
m_rgActions[i].bstrName);
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to build argument lists",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to get action name",
hr);
}
pxdnAction->Release();
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to get list item",
hr);
}
if (SUCCEEDED(hr))
{
m_cActions++;
}
else
{
FreeAction(&m_rgActions[i]);
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to allocate action table",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to get list length",
hr);
}
// Got the names and arguments, now just need to get the DISPIDs.
if (SUCCEEDED(hr))
{
for (DWORD i = 0; SUCCEEDED(hr) && (i < m_cActions); i++)
{
hr = m_pdispService->GetIDsOfNames(IID_NULL,
&m_rgActions[i].bstrName,
1,
LOCALE_SYSTEM_DEFAULT,
&m_rgActions[i].dispid);
if (SUCCEEDED(hr))
{
Assert(DISPID_UNKNOWN != m_rgActions[i].dispid);
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Action %S has dispID %d",
m_rgActions[i].bstrName,
m_rgActions[i].dispid);
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildActionTable(): "
"Failed to get dispId",
hr);
}
}
}
TraceError("CUPnPAutomationProxy::HrBuildActionTable(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrBuildArgumentLists(
IXMLDOMNode * pxdnAction,
UPNP_ACTION * pAction)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnArgList = NULL;
LPCWSTR arypszTokens[] = {L"argumentList"};
Assert(pxdnAction);
Assert(pAction);
hr = HrGetNestedChildElement(pxdnAction, arypszTokens, 1, &pxdnArgList);
if (SUCCEEDED(hr))
{
if (pxdnArgList)
{
IXMLDOMNodeList * pxdnlArgs = NULL;
hr = pxdnArgList->get_childNodes(&pxdnlArgs);
if (SUCCEEDED(hr))
{
DWORD cInArgs = 0;
DWORD cOutArgs = 0;
hr = HrCountInAndOutArgs(pxdnlArgs, &cInArgs, &cOutArgs);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrBuildArgumentLists(): "
"Action %S has %d input arguments and "
"%d output arguments",
pAction->bstrName,
cInArgs,
cOutArgs);
// Allocate memory for the argument lists.
if (cInArgs > 0)
{
pAction->rgInArgs = new UPNP_ARGUMENT[cInArgs];
if (pAction->rgInArgs)
{
pAction->cInArgs = cInArgs;
ZeroMemory(pAction->rgInArgs,
cInArgs * sizeof(UPNP_ARGUMENT));
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"HrBuildArgumentLists(): "
"Failed to allocate memory for input "
"arguments",
hr);
}
}
else
{
pAction->cInArgs = 0;
pAction->rgInArgs = NULL;
}
if (SUCCEEDED(hr))
{
if (cOutArgs > 0)
{
pAction->rgOutArgs = new UPNP_ARGUMENT[cOutArgs];
if (pAction->rgOutArgs)
{
pAction->cOutArgs = cOutArgs;
ZeroMemory(pAction->rgOutArgs,
cOutArgs * sizeof(UPNP_ARGUMENT));
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::"
"HrBuildArgumentLists(): "
"Failed to allocate memory for out "
"arguments",
hr);
}
}
else
{
pAction->cOutArgs = 0;
pAction->rgOutArgs = NULL;
}
}
if (SUCCEEDED(hr))
{
hr = HrInitializeArguments(pxdnlArgs,
pAction);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrBuildArgumentLists(): "
"Successfully initialized arguments");
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrBuildArgumentLists(): "
"Failed to initialize arguments",
hr);
}
}
// If anything above failed, pAction structure will
// be cleaned up on return from this function.
}
pxdnlArgs->Release();
}
else
{
TraceError("CUPnPAutomationProxy::HrBuildArgumentLists(): "
"Failed to get <argumentList> children",
hr);
}
pxdnArgList->Release();
}
else
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrBuildArgumentLists(): "
"Action %S does not have any arguments",
pAction->bstrName);
pAction->cInArgs = 0;
pAction->rgInArgs = NULL;
pAction->cOutArgs = 0;
pAction->rgOutArgs = NULL;
pAction->puaRetVal = NULL;
// Fix up the return value.
hr = S_OK;
}
}
else
{
TraceError("CUPnPAutomationProxy::HrBuildArgumentLists(): "
"Failed to get <argumentList> element",
hr);
}
TraceHr(ttidError, FAL, hr, (hr == S_FALSE),
"CUPnPAutomationProxy::HrBuildArgumentLists");
return hr;
}
HRESULT
CUPnPAutomationProxy::HrCountInAndOutArgs(
IXMLDOMNodeList * pxdnlArgs,
DWORD * pcInArgs,
DWORD * pcOutArgs)
{
HRESULT hr = S_OK;
LONG listLength = 0;
DWORD cInArgs = 0;
DWORD cOutArgs = 0;
Assert(pxdnlArgs);
Assert(pcInArgs);
Assert(pcOutArgs);
hr = pxdnlArgs->get_length(&listLength);
if (SUCCEEDED(hr))
{
Assert(listLength > 0);
// Loop through the list of <argument> elements and read each one's
// <direction> element.
for (LONG i = 0; SUCCEEDED(hr) && (i < listLength); i++)
{
IXMLDOMNode * pxdnArg = NULL;
hr = pxdnlArgs->get_item(i, &pxdnArg);
if (SUCCEEDED(hr))
{
LPCWSTR arypszTokens[] = {L"direction"};
BSTR bstrDirection = NULL;
hr = HrGetTextValueFromChildElement(pxdnArg,
arypszTokens,
1,
&bstrDirection);
if (SUCCEEDED(hr) && hr != S_FALSE)
{
if (lstrcmpW(bstrDirection, L"in") == 0)
{
cInArgs++;
}
else if (lstrcmpW(bstrDirection, L"out") == 0)
{
cOutArgs++;
}
else
{
// Document has already been validated - <direction>
// should contain either "in" or "out". Should never
// be here.
AssertSz(FALSE,
"Validated direction element contained"
"invalid value");
}
SysFreeString(bstrDirection);
}
else
{
TraceError("CUPnPAutomationProxy::HrCountInAndOutArgs(): "
"Failed to get <direction> value",
hr);
}
pxdnArg->Release();
}
else
{
TraceError("CUPnPAutomationProxy::HrCountInAndOutArgs(): "
"Failed to get list item",
hr);
}
}
}
else
{
TraceError("CUPnPAutomationProxy::HrCountInAndOutArgs(): "
"Failed to get list length",
hr);
}
// If everything succeeded, return counts through out parameters.
if (SUCCEEDED(hr))
{
*pcInArgs = cInArgs;
*pcOutArgs = cOutArgs;
}
TraceError("CUPnPAutomationProxy::HrCountInAndOutArgs(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrInitializeArguments(
IXMLDOMNodeList * pxdnlArgs,
UPNP_ACTION * pAction)
{
HRESULT hr = S_OK;
Assert(pxdnlArgs);
Assert(pAction);
// There should either be some input or some output arguments.
// For both input and output arguments, if there are any, an
// array should be allocated for them.
Assert(pAction->cInArgs || pAction->cOutArgs);
Assert(FImplies(pAction->cInArgs, pAction->rgInArgs));
Assert(FImplies(pAction->cOutArgs, pAction->rgOutArgs));
// In arguments must be declared before out arguments, so we can assume
// they are at the front of the list.
for (DWORD i = 0; SUCCEEDED(hr) && (i < pAction->cInArgs); i++)
{
LONG lIndex = (LONG) i;
IXMLDOMNode * pxdnArg = NULL;
hr = pxdnlArgs->get_item(lIndex, &pxdnArg);
if (SUCCEEDED(hr))
{
UPNP_ARGUMENT * puaCurrent = &pAction->rgInArgs[i];
LPCWSTR arypszNameTokens[] = {L"name"};
Assert(pxdnArg);
hr = HrGetTextValueFromChildElement(pxdnArg,
arypszNameTokens,
1,
&puaCurrent->bstrName);
if (SUCCEEDED(hr))
{
LPCWSTR arypszRSVTokens[] = {L"relatedStateVariable"};
BSTR bstrRelStateVar = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInitializeArguments(): "
"Initializing argument %S",
puaCurrent->bstrName);
hr = HrGetTextValueFromChildElement(pxdnArg,
arypszRSVTokens,
1,
&bstrRelStateVar);
if (SUCCEEDED(hr))
{
UPNP_STATE_VARIABLE * pusvRelated = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInitializeArguments(): "
"Argument %S is related to state variable %S",
puaCurrent->bstrName,
bstrRelStateVar);
pusvRelated = LookupVariableByName(bstrRelStateVar);
if (pusvRelated)
{
puaCurrent->pusvRelated = pusvRelated;
}
else
{
puaCurrent->pusvRelated = NULL;
hr = E_INVALIDARG;
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to find related state variable",
hr);
}
SysFreeString(bstrRelStateVar);
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get <relatedStateVariable> value",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get <name> value",
hr);
}
pxdnArg->Release();
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get list item",
hr);
}
}
// Now get the out arguments.
for (DWORD i = 0; SUCCEEDED(hr) && (i < pAction->cOutArgs); i++)
{
LONG lIndex = (LONG) (pAction->cInArgs + i);
IXMLDOMNode * pxdnArg = NULL;
hr = pxdnlArgs->get_item(lIndex, &pxdnArg);
if (SUCCEEDED(hr))
{
UPNP_ARGUMENT * puaCurrent = &pAction->rgOutArgs[i];
LPCWSTR arypszNameTokens[] = {L"name"};
Assert(pxdnArg);
hr = HrGetTextValueFromChildElement(pxdnArg,
arypszNameTokens,
1,
&puaCurrent->bstrName);
if (SUCCEEDED(hr))
{
LPCWSTR arypszRSVTokens[] = {L"relatedStateVariable"};
BSTR bstrRelStateVar = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInitializeArguments(): "
"Initializing argument %S",
puaCurrent->bstrName);
hr = HrGetTextValueFromChildElement(pxdnArg,
arypszRSVTokens,
1,
&bstrRelStateVar);
if (SUCCEEDED(hr))
{
UPNP_STATE_VARIABLE * pusvRelated = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInitializeArguments(): "
"Argument %S is related to state variable %S",
puaCurrent->bstrName,
bstrRelStateVar);
pusvRelated = LookupVariableByName(bstrRelStateVar);
if (pusvRelated)
{
LPCWSTR arypszRetvalTokens[] = {L"retval"};
IXMLDOMNode * pxdnRetVal = NULL;
puaCurrent->pusvRelated = pusvRelated;
hr = HrGetNestedChildElement(pxdnArg,
arypszRetvalTokens,
1,
&pxdnRetVal);
if (SUCCEEDED(hr))
{
if (pxdnRetVal)
{
// This is the return value.
pAction->puaRetVal = puaCurrent;
}
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrInitializeArguments(): "
"Failed get retval element",
hr);
}
}
else
{
puaCurrent->pusvRelated = NULL;
hr = E_INVALIDARG;
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to find related state variable",
hr);
}
SysFreeString(bstrRelStateVar);
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get <relatedStateVariable> value",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get <name> value",
hr);
}
pxdnArg->Release();
}
else
{
TraceError("CUPnPAutomationProxy::HrInitializeArguments(): "
"Failed to get list item",
hr);
}
}
TraceHr(ttidError, FAL, hr, (hr == S_FALSE),
"CUPnPAutomationProxy::HrInitializeArguments");
return hr;
}
UPNP_STATE_VARIABLE *
CUPnPAutomationProxy::LookupVariableByDispID(DISPID dispid)
{
UPNP_STATE_VARIABLE * pusv = NULL;
for (DWORD i = 0; i < m_cVariables; i++)
{
if (m_rgVariables[i].dispid == dispid)
{
pusv = &m_rgVariables[i];
break;
}
}
if (pusv)
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupVariableByDispID(): "
"DISPID %d corresponds to variable %S",
pusv->dispid,
pusv->bstrName);
}
else
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupVariableByDispID(): "
"DISPID %d does not match any variable",
dispid);
}
return pusv;
}
UPNP_STATE_VARIABLE *
CUPnPAutomationProxy::LookupVariableByName(LPCWSTR pcszName)
{
UPNP_STATE_VARIABLE * pusv = NULL;
for (DWORD i = 0; i < m_cVariables; i++)
{
if (lstrcmpiW(m_rgVariables[i].bstrName, pcszName) == 0)
{
pusv = &m_rgVariables[i];
break;
}
}
if (pusv)
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupVariableByName(): "
"Found %S in variable table",
pusv->bstrName);
}
else
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupVariableByName(): "
"%S does not match any variable in variable table",
pcszName);
}
return pusv;
}
UPNP_ACTION *
CUPnPAutomationProxy::LookupActionByName(LPCWSTR pcszName)
{
UPNP_ACTION * pua = NULL;
for (DWORD i = 0; i < m_cActions; i++)
{
if (lstrcmpiW(m_rgActions[i].bstrName, pcszName) == 0)
{
pua = &m_rgActions[i];
break;
}
}
if (pua)
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupActionByName(): "
"Found %S in action table",
pua->bstrName);
}
else
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::LookupActionByName(): "
"%S does not match any action in action table",
pcszName);
}
return pua;
}
HRESULT
CUPnPAutomationProxy::HrBuildFaultResponse(
UPNP_CONTROL_RESPONSE_DATA * pucrd,
LPCWSTR pcszFaultCode,
LPCWSTR pcszFaultString,
LPCWSTR pcszUPnPErrorCode,
LPCWSTR pcszUPnPErrorString)
{
HRESULT hr = S_OK;
pucrd->Fault.bstrFaultCode = SysAllocString(pcszFaultCode);
if (pucrd->Fault.bstrFaultCode)
{
pucrd->Fault.bstrFaultString = SysAllocString(pcszFaultString);
if (pucrd->Fault.bstrFaultString)
{
pucrd->Fault.bstrUPnPErrorCode = SysAllocString(pcszUPnPErrorCode);
if (pucrd->Fault.bstrUPnPErrorCode)
{
pucrd->Fault.bstrUPnPErrorString = SysAllocString(pcszUPnPErrorString);
if (pucrd->Fault.bstrUPnPErrorString)
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Successfully built fault response: \n"
"\tFaultCode: %S\n"
"\tFaultString: %S\n"
"\tUPnPErrorCode: %S\n"
"\tUPnPErrorString: %S",
pucrd->Fault.bstrFaultCode,
pucrd->Fault.bstrFaultString,
pucrd->Fault.bstrUPnPErrorCode,
pucrd->Fault.bstrUPnPErrorString);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Failed to allocate UPnP error string",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Failed to allocate UPnP Error code string",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Failed to allocate fault string",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Failed to allocate fault code string",
hr);
}
TraceError("CUPnPAutomationProxy::HrBuildFaultResponse(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrVariantInitForXMLType(VARIANT * pvar,
LPCWSTR pcszDataTypeString)
{
HRESULT hr = S_OK;
VARTYPE vt = VT_ERROR;
VARIANT var;
VariantInit(&var);
vt = GetVarTypeFromString(pcszDataTypeString);
if (VT_EMPTY != vt)
{
var.vt = vt;
switch (vt)
{
case VT_I1:
case VT_I2:
case VT_I4:
case VT_R4:
case VT_R8:
case VT_UI1:
case VT_UI2:
case VT_UI4:
case VT_INT:
case VT_UINT:
case VT_CY:
case VT_BOOL:
case VT_DATE:
var.dblVal = 0;
break;
case VT_BSTR:
var.bstrVal = SysAllocString(L"");
if (NULL == var.bstrVal)
{
hr = E_OUTOFMEMORY;
}
break;
default:
hr = E_INVALIDARG;
}
}
else
{
// Should never happen because the data type strings come from
// our internal tables, which must be valid.
AssertSz(FALSE,
"CUPnPAutomationProxy::HrVariantInitForXMLType(): "
"Invalid data type string passed in");
}
if (SUCCEEDED(hr))
{
*pvar = var;
}
else
{
VariantClear(&var);
}
TraceError("CUPnPAutomationProxy::HrVariantInitFroXMLType(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrInvokeAction(
UPNP_CONTROL_REQUEST * pucreq,
UPNP_CONTROL_RESPONSE * pucresp)
{
HRESULT hr = S_OK;
UPNP_CONTROL_RESPONSE ucresp = {0};
UPNP_ACTION * pua = NULL;
pua = LookupActionByName(pucreq->bstrActionName);
if (pua)
{
// Check that we've got the right number of input arguments.
if (pua->cInArgs == pucreq->cInputArgs)
{
DWORD cTotalArgs = 0;
DWORD cOutArgs = 0;
VARIANTARG * rgvarg = NULL;
VARIANTARG * rgvargData = NULL;
VARIANT varResult;
EXCEPINFO excepInfo = {0};
VariantInit(&varResult);
// Build an array of arguments to pass to the service object.
cTotalArgs = pua->cInArgs + pua->cOutArgs;
if (pua->puaRetVal)
{
Assert(cTotalArgs > 0);
// In UTL, the retval is considered an out parameter. In the
// automation world, it's considered separate, so reduce the
// count of parameters by 1 if there is a retval.
cTotalArgs--;
}
cOutArgs = cTotalArgs - pua->cInArgs;
if (cTotalArgs > 0)
{
rgvarg = new VARIANTARG[cTotalArgs];
if (cOutArgs > 0)
{
rgvargData = new VARIANTARG[cOutArgs];
}
else
{
rgvargData = NULL;
}
if (rgvarg && (!cOutArgs || rgvargData))
{
// Have to copy the arguments in reverse order. Out args
// go first.
for (DWORD i = 0,
index = pua->cOutArgs - 1;
SUCCEEDED(hr) && (i < cOutArgs);
i++, index--)
{
UPNP_STATE_VARIABLE * pusvRelated = NULL;
pusvRelated = pua->rgOutArgs[index].pusvRelated;
hr = HrVariantInitForXMLType(&rgvargData[i],
pusvRelated->bstrDataType);
if (SUCCEEDED(hr))
{
rgvarg[i].vt = rgvargData[i].vt | VT_BYREF;
rgvarg[i].pdblVal = &(rgvargData[i].dblVal);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInvokeAction(): "
"Successfully initialized output arg %d",
i);
}
else
{
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to initialize output argument",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to initialize for XML data type",
hr);
}
}
if (SUCCEEDED(hr))
{
// Now the in arguments.
// i is the index into the array of arguments we'll
// pass to IDispatch::Invoke. It starts at the first
// index after the out arguments. j is the index into
// the array of input arguments - it starts at the last
// and goes down to the first.
for (DWORD i = cOutArgs, j = pucreq->cInputArgs - 1;
i < cTotalArgs;
i++, j--)
{
// These will only be read,
// so we're going to just do straight binary copies i.e.
// we won't do VariantCopy().
// Note that because of this, we don't own the memory used
// by the input argument elements, so we don't free them
// when we clean up rgvarg down below.
rgvarg[i] = pucreq->rgvarInputArgs[j];
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to allocate arguments array",
hr);
}
}
else
{
rgvarg = NULL;
}
// Now we have the arguments sorted out. Execute the request.
if (SUCCEEDED(hr))
{
DISPPARAMS actionParams;
actionParams.rgvarg = rgvarg;
actionParams.cArgs = cTotalArgs;
actionParams.rgdispidNamedArgs = NULL;
actionParams.cNamedArgs = 0;
hr = m_pdispService->Invoke(pua->dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&actionParams,
&varResult,
&excepInfo,
NULL);
}
// Build a response.
if (SUCCEEDED(hr))
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInvokeAction(): "
"Action %S executed successfully",
pua->bstrName);
ucresp.bstrActionName = SysAllocString(pua->bstrName);
if (ucresp.bstrActionName)
{
ucresp.fSucceeded = TRUE;
pucrd = &ucresp.ucrData;
if (pua->cOutArgs > 0)
{
pucrd->Success.rgvarOutputArgs = (VARIANT *) CoTaskMemAlloc(
pua->cOutArgs * sizeof(VARIANT));
if (pucrd->Success.rgvarOutputArgs)
{
DWORD dwStartIndex = 0;
pucrd->Success.cOutputArgs = pua->cOutArgs;
if (pua->puaRetVal)
{
VariantInit(&pucrd->Success.rgvarOutputArgs[0]);
hr = VariantCopy(&pucrd->Success.rgvarOutputArgs[0],
&varResult);
if (SUCCEEDED(hr))
{
dwStartIndex = 1;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrInvokeAction(): "
"Successfully copied retval");
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrInvokeAction(): "
"Failed to copy retval",
hr);
}
}
if (SUCCEEDED(hr))
{
for (DWORD i = 0,
j = cOutArgs + dwStartIndex - 1;
SUCCEEDED(hr) && (i < cOutArgs);
i++, j--)
{
VariantInit(&pucrd->Success.rgvarOutputArgs[j]);
hr = VariantCopy(&pucrd->Success.rgvarOutputArgs[j],
&rgvargData[i]);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::"
"HrInvokeAction(): "
"Successfully copied out arg %d",
j);
}
else
{
TraceError("CUPnPAutomationProxy::"
"HrInvokeAction(): "
"Failed to copy out arg",
hr);
}
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to allocate memory for out args",
hr);
}
}
else
{
pucrd->Success.rgvarOutputArgs = NULL;
pucrd->Success.cOutputArgs = 0;
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to allocate memory for action name",
hr);
}
}
else if (DISP_E_EXCEPTION == hr)
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInvokeAction(): "
"Action %S returned an exception",
pua->bstrName);
// Fix up the HRESULT. Even though this is an error in the
// UPnP sense, we are returning success because from the
// processing point of view, the request went through correctly
// and just returned a fault response.
hr = S_OK;
ucresp.bstrActionName = SysAllocString(pua->bstrName);
if (ucresp.bstrActionName)
{
ucresp.fSucceeded = FALSE;
pucrd = &ucresp.ucrData;
// If the service object requested deferred fill-in of
// the exception info, call its callback function now.
if (excepInfo.pfnDeferredFillIn)
{
hr = (*(excepInfo.pfnDeferredFillIn))(&excepInfo);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrInvokeAction(): "
"Successfully filled in "
"deferred exception info");
}
else
{
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to fill in "
"deferred exception info",
hr);
}
}
if (SUCCEEDED(hr))
{
// excepInfo may not be complete
LPCWSTR pszSource = excepInfo.bstrSource ? excepInfo.bstrSource : L"501";
LPCWSTR pszDesc = excepInfo.bstrDescription ? excepInfo.bstrDescription : L"Action Failed";
hr = HrBuildFaultResponse(pucrd,
L"SOAP-ENV:Client",
L"UPnPError",
pszSource,
pszDesc);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to allocate memory for action name",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to invoke action",
hr);
// Build up a SOAP Fault response with the UPnP error code
// "501 - Action Failed". Allow the above HRESULT to be lost
// because even though there was an "error", we're going to
// return success.
ucresp.bstrActionName = SysAllocString(pua->bstrName);
if (ucresp.bstrActionName)
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
ucresp.fSucceeded = FALSE;
pucrd = &ucresp.ucrData;
hr = HrBuildFaultResponse(pucrd,
L"SOAP-ENV:Client",
L"UPnPError",
L"501",
L"Action Failed");
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Failed to allocate memory for action name",
hr);
}
}
// Cleanup. At this point, all the output information should be
// in the ucresp structure.
if (rgvarg)
{
// The input arguments were straight binary copies, so
// we don't want to free them. Free only the output arguments.
for (DWORD i = 0; i < cOutArgs; i++)
{
VariantClear(&rgvargData[i]);
}
delete [] rgvarg;
delete [] rgvargData;
rgvarg = NULL;
rgvargData = NULL;
cTotalArgs = 0;
}
VariantClear(&varResult);
}
else
{
// Invalid arguments.
ucresp.fSucceeded = FALSE;
hr = HrBuildFaultResponse(&ucresp.ucrData,
L"SOAP-ENV:Client",
L"UPnPError",
L"402",
L"Invalid Args");
}
}
else
{
// Invalid Action name
ucresp.fSucceeded = FALSE;
hr = HrBuildFaultResponse(&ucresp.ucrData,
L"SOAP-ENV:Client",
L"UPnPError",
L"401",
L"Invalid Action");
}
// If succeeded, copy the response info to the output structure, otherwise
// free it.
if (SUCCEEDED(hr))
{
*pucresp = ucresp;
}
else
{
FreeControlResponse(&ucresp);
}
TraceError("CUPnPAutomationProxy::HrInvokeAction(): "
"Exiting",
hr);
return hr;
}
HRESULT
CUPnPAutomationProxy::HrQueryStateVariable(
UPNP_CONTROL_REQUEST * pucreq,
UPNP_CONTROL_RESPONSE * pucresp)
{
HRESULT hr = S_OK;
UPNP_CONTROL_RESPONSE ucresp = {0};
UPNP_STATE_VARIABLE * pusv = NULL;
BSTR bstrVarName = NULL;
// QueryStateVariable should have 1 input argument which is the variable
// name.
Assert(pucreq->cInputArgs == 1);
Assert(pucreq->rgvarInputArgs[0].vt == VT_BSTR);
bstrVarName = V_BSTR(&pucreq->rgvarInputArgs[0]);
pusv = LookupVariableByName(bstrVarName);
if (pusv)
{
DISPPARAMS dispparamsEmpty = {NULL, NULL, 0, 0};
VARIANT varResult;
EXCEPINFO excepInfo = {0};
VariantInit(&varResult);
// Query the value.
hr = m_pdispService->Invoke(pusv->dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET,
&dispparamsEmpty,
&varResult,
&excepInfo,
NULL);
// Build a response.
if (SUCCEEDED(hr))
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrQueryStateVariable(): "
"PROPGET for %S succeeded",
bstrVarName);
ucresp.bstrActionName = SysAllocString(L"QueryStateVariable");
if (ucresp.bstrActionName)
{
ucresp.fSucceeded = TRUE;
pucrd = &ucresp.ucrData;
pucrd->Success.cOutputArgs = 1;
pucrd->Success.rgvarOutputArgs = (VARIANT *) CoTaskMemAlloc(
sizeof(VARIANT));
if (pucrd->Success.rgvarOutputArgs)
{
VariantInit(&pucrd->Success.rgvarOutputArgs[0]);
hr = VariantCopy(&pucrd->Success.rgvarOutputArgs[0],
&varResult);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrQueryStateVariable(): "
"Successfully copied result to output");
}
else
{
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Failed to copy result to output",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Failed to allocate memory for output arg",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Failed to allocate memory for action name",
hr);
}
}
else if (DISP_E_EXCEPTION == hr)
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrQueryStateVariable(): "
"PROPGET for %S returned an exception",
bstrVarName);
// Fix up the HRESULT. Even though this is an error in the
// UPnP sense, we are returning success because from the
// processing point of view, the request went through correctly
// and just returned a fault response.
hr = S_OK;
ucresp.bstrActionName = SysAllocString(L"QueryStateVariable");
if (ucresp.bstrActionName)
{
ucresp.fSucceeded = FALSE;
pucrd = &ucresp.ucrData;
// If the service object requested deferred fill-in of
// the exception info, call its callback function now.
if (excepInfo.pfnDeferredFillIn)
{
hr = (*(excepInfo.pfnDeferredFillIn))(&excepInfo);
if (SUCCEEDED(hr))
{
TraceTag(ttidAutomationProxy,
"CUPnPAutomationProxy::HrQueryStateVariable(): "
"Successfully filled in "
"deferred exception info");
}
else
{
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Failed to fill in "
"deferred exception info",
hr);
}
}
if (SUCCEEDED(hr))
{
// excepInfo may not be complete
LPCWSTR pszSource = excepInfo.bstrSource ? excepInfo.bstrSource : L"501";
LPCWSTR pszDesc = excepInfo.bstrDescription ? excepInfo.bstrDescription : L"Action Failed";
hr = HrBuildFaultResponse(pucrd,
L"SOAP-ENV:Client",
L"UPnPError",
pszSource,
pszDesc);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Failed to allocate memory for action name",
hr);
}
}
else
{
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"PROPGET failed",
hr);
}
VariantClear(&varResult);
}
else
{
// Invalid variable name
ucresp.fSucceeded = FALSE;
hr = HrBuildFaultResponse(&ucresp.ucrData,
L"SOAP-ENV:Client",
L"UPnPError",
L"404",
L"Invalid Var");
}
// If succeeded, copy the response info to the output structure, otherwise
// free it.
if (SUCCEEDED(hr))
{
*pucresp = ucresp;
}
else
{
FreeControlResponse(&ucresp);
}
TraceError("CUPnPAutomationProxy::HrQueryStateVariable(): "
"Exiting",
hr);
return hr;
}