// // util.cpp // // utility functions used by updiag.exe // #include "pch.h" #pragma hdrstop #include "oleauto.h" #include "ncbase.h" #include "ncinet.h" #include "ssdpapi.h" #include "util.h" // // Functions for Standard State Table Operations // DWORD Do_Set(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_Assign(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_Toggle(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_Increment(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_Decrement(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_IncrementWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_DecrementWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_IncrementBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_DecrementBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_NextStringWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_PrevStringWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_NextStringBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); DWORD Do_PrevStringBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs); // // List of Standard State Table Operations // extern const STANDARD_OPERATION_LIST c_Ops = { // total number of standard operations 13, { // Operation name, # of arguments, # of constants, actual function { TEXT("SET"), 0, 1, Do_Set}, { TEXT("ASSIGN"), 1, 0, Do_Assign}, { TEXT("TOGGLE"), 0, 0, Do_Toggle}, { TEXT("INCREMENT"), 0, 0, Do_Increment}, { TEXT("DECREMENT"), 0, 0, Do_Decrement}, { TEXT("INCREMENT_WRAP"), 0, 2, Do_IncrementWrap}, { TEXT("DECREMENT_WRAP"), 0, 2, Do_DecrementWrap}, { TEXT("INCREMENT_BOUNDED"), 0, 1, Do_IncrementBounded}, { TEXT("DECREMENT_BOUNDED"), 0, 1, Do_DecrementBounded}, { TEXT("NEXT_STRING_WRAP"), 0, 0, Do_NextStringWrap}, { TEXT("PREV_STRING_WRAP"), 0, 0, Do_PrevStringWrap}, { TEXT("NEXT_STRING_BOUNDED"), 0, 0, Do_NextStringBounded}, { TEXT("PREV_STRING_BOUNDED"), 0, 0, Do_PrevStringBounded}, }, }; VOID WcharToTcharInPlace(LPTSTR szT, LPWSTR szW) { DWORD cch = wcslen(szW) + 1; #ifndef UNICODE WideCharToMultiByte(CP_ACP, 0, szW, cch, szT, cch, NULL, NULL); #else lstrcpyW(szT, szW); #endif } //+--------------------------------------------------------------------------- // // Function: HrSetupOpenConfigFile // // Purpose: Open a service's configuration file (INF format) // HRESULT HrSetupOpenConfigFile( PCTSTR pszFileName, UINT* punErrorLine, HINF* phinf) { HRESULT hr; HINF hinf; Assert (pszFileName); Assert (phinf); // Try to open the file. // hinf = SetupOpenInfFile (pszFileName, NULL, INF_STYLE_WIN4, punErrorLine); if (INVALID_HANDLE_VALUE != hinf) { hr = S_OK; *phinf = hinf; } else { hr = HrFromLastWin32Error (); *phinf = NULL; if (punErrorLine) { *punErrorLine = 0; } } TraceHr (ttidError, FAL, hr, FALSE, "HrSetupOpenConfigFile (%S)", pszFileName); return hr; } //+--------------------------------------------------------------------------- // // Function: HrSetupFindFirstLine // // Purpose: Find the first line in an INF file with a matching section // and key. // HRESULT HrSetupFindFirstLine( HINF hinf, PCTSTR pszSection, PCTSTR pszKey, INFCONTEXT* pctx) { Assert (hinf); Assert (pszSection); Assert (pctx); HRESULT hr; if (SetupFindFirstLine (hinf, pszSection, pszKey, pctx)) { hr = S_OK; } else { hr = HrFromLastWin32Error (); } TraceErrorOptional ("HrSetupFindFirstLine", hr, (SPAPI_E_LINE_NOT_FOUND == hr)); return hr; } //+--------------------------------------------------------------------------- // // Function: HrSetupFindNextLine // // Purpose: Find the next line in an INF file relative to ctxIn. // HRESULT HrSetupFindNextLine( const INFCONTEXT& ctxIn, INFCONTEXT* pctxOut) { Assert (pctxOut); HRESULT hr; if (SetupFindNextLine (const_cast(&ctxIn), pctxOut)) { hr = S_OK; } else { hr = HrFromLastWin32Error (); if (SPAPI_E_LINE_NOT_FOUND == hr) { // Translate ERROR_LINE_NOT_FOUND into S_FALSE hr = S_FALSE; } } TraceError ("HrSetupFindNextLine", (hr == S_FALSE) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Function: HrSetupGetStringField // // Purpose: Gets a string from an INF field. // HRESULT HrSetupGetStringField( const INFCONTEXT& ctx, DWORD dwFieldIndex, PTSTR pszBuf, DWORD cchBuf, DWORD* pcchRequired) { HRESULT hr; if (SetupGetStringField ((PINFCONTEXT)&ctx, dwFieldIndex, pszBuf, cchBuf, pcchRequired)) { hr = S_OK; } else { hr = HrFromLastWin32Error (); if (pszBuf) { *pszBuf = 0; } if (pcchRequired) { *pcchRequired = 0; } } TraceError ("HrSetupGetStringField", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: HrSetupGetLineText // // Purpose: Gets a line from an INF field. // HRESULT HrSetupGetLineText( const INFCONTEXT& ctx, PTSTR pszBuf, DWORD cchBuf, DWORD* pcchRequired) { HRESULT hr; if (SetupGetLineText((PINFCONTEXT)&ctx, NULL, NULL, NULL, pszBuf, cchBuf, pcchRequired)) { hr = S_OK; } else { hr = HrFromLastWin32Error (); if (pszBuf) { *pszBuf = 0; } if (pcchRequired) { *pcchRequired = 0; } } TraceError ("HrSetupGetStringField", hr); return hr; } VOID SetupCloseInfFileSafe(HINF hinf) { if (IsValidHandle(hinf)) { SetupCloseInfFile(hinf); } } // get the next field // returns FALSE if field not found or is empty BOOL fGetNextField(TCHAR ** pszLine, TCHAR * szBuffer) { Assert(*pszLine); Assert(szBuffer); *szBuffer = '\0'; if (lstrlen(*pszLine)) { TCHAR * pChar; if (**pszLine == '(') { pChar = _tcschr(*pszLine, TEXT(')')); pChar++; } else { pChar = _tcschr(*pszLine, TEXT(',')); } if (pChar) { *pChar ='\0'; lstrcpy(szBuffer, *pszLine); *pszLine = ++pChar; } else { lstrcpy(szBuffer, *pszLine); **pszLine = '\0'; } } return (!!lstrlen(szBuffer)); } // Input: name of the operation // Output: number of arguments and constants BOOL IsStandardOperation(TCHAR * szOpName, DWORD * pnArgs, DWORD * pnConsts) { *pnArgs =0; *pnConsts=0; for (DWORD iOps = 0; iOps < c_Ops.cOperations; iOps++) { if (!_tcsicmp(c_Ops.rgOperations[iOps].szOperation, szOpName)) { *pnArgs = c_Ops.rgOperations[iOps].nArguments; *pnConsts = c_Ops.rgOperations[iOps].nConstants; return TRUE; } } return FALSE; } // Find the row in the state table that needs to be changed // SST_ROW * FindSSTRowByVarName(SST * psst, TCHAR * szVariableName) { SST_ROW * pRow = NULL; for (DWORD iRow =0; iRowcRows; iRow++) { if (!lstrcmpi(psst->rgRows[iRow].szPropName, szVariableName)) { pRow = &psst->rgRows[iRow]; break; } } return pRow; } // // Functions for Standard State Table Operations // HRESULT HrSetVariantValue(VARIANT * pVar, TCHAR * szNewValue) { HRESULT hr; VARIANT varNew; VariantInit(&varNew); varNew.vt = VT_BSTR; WCHAR * wszNewValue = WszFromTsz(szNewValue); V_BSTR(&varNew) = SysAllocString(wszNewValue); hr = VariantChangeType(&varNew, &varNew, 0, pVar->vt); if (S_OK == hr) { hr = VariantCopy(pVar, &varNew); } TraceError("HrSetVariantValue", hr); return hr; } DWORD dwSubmitEvent(UPNPSVC * psvc, SST_ROW * pRow) { HRESULT hr; DWORD dwErr =0; CHAR szUri[INTERNET_MAX_URL_LENGTH]; UPNP_PROPERTY Property = {0}; // convert current value to string VARIANT varValue; VariantInit(&varValue); hr = VariantChangeType(&varValue, &pRow->varValue, 0, VT_BSTR); if (S_OK ==hr) { LPSTR pszUrl = SzFromTsz(psvc->szEvtUrl); if (pszUrl) { LPWSTR wszVal = varValue.bstrVal; Property.szValue = SzFromWsz(wszVal); if (Property.szValue) { hr = HrGetRequestUriA(pszUrl, INTERNET_MAX_URL_LENGTH, szUri); if (SUCCEEDED(hr)) { if (SubmitUpnpPropertyEvent(szUri, 0, 1, &Property)) { TraceTag(ttidUpdiag, "Successfully submitted event to %s.", psvc->szEvtUrl); } else { dwErr =1; TraceTag(ttidUpdiag, "Failed to submit event to %s! Error %d.", psvc->szEvtUrl, GetLastError()); } } else { dwErr =1; TraceTag(ttidUpdiag, "Failed to crack URL %s! Error %d.", psvc->szEvtUrl, GetLastError()); } delete [] Property.szValue; } else { dwErr =1; TraceTag(ttidUpdiag, "SzFromWsz (#2) failed"); } } else { dwErr =1; TraceTag(ttidUpdiag, "SzFromWsz (#1) failed"); } } else { dwErr =1; TraceTag(ttidUpdiag, "Failed to convert variable value to string. Error: %x.", hr); } return dwErr; } DWORD Do_Set(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_Set: set variable %s to constant %s", pOpData->szVariableName, pOpData->mszConstantList); DWORD dwError = 0; Assert(cArgs == 0); // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { HRESULT hr = HrSetVariantValue(&pRow->varValue, pOpData->mszConstantList); if (S_OK != hr) { dwError =1; } else { dwError = dwSubmitEvent(psvc, pRow); } } return dwError; }; DWORD Do_Assign(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_Assign: assign variable %s to argument %s", pOpData->szVariableName, rgArgs[0].szValue); DWORD dwError = 0; Assert(cArgs == 1); // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // check if the new value is within the range or is in the allowed value list if (*pRow->mszAllowedValueList) { TCHAR * pNextString = pRow->mszAllowedValueList; while (*pNextString && (lstrcmpi(rgArgs[0].szValue, pNextString) !=0)) { pNextString += lstrlen(pNextString); pNextString ++; } if (!*pNextString) { TraceTag(ttidUpdiag, "Do_Assign: new variable value is not in the allowed value list !"); dwError =1; } } else if (*pRow->szMin) { Assert(*pRow->szMax); // This should only work if the variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { long lMin = _ttol(pRow->szMin); long lMax = _ttol(pRow->szMax); long lVal = _ttol(rgArgs[0].szValue); if ((lVallMax)) { TraceTag(ttidUpdiag, "Do_Assign: new variable value is not in the allowed range !"); dwError =1; } } } if (!dwError) { HRESULT hr = HrSetVariantValue(&pRow->varValue, rgArgs[0].szValue); if (S_OK != hr) { dwError =1; } else { dwError = dwSubmitEvent(psvc, pRow); } } } return dwError; }; DWORD Do_Toggle(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_Toggle"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { Assert(pRow->varValue.vt == VT_BOOL); if (pRow->varValue.vt == VT_BOOL) { pRow->varValue.boolVal = ~pRow->varValue.boolVal; dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "Error: variable %s is not a boolean.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_Increment(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_Increment"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if teh variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { pRow->varValue.lVal++; dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_Decrement(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_Decrement"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if teh variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { pRow->varValue.lVal--; dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_IncrementWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_IncrementWrap"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { // get the max and min values from the constant list TCHAR * szMax = pOpData->mszConstantList; szMax += lstrlen(szMax)+1; long lMin = _ttol(pOpData->mszConstantList); long lMax = _ttol(szMax); Assert(lMax >= lMin); TraceTag(ttidUpdiag, "Do_IncrementWrap: variable= %d, min= %d, max= %d", pRow->varValue.lVal, lMin, lMax); pRow->varValue.lVal = lMin + ((pRow->varValue.lVal-lMin+1) % (lMax-lMin+1)); Assert((lMin<=pRow->varValue.lVal) && (lMax>=pRow->varValue.lVal)); dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_DecrementWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_DecrementWrap"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { // get the max and min values from the constant list TCHAR * szMax = pOpData->mszConstantList; szMax += lstrlen(szMax)+1; long lMin = _ttol(pOpData->mszConstantList); long lMax = _ttol(szMax); Assert(lMax >= lMin); TraceTag(ttidUpdiag, "Do_DecrementWrap: variable= %d, min= %d, max= %d", pRow->varValue.lVal, lMin, lMax); pRow->varValue.lVal = lMax - ((lMax-pRow->varValue.lVal+1) % (lMax-lMin+1)); Assert((lMin<=pRow->varValue.lVal) && (lMax>=pRow->varValue.lVal)); dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_IncrementBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_IncrementBounded"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { // max value is the only constant long lMax = _ttol(pOpData->mszConstantList); if (pRow->varValue.lVal < lMax) { pRow->varValue.lVal++; dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "IncrementBounded: variable %s already has the maximum value.", pOpData->szVariableName); } } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD Do_DecrementBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_DecrementBounded"); DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a number ?? Assert(pRow->varValue.vt == VT_I4); if (pRow->varValue.vt == VT_I4) { // max value is the only constant long lMin = _ttol(pOpData->mszConstantList); if (pRow->varValue.lVal > lMin) { pRow->varValue.lVal--; dwError = dwSubmitEvent(psvc, pRow); } else { TraceTag(ttidUpdiag, "DecrementBounded: variable %s already has the minimal value.", pOpData->szVariableName); } } else { TraceTag(ttidUpdiag, "Error: variable %s is not a number.", pOpData->szVariableName); dwError =1; } } return dwError; }; DWORD dwMoveToNextString(UPNPSVC * psvc, OPERATION_DATA * pOpData, BOOL fWrap) { DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a string // and we have a list of allowed values Assert(pRow->varValue.vt == VT_BSTR); if (pRow->varValue.vt == VT_BSTR) { if (lstrlen(pRow->mszAllowedValueList)>0) { TCHAR * pNextString = pRow->mszAllowedValueList; while (*pNextString && (lstrcmpi(TszFromWsz(pRow->varValue.bstrVal), pNextString) !=0)) { pNextString += lstrlen(pNextString); pNextString ++; } if (!*pNextString) { TraceTag(ttidUpdiag, "dwSetToNextString: variable value is not in the allowed value list ??"); dwError =1; } else { // is pNextString the last string in the list ? TCHAR * pChar = pNextString + lstrlen(pNextString); pChar++; if (*pChar) { // not the last string V_BSTR(&pRow->varValue) = SysAllocString(WszFromTsz(pChar)); dwError = dwSubmitEvent(psvc, pRow); } else if (fWrap) { V_BSTR(&pRow->varValue) = SysAllocString(WszFromTsz(pRow->mszAllowedValueList)); dwError = dwSubmitEvent(psvc, pRow); } } } else { TraceTag(ttidUpdiag, "dwSetToNextString: variable %s has no list of allowed values.", pOpData->szVariableName); dwError =1; } } else { TraceTag(ttidUpdiag, "dwSetToNextString: variable %s is not a string.", pOpData->szVariableName); dwError =1; } } return dwError; } DWORD dwMoveToPrevString(UPNPSVC * psvc, OPERATION_DATA * pOpData, BOOL fWrap) { DWORD dwError = 0; // find the SST row to update SST_ROW * pRow = FindSSTRowByVarName(&psvc->sst, pOpData->szVariableName); Assert(pRow); if (pRow) { // This should only work if the variable is a string // and we have a list of allowed values Assert(pRow->varValue.vt == VT_BSTR); if (pRow->varValue.vt == VT_BSTR) { if (lstrlen(pRow->mszAllowedValueList)>0) { TCHAR * pNextString = pRow->mszAllowedValueList; TCHAR * pPrevString = pNextString; while (*pNextString && (lstrcmpi(TszFromWsz(pRow->varValue.bstrVal), pNextString) !=0)) { if (pPrevString != pNextString) { pPrevString = pNextString; } pNextString += lstrlen(pNextString); pNextString++; } if (!pNextString) { TraceTag(ttidUpdiag, "dwSetToNextString: variable value is not in the allowed value list ??"); dwError =1; } else { // is pNextString the first string in the list ? if (pNextString != pPrevString) { // not the first string V_BSTR(&pRow->varValue) = SysAllocString(WszFromTsz(pPrevString)); dwError = dwSubmitEvent(psvc, pRow); } else if (fWrap) { // go to the last string pNextString += lstrlen(pNextString); pNextString++; while (*pNextString) { pPrevString = pNextString; pNextString += lstrlen(pNextString); pNextString++; } V_BSTR(&pRow->varValue) = SysAllocString(WszFromTsz(pPrevString)); dwError = dwSubmitEvent(psvc, pRow); } } } else { TraceTag(ttidUpdiag, "dwMoveToPrevString: variable %s has no list of allowed values.", pOpData->szVariableName); dwError =1; } } else { TraceTag(ttidUpdiag, "dwMoveToPrevString: variable %s is not a string.", pOpData->szVariableName); dwError =1; } } return dwError; } DWORD Do_NextStringWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_NextStringWrap"); return dwMoveToNextString(psvc, pOpData, TRUE); } DWORD Do_PrevStringWrap(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_PrevStringWrap"); return dwMoveToPrevString(psvc, pOpData, TRUE); } DWORD Do_NextStringBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_NextStringBounded"); return dwMoveToNextString(psvc, pOpData, FALSE); } DWORD Do_PrevStringBounded(UPNPSVC * psvc, OPERATION_DATA * pOpData, DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Do_PrevStringBounded"); return dwMoveToPrevString(psvc, pOpData, FALSE); }