1148 lines
37 KiB
C++
1148 lines
37 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1997.
|
||
|
//
|
||
|
// File: S C P D G E N . C P P
|
||
|
//
|
||
|
// Contents: Functions that generates scpd for upnp service
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Author: tongl 7 December 1999
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include "pch.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "stdio.h"
|
||
|
#include "ncstring.h"
|
||
|
#include "oleauto.h"
|
||
|
#include "setupapi.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
struct ARG_DATA
|
||
|
{
|
||
|
// argument to an action
|
||
|
TCHAR szArg[256];
|
||
|
|
||
|
// related state variable
|
||
|
TCHAR szVariable[256];
|
||
|
};
|
||
|
|
||
|
struct ARG_LIST
|
||
|
{
|
||
|
DWORD cArgs;
|
||
|
ARG_DATA rgArguments[MAX_ACTION_ARGUMENTS];
|
||
|
};
|
||
|
|
||
|
HRESULT HrCreateScpdNode(IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppScpdNode);
|
||
|
|
||
|
HRESULT HrCreateStateVariableNode(IN LPTSTR szVariableLine,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppVariableNode);
|
||
|
|
||
|
HRESULT HrCreateServiceStateTableNode(IN HINF hinf,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppSSTNode);
|
||
|
|
||
|
HRESULT HrCreateActionNode(IN HINF hinf,
|
||
|
IN LPTSTR szActionLine,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppActionNode);
|
||
|
|
||
|
HRESULT HrCreateActionListNode(IN HINF hinf,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppActionListNode);
|
||
|
|
||
|
BOOL IsStandardOperation(TCHAR * szOpName, DWORD * pnArgs, DWORD * pnConsts);
|
||
|
|
||
|
//
|
||
|
// Create scpd doc for service from the config file and save to
|
||
|
// specified destination
|
||
|
//
|
||
|
|
||
|
EXTERN_C
|
||
|
VOID
|
||
|
__cdecl
|
||
|
wmain (
|
||
|
IN INT argc,
|
||
|
IN PCWSTR argv[])
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (argc != 3)
|
||
|
{
|
||
|
_tprintf(TEXT("\nUsage: \n %s\n\n"),
|
||
|
TEXT("<Service INF file>, <SCPD xml file name and path>"));
|
||
|
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
TCHAR szSvcConfigFile[MAX_PATH];
|
||
|
WszToTszBuf(szSvcConfigFile, argv[1], MAX_PATH);
|
||
|
|
||
|
LPCWSTR pszScpdFileWithPath = argv[2];
|
||
|
|
||
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("CoInitializeEx", hr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IXMLDOMDocument *pScpdDoc = NULL;
|
||
|
|
||
|
// Create a new document object
|
||
|
hr = CoCreateInstance(CLSID_DOMDocument30,
|
||
|
NULL,
|
||
|
CLSCTX_INPROC_SERVER,
|
||
|
IID_IXMLDOMDocument,
|
||
|
(void **) &pScpdDoc);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pScpdDoc->put_preserveWhiteSpace(VARIANT_TRUE);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Build <?xml version="1.0"?>
|
||
|
IXMLDOMProcessingInstruction * piVersion = NULL;
|
||
|
|
||
|
BSTR bstrTarget = SysAllocString(L"xml");
|
||
|
if (bstrTarget)
|
||
|
{
|
||
|
BSTR bstrData = SysAllocString(L"version=\"1.0\"");
|
||
|
if (bstrData)
|
||
|
{
|
||
|
hr = pScpdDoc->createProcessingInstruction(
|
||
|
bstrTarget, bstrData, &piVersion);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Append the <version> element to the document.
|
||
|
hr = pScpdDoc->appendChild(piVersion, NULL);
|
||
|
piVersion->Release();
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("Failed to append <version> to document.", hr);
|
||
|
}
|
||
|
|
||
|
SysFreeString(bstrTarget);
|
||
|
SysFreeString(bstrData);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// Build <scpd> element
|
||
|
VARIANT vNULL;
|
||
|
vNULL.vt = VT_EMPTY;
|
||
|
|
||
|
IXMLDOMElement * pScpdNode = NULL;
|
||
|
hr = HrCreateScpdNode(pScpdDoc, &pScpdNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// open the config file
|
||
|
HINF hinf = NULL;
|
||
|
UINT unErrorLine;
|
||
|
|
||
|
hr = HrSetupOpenConfigFile(szSvcConfigFile, &unErrorLine, &hinf);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert(IsValidHandle(hinf));
|
||
|
|
||
|
// Build <serviceStateTable> element
|
||
|
IXMLDOMElement * pSSTNode = NULL;
|
||
|
hr = HrCreateServiceStateTableNode(hinf, pScpdDoc, &pSSTNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
Assert(pSSTNode);
|
||
|
hr = pScpdNode->insertBefore(pSSTNode,
|
||
|
vNULL,
|
||
|
NULL);
|
||
|
|
||
|
pSSTNode->Release();
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("Failed to insert <serviceStateTable> element", hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Build <actionList> element
|
||
|
IXMLDOMElement * pActionListNode = NULL;
|
||
|
hr = HrCreateActionListNode(hinf, pScpdDoc, &pActionListNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
Assert(pActionListNode);
|
||
|
hr = pScpdNode->insertBefore(pActionListNode,
|
||
|
vNULL,
|
||
|
NULL);
|
||
|
pActionListNode->Release();
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("Failed to insert <actionList> element", hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetupCloseInfFileSafe(hinf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "Failed to open file %s, line = %d",
|
||
|
szSvcConfigFile, unErrorLine);
|
||
|
}
|
||
|
|
||
|
// Append the <scpd> element to the document.
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pScpdDoc->appendChild(pScpdNode, NULL);
|
||
|
pScpdNode->Release();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("Failed to append <scpd> element to document.", hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("Failed to create <scpd> element", hr);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Persist the xml document
|
||
|
VARIANT vFileName;
|
||
|
vFileName.vt = VT_BSTR;
|
||
|
|
||
|
V_BSTR(&vFileName) = SysAllocString(pszScpdFileWithPath);
|
||
|
|
||
|
hr = pScpdDoc->save(vFileName);
|
||
|
VariantClear(&vFileName);
|
||
|
}
|
||
|
|
||
|
pScpdDoc->Release();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("Failed to create XML DOM Document object", hr);
|
||
|
}
|
||
|
|
||
|
CoUninitialize();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the <scpd> note that will include a serviceStateTable and
|
||
|
// an actionList
|
||
|
//
|
||
|
HRESULT HrCreateScpdNode(IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppScpdNode)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(ppScpdNode);
|
||
|
*ppScpdNode = NULL;
|
||
|
|
||
|
IXMLDOMElement * pScpdNode = NULL;
|
||
|
BSTR bstrElementName = SysAllocString(L"scpd");
|
||
|
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pScpdDoc->createElement(bstrElementName, &pScpdNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// set the xmlns attribute
|
||
|
BSTR bstrAttrName = SysAllocString(L"xmlns");
|
||
|
if (bstrAttrName)
|
||
|
{
|
||
|
VARIANT vValue;
|
||
|
vValue.vt = VT_BSTR;
|
||
|
V_BSTR(&vValue) = SysAllocString(L"x-schema:scpdl-schema.xml");
|
||
|
|
||
|
hr = pScpdNode->setAttribute(bstrAttrName, vValue);
|
||
|
|
||
|
SysFreeString(bstrAttrName);
|
||
|
VariantClear(&vValue);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppScpdNode = pScpdNode;
|
||
|
pScpdNode->AddRef();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: Failed to set xmlns attribute", hr);
|
||
|
}
|
||
|
pScpdNode->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: Failed to create <scpd> element", hr);
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
TraceError("HrCreateScpdNode: Failed to allocate BSTR for element name", hr);
|
||
|
}
|
||
|
|
||
|
TraceError("HrCreateScpdNode", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrCreateServiceStateTableNode(IN HINF hinf,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppSSTNode)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(ppSSTNode);
|
||
|
*ppSSTNode = NULL;
|
||
|
|
||
|
IXMLDOMElement * pSSTNode = NULL;
|
||
|
BSTR bstrElementName = SysAllocString(L"serviceStateTable");
|
||
|
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pScpdDoc->createElement(bstrElementName, &pSSTNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// get the [StateTable] section
|
||
|
INFCONTEXT ctx;
|
||
|
|
||
|
hr = HrSetupFindFirstLine(hinf, TEXT("StateTable"), NULL, &ctx);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// loop through [StateTable] section and create stateVariable's
|
||
|
|
||
|
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
|
||
|
TCHAR szVariableLine[LINE_LEN];
|
||
|
|
||
|
IXMLDOMElement * pVariableNode = NULL;
|
||
|
VARIANT vNULL;
|
||
|
vNULL.vt = VT_EMPTY;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Retrieve a line from the ActionSet section
|
||
|
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
|
||
|
if(S_OK == hr)
|
||
|
{
|
||
|
// varify this is a "Variable"
|
||
|
szKey[celems(szKey)-1] = L'\0';
|
||
|
if (lstrcmpi(szKey, TEXT("Variable")))
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "Wrong key in the StateTable section: %s", szKey);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// get the line text
|
||
|
hr = HrSetupGetLineText(ctx, szVariableLine, celems(szVariableLine),
|
||
|
NULL);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Add variable in this line
|
||
|
hr = HrCreateStateVariableNode(szVariableLine, pScpdDoc, &pVariableNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
Assert(pVariableNode);
|
||
|
hr = pSSTNode->insertBefore(pVariableNode,
|
||
|
vNULL,
|
||
|
NULL);
|
||
|
pVariableNode->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
|
||
|
|
||
|
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
// S_FALSE will terminate the loop successfully, so convert it to S_OK
|
||
|
// here.
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
*ppSSTNode = pSSTNode;
|
||
|
pSSTNode->AddRef();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: [StateTable] section not found in inf", hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: Failed to create <serviceStateTable> element", hr);
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
TraceError("HrCreateServiceStateTableNode: Failed to allocate BSTR for element name", hr);
|
||
|
}
|
||
|
|
||
|
TraceError("HrCreateServiceStateTableNode", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrAddElementWithText(IXMLDOMDocument * pDoc,
|
||
|
IXMLDOMElement * pParentNode,
|
||
|
LPTSTR szElementName,
|
||
|
LPTSTR szElementText)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
IXMLDOMElement * pNewNode = NULL;
|
||
|
VARIANT vNull;
|
||
|
vNull.vt = VT_EMPTY;
|
||
|
|
||
|
BSTR bstrElementName = NULL;
|
||
|
BSTR bstrTextValue = NULL;
|
||
|
|
||
|
WCHAR * wszElementName = WszFromTsz(szElementName);
|
||
|
WCHAR * wszElementText = WszFromTsz(szElementText);
|
||
|
|
||
|
if (!wszElementName || !wszElementText)
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bstrElementName = SysAllocString(wszElementName);
|
||
|
bstrTextValue = SysAllocString(wszElementText);
|
||
|
|
||
|
if (!bstrElementName || !bstrTextValue)
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
delete wszElementName;
|
||
|
delete wszElementText;
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrElementName, &pNewNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
IXMLDOMText * pText = NULL;
|
||
|
|
||
|
hr = pDoc->createTextNode(bstrTextValue, &pText);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pNewNode->insertBefore(pText, vNull, NULL);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("HrAddElementWithText: Failed to insert text node", hr);
|
||
|
}
|
||
|
pText->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrAddElementWithText: Failed to create text node", hr);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pParentNode->insertBefore(pNewNode, vNull, NULL);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceError("HrAddElementWithText: Failed to insert new node", hr);
|
||
|
}
|
||
|
}
|
||
|
pNewNode->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrAddElementWithText: Could not create new element", hr);
|
||
|
}
|
||
|
|
||
|
SysFreeString(bstrElementName);
|
||
|
SysFreeString(bstrTextValue);
|
||
|
}
|
||
|
|
||
|
TraceError("HrAddElementWithText", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
BOOL fIsValidDataType(LPTSTR szBuf)
|
||
|
{
|
||
|
return ((lstrcmpi(szBuf, TEXT("number")) ==0) ||
|
||
|
(lstrcmpi(szBuf, TEXT("string")) ==0) ||
|
||
|
(lstrcmpi(szBuf, TEXT("dateTime")) ==0) ||
|
||
|
(lstrcmpi(szBuf, TEXT("boolean")) ==0) ||
|
||
|
(lstrcmpi(szBuf, TEXT("ByteBlock")) ==0));
|
||
|
}
|
||
|
|
||
|
HRESULT HrAddAllowedValueNode( IXMLDOMDocument * pDoc,
|
||
|
IXMLDOMElement * pVariableNode,
|
||
|
LPTSTR szBuf)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(*szBuf == '(');
|
||
|
szBuf++;
|
||
|
|
||
|
IXMLDOMElement * pAllowedValueNode = NULL;
|
||
|
BSTR bstrElementName;
|
||
|
|
||
|
// we assume that ".." specifies a range, otherwise it's a comma separated
|
||
|
// list of allowed values
|
||
|
TCHAR * pChar = _tcsstr(szBuf, TEXT(".."));
|
||
|
if (pChar)
|
||
|
{
|
||
|
// we have a range
|
||
|
// BUGBUG: we should check if data type of the min, max & step
|
||
|
// matches the variable type
|
||
|
TCHAR * szMin, * szMax, * szStep;
|
||
|
|
||
|
szMin = szBuf;
|
||
|
*pChar = '\0';
|
||
|
|
||
|
szMax = pChar+2;
|
||
|
pChar = _tcschr(szMax, TEXT(','));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
szStep = ++pChar;
|
||
|
|
||
|
pChar = _tcschr(szStep, TEXT(')'));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
|
||
|
bstrElementName = SysAllocString(L"allowedValueRange");
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrElementName, &pAllowedValueNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pDoc, pAllowedValueNode, TEXT("minimum"), szMin);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pDoc, pAllowedValueNode, TEXT("maximum"), szMax);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pDoc, pAllowedValueNode, TEXT("step"), szStep);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrAddAllowedValueNode: missing closing )");
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrAddAllowedValueNode: step not specified");
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we have a list of allowed values
|
||
|
pChar = _tcschr(szBuf, TEXT(')'));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
if (lstrlen(szBuf))
|
||
|
{
|
||
|
bstrElementName = SysAllocString(L"allowedValueList");
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrElementName, &pAllowedValueNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
while ((S_OK ==hr) && (pChar = _tcschr(szBuf, TEXT(','))))
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
hr = HrAddElementWithText(pDoc, pAllowedValueNode,
|
||
|
TEXT("allowedValue"), szBuf);
|
||
|
|
||
|
szBuf = ++pChar;
|
||
|
}
|
||
|
|
||
|
// add the last one
|
||
|
if (*szBuf)
|
||
|
{
|
||
|
hr = HrAddElementWithText(pDoc, pAllowedValueNode,
|
||
|
TEXT("allowedValue"), szBuf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrAddAllowedValueNode: invalid syntax");
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrAddAllowedValueNode: missing closing )");
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pAllowedValueNode && (S_OK == hr))
|
||
|
{
|
||
|
// append the allowed value node to "stateVariable"
|
||
|
VARIANT vNull;
|
||
|
vNull.vt = VT_EMPTY;
|
||
|
|
||
|
hr = pVariableNode->insertBefore(pAllowedValueNode, vNull, NULL);
|
||
|
}
|
||
|
|
||
|
TraceError("HrAddAllowedValueRangeNode" , hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrCreateStateVariableNode(IN LPTSTR szVariableLine,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppVariableNode)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(ppVariableNode);
|
||
|
*ppVariableNode = NULL;
|
||
|
|
||
|
IXMLDOMElement * pVariableNode = NULL;
|
||
|
BSTR bstrElementName = SysAllocString(L"stateVariable");
|
||
|
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pScpdDoc->createElement(bstrElementName, &pVariableNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
TCHAR szBuf[MAX_PATH];
|
||
|
if (fGetNextField(&szVariableLine, szBuf))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pScpdDoc, pVariableNode, TEXT("name"), szBuf);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (fGetNextField(&szVariableLine, szBuf))
|
||
|
{
|
||
|
if (fIsValidDataType(szBuf))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pScpdDoc, pVariableNode,
|
||
|
TEXT("dataType"), szBuf);
|
||
|
|
||
|
// AllowedValueRange is optional
|
||
|
if (SUCCEEDED(hr) && fGetNextField(&szVariableLine, szBuf))
|
||
|
{
|
||
|
hr = HrAddAllowedValueNode(pScpdDoc, pVariableNode, szBuf);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr) && fGetNextField(&szVariableLine, szBuf))
|
||
|
{
|
||
|
hr = HrAddElementWithText(pScpdDoc, pVariableNode,
|
||
|
TEXT("defaultValue"), szBuf);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
TraceError("HrCreateStateVariableNode: invalid data type specified", hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
*ppVariableNode = pVariableNode;
|
||
|
pVariableNode->AddRef();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateStateVariableNode: Failed to create <stateVariable> element", hr);
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
TraceError("HrCreateStateVariableNode: Failed to allocate BSTR for element name", hr);
|
||
|
}
|
||
|
|
||
|
TraceError("HrCreateStateVariableNode", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrCreateActionListNode(IN HINF hinf,
|
||
|
IN IXMLDOMDocument * pScpdDoc,
|
||
|
OUT IXMLDOMElement ** ppActionListNode)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(ppActionListNode);
|
||
|
*ppActionListNode = NULL;
|
||
|
|
||
|
IXMLDOMElement * pActionListNode = NULL;
|
||
|
BSTR bstrElementName = SysAllocString(L"actionList");
|
||
|
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pScpdDoc->createElement(bstrElementName, &pActionListNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// get the [ActionSet] section
|
||
|
INFCONTEXT ctx;
|
||
|
|
||
|
hr = HrSetupFindFirstLine(hinf, TEXT("ActionSet"), NULL, &ctx);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// loop through [ActionSet] section and create actions
|
||
|
|
||
|
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
|
||
|
TCHAR szActionLine[LINE_LEN];
|
||
|
|
||
|
IXMLDOMElement * pActionNode = NULL;
|
||
|
VARIANT vNULL;
|
||
|
vNULL.vt = VT_EMPTY;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Retrieve a line from the ActionSet section
|
||
|
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
|
||
|
if(S_OK == hr)
|
||
|
{
|
||
|
// varify this is a "Action"
|
||
|
szKey[celems(szKey)-1] = L'\0';
|
||
|
if (lstrcmpi(szKey, TEXT("Action")))
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "Wrong key in the ActionSet section: %s", szKey);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// get the line text
|
||
|
hr = HrSetupGetLineText(ctx, szActionLine, celems(szActionLine),
|
||
|
NULL);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Add action in this line
|
||
|
hr = HrCreateActionNode(hinf, szActionLine, pScpdDoc, &pActionNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
Assert(pActionNode);
|
||
|
hr = pActionListNode->insertBefore( pActionNode,
|
||
|
vNULL,
|
||
|
NULL);
|
||
|
pActionNode->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
|
||
|
|
||
|
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
// S_FALSE will terminate the loop successfully, so convert it to S_OK
|
||
|
// here.
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
*ppActionListNode = pActionListNode;
|
||
|
pActionListNode->AddRef();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: [ActionSet] not found in service inf", hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceError("HrCreateScpdNode: Failed to create <actionList> element", hr);
|
||
|
}
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
TraceError("HrCreateScpdNode: Failed to allocate BSTR for element name", hr);
|
||
|
}
|
||
|
|
||
|
TraceError("HrCreateActionListNode", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Construct the list of arguments and related state variable
|
||
|
// for each argument
|
||
|
//
|
||
|
HRESULT HrGetArgumentList(IN HINF hinf,
|
||
|
IN LPTSTR szActionName,
|
||
|
IN LPTSTR szActionArgList,
|
||
|
OUT ARG_LIST * pargList)
|
||
|
{
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
BOOL fNamedArgs = FALSE;
|
||
|
if (szActionArgList)
|
||
|
{
|
||
|
fNamedArgs = !!lstrlen(szActionArgList);
|
||
|
}
|
||
|
|
||
|
DWORD dwNum = 1;
|
||
|
|
||
|
// Loop over the list of operations for this action
|
||
|
INFCONTEXT ctx;
|
||
|
hr = HrSetupFindFirstLine(hinf, szActionName, NULL, &ctx);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
|
||
|
TCHAR szOpLine[LINE_LEN];
|
||
|
|
||
|
// Retrieve a line from the Action
|
||
|
hr = HrSetupGetStringField(ctx, 0, szKey, celems(szKey), NULL);
|
||
|
if(S_OK == hr)
|
||
|
{
|
||
|
// varify this is an "Operation"
|
||
|
szKey[celems(szKey)-1] = L'\0';
|
||
|
if (lstrcmpi(szKey, TEXT("Operation")))
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "ERROR! HrGetRelatedVariableList: Wrong key in the Operation section: %s", szKey);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
hr = HrSetupGetLineText(ctx, szOpLine, celems(szOpLine), NULL);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Get the affected variables in this operation
|
||
|
TCHAR * szRelatedVariable;
|
||
|
|
||
|
TCHAR * pChar = _tcschr(szOpLine, TEXT('('));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar ='\0';
|
||
|
TCHAR * szOpName = szOpLine;
|
||
|
|
||
|
pChar ++;
|
||
|
szRelatedVariable = pChar;
|
||
|
|
||
|
DWORD nArgs;
|
||
|
DWORD nConsts;
|
||
|
if (IsStandardOperation(szOpName, &nArgs, &nConsts))
|
||
|
{
|
||
|
// get the Variable name
|
||
|
if (nArgs+nConsts ==0)
|
||
|
{
|
||
|
pChar = _tcschr(szRelatedVariable, TEXT(')'));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pChar = _tcschr(szRelatedVariable, TEXT(','));
|
||
|
}
|
||
|
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = TEXT('\0');
|
||
|
|
||
|
// BUGBUG: Can we check if the variable is valid ??
|
||
|
for (DWORD iArg =0; iArg < nArgs; iArg++)
|
||
|
{
|
||
|
TCHAR szArgName[256];
|
||
|
*szArgName = '\0';
|
||
|
|
||
|
if (!fNamedArgs)
|
||
|
{
|
||
|
// generate argument name, add to the list
|
||
|
lstrcpy(szArgName, TEXT("Argument_"));
|
||
|
|
||
|
TCHAR szNum[2];
|
||
|
_itot(dwNum, szNum, 10);
|
||
|
|
||
|
lstrcat(szArgName, szNum);
|
||
|
dwNum++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// use the argument name specified in the inf
|
||
|
pChar = _tcschr(szActionArgList, TEXT(','));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
lstrcpy(szArgName, szActionArgList);
|
||
|
|
||
|
pChar ++;
|
||
|
szActionArgList = pChar;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lstrcpy(szArgName, szActionArgList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(*szArgName))
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "Argument list incomplete!");
|
||
|
hr = E_INVALIDARG;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pargList->cArgs < MAX_ACTION_ARGUMENTS)
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrGetArgumentList: action: %s, argument: %s, relatedStateVariable: %s",
|
||
|
szActionName, szArgName, szRelatedVariable);
|
||
|
|
||
|
lstrcpy(pargList->rgArguments[pargList->cArgs].szArg, szArgName);
|
||
|
lstrcpy(pargList->rgArguments[pargList->cArgs].szVariable, szRelatedVariable);
|
||
|
pargList->cArgs++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "Too many arguments!");
|
||
|
hr = E_INVALIDARG;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "ERROR! HrGetArgumentList: unknown operation: %s",
|
||
|
szOpName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
|
||
|
}
|
||
|
|
||
|
if (hr == S_FALSE)
|
||
|
{
|
||
|
// S_FALSE will terminate the loop successfully, so convert it to S_OK
|
||
|
// here.
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
TraceError("HrGetArgumentList",hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrAddArgumentNode( IXMLDOMDocument * pDoc,
|
||
|
IXMLDOMElement * pArgumentListNode,
|
||
|
LPTSTR szArgumentName,
|
||
|
LPTSTR szVariableName)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
IXMLDOMElement * pArgumentNode = NULL;
|
||
|
BSTR bstrElementName = SysAllocString(L"argument");
|
||
|
if (bstrElementName)
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrElementName, &pArgumentNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// name
|
||
|
hr = HrAddElementWithText(pDoc, pArgumentNode,
|
||
|
TEXT("name"), szArgumentName);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// relatedStateVariable
|
||
|
hr = HrAddElementWithText(pDoc, pArgumentNode,
|
||
|
TEXT("relatedStateVariable"), szVariableName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrAddArgumentNode: failed creating <argument> node");
|
||
|
}
|
||
|
|
||
|
SysFreeString(bstrElementName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
if (pArgumentNode && (S_OK == hr))
|
||
|
{
|
||
|
// append the argument node to "argumentList"
|
||
|
VARIANT vNull;
|
||
|
vNull.vt = VT_EMPTY;
|
||
|
|
||
|
hr = pArgumentListNode->insertBefore(pArgumentNode, vNull, NULL);
|
||
|
pArgumentNode->Release();
|
||
|
}
|
||
|
|
||
|
TraceError("HrAddArgumentNode", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT HrCreateActionNode(IN HINF hinf,
|
||
|
IN LPTSTR szActionLine,
|
||
|
IN IXMLDOMDocument * pDoc,
|
||
|
OUT IXMLDOMElement ** ppActionNode)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
Assert(ppActionNode);
|
||
|
*ppActionNode = NULL;
|
||
|
|
||
|
IXMLDOMElement * pActionNode = NULL;
|
||
|
BSTR bstrAction = SysAllocString(L"action");
|
||
|
|
||
|
if (bstrAction)
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrAction, &pActionNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
TCHAR * szActionName = szActionLine;
|
||
|
TCHAR * szActionArgList = NULL;
|
||
|
|
||
|
TCHAR * pChar = _tcschr(szActionLine, TEXT('('));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
|
||
|
pChar ++;
|
||
|
szActionArgList = pChar;
|
||
|
|
||
|
pChar = _tcschr(szActionArgList, TEXT(')'));
|
||
|
if (pChar)
|
||
|
{
|
||
|
*pChar = '\0';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrCreateActionNode: argument list missing closing )");
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// <name>
|
||
|
hr = HrAddElementWithText(pDoc, pActionNode, TEXT("name"), szActionName);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// <argumentList>
|
||
|
ARG_LIST argList;
|
||
|
argList.cArgs = 0;
|
||
|
|
||
|
hr = HrGetArgumentList(hinf, szActionName, szActionArgList, &argList);
|
||
|
|
||
|
if (SUCCEEDED(hr) && (argList.cArgs > 0))
|
||
|
{
|
||
|
IXMLDOMElement * pArgumentListNode = NULL;
|
||
|
BSTR bstrArgumentList = SysAllocString(L"argumentList");
|
||
|
if (bstrArgumentList)
|
||
|
{
|
||
|
hr = pDoc->createElement(bstrArgumentList, &pArgumentListNode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
for (DWORD iArg = 0; iArg < argList.cArgs; iArg++)
|
||
|
{
|
||
|
hr = HrAddArgumentNode(pDoc, pArgumentListNode,
|
||
|
argList.rgArguments[iArg].szArg,
|
||
|
argList.rgArguments[iArg].szVariable);
|
||
|
if (FAILED(hr))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// append the argumentList node to "action"
|
||
|
VARIANT vNull;
|
||
|
vNull.vt = VT_EMPTY;
|
||
|
|
||
|
hr = pActionNode->insertBefore(pArgumentListNode, vNull, NULL);
|
||
|
pArgumentListNode->Release();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrCreateActionNode: failed creating argument list node");
|
||
|
}
|
||
|
|
||
|
SysFreeString(bstrArgumentList);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag(ttidUpdiag, "HrCreateActionNode: Failed to create <action> node.");
|
||
|
}
|
||
|
SysFreeString(bstrAction);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
*ppActionNode = pActionNode;
|
||
|
pActionNode->AddRef();
|
||
|
}
|
||
|
|
||
|
TraceError("HrCreateActionNode", hr);
|
||
|
return hr;
|
||
|
}
|