3092 lines
94 KiB
C++
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;
|
|
}
|
|
|
|
|