📄 serviceimpl.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "pch.h"
#pragma hdrstop
#include "ssdpapi.h"
#include "upnp.h"
#include "url_verifier.h"
#include "upnp_config.h"
#include "ServiceImpl.h"
#include "SoapRequest.h"
#include "com_macros.h"
#include "HttpRequest.h"
extern url_verifier* g_pURL_Verifier;
// ServiceImpl
ServiceImpl::ServiceImpl(LPCWSTR pwszUniqueDeviceName,
LPCWSTR pwszServiceType,
LPCWSTR pwszDescriptionURL,
LPCWSTR pwszControlURL,
LPCWSTR pwszEventsURL,
UINT nLifeTime)
: m_actionQueryStateVar(L"QueryStateVariable", L"urn:schemas-upnp-org:control-1-0"),
m_statevarVarName(L"", StateVar::string, false),
m_dwSubscriptionCookie(0),
m_pUPnPService(NULL),
m_bServiceInstanceDied(false),
m_bParsedRootElement(false),
m_hrInitResult(E_UNEXPECTED),
m_bSubscribed(false)
{
// fabricate QueryStateVariable "action"
Argument arg(L"u:varName", L"", false);
arg.BindStateVar(&m_statevarVarName);
m_actionQueryStateVar.AddInArgument(arg);
m_actionQueryStateVar.AddOutArgument(Argument(L"return", L"", true));
// init data
m_strUniqueServiceName = pwszUniqueDeviceName;
m_strUniqueServiceName += L"::";
m_strUniqueServiceName += pwszServiceType;
m_strType = pwszServiceType;
ce::WideCharToMultiByte(CP_UTF8, pwszDescriptionURL, -1, &m_strDescriptionURL);
ce::WideCharToMultiByte(CP_UTF8, pwszControlURL, -1, &m_strControlURL);
ce::WideCharToMultiByte(CP_UTF8, pwszEventsURL, -1, &m_strEventsURL);
// advise to connection point
m_hrAdviseResult = g_pConnectionPoint->advise(m_strUniqueServiceName, nLifeTime, this, &m_dwSubscriptionCookie);
}
// ~ServiceImpl
ServiceImpl::~ServiceImpl()
{
if(m_dwSubscriptionCookie)
g_pConnectionPoint->unadvise(m_dwSubscriptionCookie);
}
// Init
HRESULT ServiceImpl::Init(const ce::string& strBaseURL)
{
// convert URLs to absolute
char pszAbsoluteURL[INTERNET_MAX_URL_LENGTH];
DWORD dw;
InternetCombineUrlA(strBaseURL, m_strDescriptionURL, pszAbsoluteURL, &(dw = sizeof(pszAbsoluteURL)/sizeof(*pszAbsoluteURL)), 0);
m_strDescriptionURL = pszAbsoluteURL;
if(!g_pURL_Verifier->is_url_ok(m_strDescriptionURL))
{
TraceTag(ttidError, "ServiceImpl::Init: invalid service description URL");
return m_hrInitResult = E_FAIL;
}
InternetCombineUrlA(strBaseURL, m_strControlURL, pszAbsoluteURL, &(dw = sizeof(pszAbsoluteURL)/sizeof(*pszAbsoluteURL)), 0);
m_strControlURL = pszAbsoluteURL;
if(!g_pURL_Verifier->is_url_ok(m_strControlURL))
{
TraceTag(ttidError, "ServiceImpl::Init: invalid service control URL");
return m_hrInitResult = E_FAIL;
}
InternetCombineUrlA(strBaseURL, m_strEventsURL, pszAbsoluteURL, &(dw = sizeof(pszAbsoluteURL)/sizeof(*pszAbsoluteURL)), 0);
m_strEventsURL = pszAbsoluteURL;
if(!g_pURL_Verifier->is_url_ok(m_strEventsURL))
{
TraceTag(ttidError, "ServiceImpl::Init: invalid service events URL");
return m_hrInitResult = E_FAIL;
}
// request service description document
HttpRequest request;
if(!request.Open("GET", m_strDescriptionURL))
return request.GetHresult();
if(!request.Send())
return request.GetHresult();
if(HTTP_STATUS_OK != request.GetStatus())
return request.GetHresult();
ce::SAXReader Reader;
ce::SequentialStream<HttpRequest> Stream(request, upnp_config::max_document_size());
// parse service description document
if(Reader.valid())
{
ce::variant v;
Stream.QueryInterface(IID_ISequentialStream, (void**)&v.punkVal);
v.vt = VT_UNKNOWN;
Reader->putContentHandler(this);
m_hrInitResult = Reader->parse(v);
if(FAILED(m_hrInitResult))
TraceTag(ttidError, "SAXXMLReader::parse returned error 0x%08x", m_hrInitResult);
}
else
m_hrInitResult = Reader.Error();
if(SUCCEEDED(m_hrInitResult) && (m_strSpecVersionMajor != L"1" || m_strSpecVersionMinor != L"0"))
{
TraceTag(ttidError, "ServiceImpl: Invalid document version");
m_hrInitResult = E_FAIL;
}
if(SUCCEEDED(m_hrInitResult))
{
// Bind state variables to action arguments
for(ce::vector<Action>::iterator it = m_Actions.begin(), itEnd = m_Actions.end(); it != itEnd; ++it)
it->BindArgumentsToStateVars(m_StateVars.begin(), m_StateVars.end());
}
return m_hrInitResult;
}
// AddCallback
HRESULT ServiceImpl::AddCallback(IUPnPService* pUPnPService, IUnknown *punkCallback, DWORD* pdwCookie)
{
callback c;
HRESULT hr;
if(FAILED(m_hrInitResult))
return m_hrInitResult;
if(FAILED(m_hrAdviseResult))
return m_hrAdviseResult;
Assert(m_dwSubscriptionCookie != 0);
if(!m_bSubscribed)
{
if(FAILED(hr = g_pConnectionPoint->subscribe(m_dwSubscriptionCookie, m_strEventsURL)))
return hr;
m_bSubscribed = true;
}
if(SUCCEEDED(punkCallback->QueryInterface(IID_IUPnPServiceCallback, (void**)&c.m_pUPnPServiceCallback)))
c.m_type = callback::upnp_service_callback;
else
if(SUCCEEDED(punkCallback->QueryInterface(IID_IDispatch, (void**)&c.m_pDispatch)))
c.m_type = callback::dispatch;
else
return E_FAIL;
Assert(m_pUPnPService == NULL || m_pUPnPService == pUPnPService);
m_pUPnPService = pUPnPService;
ce::gate<ce::critical_section> _gate(m_csListCallback);
m_listCallback.push_front(c);
if(pdwCookie)
{
// return iterator as cookie
Assert(sizeof(ce::list<callback>::iterator) == sizeof(*pdwCookie));
*((ce::list<callback>::iterator*)pdwCookie) = m_listCallback.begin();
}
return S_OK;
}
// RemoveCallback
HRESULT ServiceImpl::RemoveCallback(DWORD dwCookie)
{
HRESULT hr = E_INVALIDARG;
if(FAILED(m_hrInitResult))
return m_hrInitResult;
Assert(sizeof(ce::list<callback>::iterator) == sizeof(dwCookie));
ce::list<callback>::iterator itCallback = *((ce::list<callback>::iterator*)&dwCookie);
ce::gate<ce::critical_section> _gate(m_csListCallback);
// can't assume that dwCookie is a valid iterator so I look for it in the list
for(ce::list<callback>::iterator it = m_listCallback.begin(), itEnd = m_listCallback.end(); it != itEnd; ++it)
if(it == itCallback)
{
m_listCallback.erase(it);
hr = S_OK;
break;
}
return hr;
}
// StateVariableChanged
void ServiceImpl::StateVariableChanged(LPCWSTR pwszName, LPCWSTR pwszValue)
{
ce::variant varValue;
ce::vector<StateVar>::iterator itStateVar, itEndStateVar;
// find the state variable
for(itStateVar = m_StateVars.begin(), itEndStateVar = m_StateVars.end(); itStateVar != itEndStateVar; ++itStateVar)
if(0 == wcscmp(itStateVar->GetName(), pwszName))
{
itStateVar->Decode(pwszValue, &varValue);
break;
}
if(itStateVar != itEndStateVar)
{
ce::gate<ce::critical_section> _gate(m_csListCallback);
for(ce::list<callback>::iterator it = m_listCallback.begin(), itEnd = m_listCallback.end(); it != itEnd; ++it)
if(it->m_type == callback::upnp_service_callback)
{
// IUPnPServiceCallback callback
it->m_pUPnPServiceCallback->StateVariableChanged(m_pUPnPService, pwszName, varValue);
}
else
{
// IDispatch callback
Assert(it->m_type == callback::dispatch);
DISPPARAMS DispParams = {0};
VARIANT rgvarg[4];
UINT uArgErr;
DispParams.cArgs = 4;
DispParams.rgvarg = rgvarg;
rgvarg[3].vt = VT_BSTR;
rgvarg[3].bstrVal = SysAllocString(L"VARIABLE_UPDATE");
// don't AddRef m_pUPnPService
rgvarg[2].vt = VT_DISPATCH;
rgvarg[2].pdispVal = m_pUPnPService;
rgvarg[1].vt = VT_BSTR;
rgvarg[1].bstrVal = SysAllocString(pwszName);
VariantInit(&rgvarg[0]);
if(SUCCEEDED(VariantCopy(&rgvarg[0], &varValue)))
{
it->m_pDispatch->Invoke(DISPID_VALUE, IID_NULL, 0, DISPATCH_METHOD, &DispParams, NULL, NULL, &uArgErr);
VariantClear(&rgvarg[0]);
}
VariantClear(&rgvarg[1]);
// don't clear rgvarg[2]
VariantClear(&rgvarg[3]);
}
}
}
// AliveNotification
void ServiceImpl::AliveNotification(LPCWSTR pszUSN, LPCWSTR pszLocation, LPCWSTR pszAL, DWORD dwLifeTime)
{
}
// ServiceInstanceDied
void ServiceImpl::ServiceInstanceDied(LPCWSTR pszUSN)
{
if(!m_bServiceInstanceDied)
{
ce::gate<ce::critical_section> _gate(m_csListCallback);
m_bServiceInstanceDied = true;
for(ce::list<callback>::iterator it = m_listCallback.begin(), itEnd = m_listCallback.end(); it != itEnd; ++it)
if(it->m_type == callback::upnp_service_callback)
{
// IUPnPServiceCallback callback
it->m_pUPnPServiceCallback->ServiceInstanceDied(m_pUPnPService);
}
else
{
// IDispatch callback
Assert(it->m_type == callback::dispatch);
DISPPARAMS DispParams = {0};
VARIANT rgvarg[2];
UINT uArgErr;
DispParams.cArgs = 2;
DispParams.rgvarg = rgvarg;
rgvarg[1].vt = VT_BSTR;
rgvarg[1].bstrVal = SysAllocString(L"SERVICE_INSTANCE_DIED");
// don't AddRef m_pUPnPService
rgvarg[0].vt = VT_DISPATCH;
rgvarg[0].pdispVal = m_pUPnPService;
it->m_pDispatch->Invoke(DISPID_VALUE, IID_NULL, 0, DISPATCH_METHOD, &DispParams, NULL, NULL, &uArgErr);
VariantClear(&rgvarg[1]);
// don't clear rgvarg[0]
}
}
}
// DispGetParamPtr
HRESULT DispGetParamPtr(DISPPARAMS FAR *pDispParams, unsigned int position, VARIANTARG** ppVar, unsigned int FAR *puArgErr)
{
unsigned int cPositionArgs = pDispParams->cArgs - pDispParams->cNamedArgs;
// check if the argument is among positional arguments
if(position < cPositionArgs)
{
*ppVar = &pDispParams->rgvarg[pDispParams->cArgs - position - 1];
return S_OK;
}
// look for the argument among named arguments
for(unsigned int i = 0; i < pDispParams->cNamedArgs; ++i)
if(static_cast<DISPID>(position) == pDispParams->rgdispidNamedArgs[i])
{
*ppVar = &pDispParams->rgvarg[i];
return S_OK;
}
CHECK_POINTER(puArgErr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -