//+--------------------------------------------------------------------------- // // 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 #pragma hdrstop #include #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 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 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 elements", hr); } pxdnSST->Release(); } else { TraceError("CUPnPAutomationProxy::" "HrBuildTablesFromServiceDescription(): " "Failed to get 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 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 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 elements", hr); } pxdnActionList->Release(); } else { TraceErrorOptional("CUPnPAutomationProxy::" "HrBuildTablesFromServiceDescription(): " "Failed to get 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 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 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 elements and read each one's // 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 - // 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 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 value", hr); } } else { TraceError("CUPnPAutomationProxy::HrInitializeArguments(): " "Failed to get 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 value", hr); } } else { TraceError("CUPnPAutomationProxy::HrInitializeArguments(): " "Failed to get 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; }