//+--------------------------------------------------------------------------- // // 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(", ")); 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 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 element to the document. hr = pScpdDoc->appendChild(piVersion, NULL); piVersion->Release(); if (FAILED(hr)) { TraceError("Failed to append to document.", hr); } SysFreeString(bstrTarget); SysFreeString(bstrData); } } else { hr = E_OUTOFMEMORY; } } else { hr = E_OUTOFMEMORY; } // Build 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 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 element", hr); } else { // Build 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 element", hr); } } } } SetupCloseInfFileSafe(hinf); } else { TraceTag(ttidUpdiag, "Failed to open file %s, line = %d", szSvcConfigFile, unErrorLine); } // Append the element to the document. if (SUCCEEDED(hr)) { hr = pScpdDoc->appendChild(pScpdNode, NULL); pScpdNode->Release(); if (FAILED(hr)) { TraceError("Failed to append element to document.", hr); } } } else { TraceError("Failed to create 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 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 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 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 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 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 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)) { // hr = HrAddElementWithText(pDoc, pActionNode, TEXT("name"), szActionName); if (SUCCEEDED(hr)) { // 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 node."); } SysFreeString(bstrAction); } else { hr = E_OUTOFMEMORY; } if (S_OK == hr) { *ppActionNode = pActionNode; pActionNode->AddRef(); } TraceError("HrCreateActionNode", hr); return hr; }