/*=================================================================== Microsoft Denali Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved. Component: MetaUtil object File: Utility.h Owner: t-BrianM This file contains implementation of the utility functions. ===================================================================*/ #include "stdafx.h" #include "MetaUtil.h" #include "MUtilObj.h" /*------------------------------------------------------------------ * U t i l i t i e s */ /*=================================================================== Report Error Sets up IErrorInfo. Does a simple FormatMessage and returns the correct HRESULT. Ripped from a-georgr's stuff. Parameters: hr HRESULT to return to caller dwErr Win32 error code to format message for Returns: hr ===================================================================*/ HRESULT ReportError(HRESULT hr, DWORD dwErr) { HLOCAL pMsgBuf = NULL; // If there's a message associated with this error, report that if (::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &pMsgBuf, 0, NULL) > 0) { AtlReportError(CLSID_MetaUtil, (LPCTSTR) pMsgBuf, IID_IMetaUtil, hr); } // TODO: add some error messages to the string resources and // return those, if FormatMessage doesn't return anything (not // all system errors have associated error messages). // Free the buffer, which was allocated by FormatMessage if (pMsgBuf != NULL) ::LocalFree(pMsgBuf); return hr; } // Report a Win32 error code HRESULT ReportError(DWORD dwErr) { return ::ReportError(HRESULT_FROM_WIN32(dwErr), dwErr); } // Report an HRESULT error HRESULT ReportError(HRESULT hr) { return ::ReportError(hr, (DWORD) hr); } /*=================================================================== Cannonize Key Converts a key into cannonical form. To do this it does the following: o Removes leading slashes o Converts back-slashes to forward-slashes CONSIDER: Resolve . and .. CONSIDER: Case conversion Parameters: tszKey [in, out] Key to cannonize Returns: Nothing ===================================================================*/ LPTSTR CannonizeKey(LPTSTR tszKey) { LPTSTR tszSrc; LPTSTR tszDest; tszSrc = tszKey; tszDest = tszKey; // Remove leading slashes while ((*tszSrc == _T('/')) || (*tszSrc == _T('\\'))) { tszSrc++; } // Convert slashes while (*tszSrc) { if (*tszSrc == _T('\\')) { *tszDest = _T('/'); } else { *tszDest = *tszSrc; } tszSrc++; tszDest++; } *tszDest = '\0'; return tszKey; } /*=================================================================== Split Key Splits a key path into parent and child parts. For example: tszKey = "/LM/Root/Path1/Path2/Path3" tszParent = "/LM/Root/Path1/Path2" tszChild = "Path3" Parameters: tszKey [in] Key to split tszParent [out] Parent part of key (allocated for ADMINDATA_MAX_NAME_LEN) tszChild [out] Child part of key (allocated for ADMINDATA_MAX_NAME_LEN) Returns: Nothing ===================================================================*/ void SplitKey(LPCTSTR tszKey, LPTSTR tszParent, LPTSTR tszChild) { ASSERT_STRING(tszKey); ASSERT(IsValidAddress(tszParent,ADMINDATA_MAX_NAME_LEN * sizeof(TCHAR), TRUE)); ASSERT(IsValidAddress(tszChild,ADMINDATA_MAX_NAME_LEN * sizeof(TCHAR), TRUE)); LPTSTR tszWork; // Copy the key to the parent _tcscpy(tszParent, tszKey); // Find the end of the parent tszWork = tszParent; while (*tszWork != _T('\0')) { tszWork++; } // Find the start of the child while ( (tszWork != tszParent) && (*tszWork != _T('/')) ) { tszWork--; } // Cut off and copy the child if (*tszWork == _T('/')) { // Multiple parts *tszWork = _T('\0'); tszWork++; _tcscpy(tszChild, tszWork); } else if (*tszWork != _T('\0')) { // One part _tcscpy(tszChild, tszWork); *tszWork = _T('\0'); } else { // No parts tszChild[0] = _T('\0'); } } /*=================================================================== Get Machine From Key Gets the machine name part of a full key path. Assumes that the machine name is the first component of the path. For example: tszKey = "/LM/Root/Path1/Path2/Path3" tszMachine = "LM" Parameters: tszKey [in] Full Key to get machine name of tszMachine [out] Machine name part of path (allocated for ADMINDATA_MAX_NAME_LEN) Returns: Nothing ===================================================================*/ void GetMachineFromKey(LPCTSTR tszFullKey, LPTSTR tszMachine) { ASSERT_STRING(tszFullKey); ASSERT(IsValidAddress(tszMachine, ADMINDATA_MAX_NAME_LEN * sizeof(TCHAR), TRUE)); int iSource; int iDest; iSource = 0; // Copy the machine name iDest = 0; while ((tszFullKey[iSource] != _T('/')) && (tszFullKey[iSource] != _T('\0'))) { tszMachine[iDest] = tszFullKey[iSource]; iSource++; iDest++; } // Cap it off with NULL tszMachine[iDest] = _T('\0'); } /*=================================================================== Key Is In Schema Determines if a full key path is or is part of the schema. For Example: TRUE "/Schema" "/Schema/Properties" "/Schema/Properties/Words" FALSE "" "/LM" "/LM/ROOT/Schema" "/LM/ROOT/Path/Schema" "/LM/ROOT/Path1/Path2" Parameters: tszKey [in] Key to evaluate Returns: TRUE if key is in the schema ===================================================================*/ BOOL KeyIsInSchema(LPCTSTR tszFullKey) { ASSERT_STRING(tszFullKey); LPTSTR tszWork; // Remove the const so I can play with the read pointer tszWork = const_cast (tszFullKey); // Skip the slash if (*tszWork != _T('\0') && *tszWork == _T('/')) { tszWork++; } // Check for "schema\0" or "schema/" if ((_tcsicmp(tszWork, _T("schema")) == 0) || (_tcsnicmp(tszWork, _T("schema/"), 7) == 0)) { return TRUE; } else { return FALSE; } } /*=================================================================== Key Is In IISAdmin Determines if a full key path is or is part of IISAdmin. For Example: TRUE "/LM/IISAdmin" "/SomeMachine/IISAdmin" "/LM/IISAdmin/Extensions" "/LM/IISAdmin/Extensions/DCOMCLSIDs" FALSE "" "/LM" "/LM/ROOT/IISAdmin" "/LM/ROOT/Path/IISAdmin" "/LM/ROOT/Path1/Path2" Parameters: tszKey [in] Key to evaluate Returns: TRUE if key is in IISAdmin ===================================================================*/ BOOL KeyIsInIISAdmin(LPCTSTR tszFullKey) { ASSERT_STRING(tszFullKey); LPTSTR tszWork; // Remove the const so I can play with the read pointer tszWork = const_cast (tszFullKey); // Skip leading slashes while (*tszWork == _T('/')) { tszWork++; } // Skip the machine name while ((*tszWork != _T('/')) && (*tszWork != _T('\0'))) { tszWork++; } // Skip the slash after the machine name if (*tszWork != '\0') { tszWork++; } // Check for "iisadmin\0" or "iisadmin/" if ((_tcsicmp(tszWork, _T("iisadmin")) == 0) || (_tcsnicmp(tszWork, _T("iisadmin/"), 8) == 0)) { return TRUE; } else { return FALSE; } } // // VariantResolveDispatch // Convert an IDispatch VARIANT to a (non-Dispatch) VARIANT by // invoking its default property until the object that remains // is not an IDispatch. If the original VARIANT is not an IDispatch // then the behavior is identical to VariantCopyInd(), with the // exception that arrays are copied. // // Parameters: // pVarOut - if successful, the return value is placed here // pVarIn - the variant to copy // GUID& riidObj - the calling interface (for error reporting) // nObjID - the Object's name from the resource file // // pVarOut need not be initialized. Since pVarOut is a new // variant, the caller must VariantClear this object. // // Returns: // The result of calling IDispatch::Invoke. (either NOERROR or // the error resulting from the call to Invoke) may also return // E_OUTOFMEMORY if an allocation fails // // This function always calls Exception() if an error occurs - // this is because we need to call Exception() if an IDispatch // method raises an exception. Instead of having the client // worry about whether we called Exception() on its behalf or // not, we always raise the exception. // HRESULT VariantResolveDispatch( VARIANT* pVarIn, VARIANT* pVarOut) { ASSERT(pVarIn != NULL && pVarOut != NULL); VariantInit(pVarOut); HRESULT hrCopy; if (V_VT(pVarIn) & VT_BYREF) hrCopy = VariantCopyInd(pVarOut, pVarIn); else hrCopy = VariantCopy(pVarOut, pVarIn); if (FAILED(hrCopy)) return ::ReportError(hrCopy); // Follow the IDispatch chain. while (V_VT(pVarOut) == VT_DISPATCH) { VARIANT varResolved; // value of IDispatch::Invoke DISPPARAMS dispParamsNoArgs = {NULL, NULL, 0, 0}; EXCEPINFO ExcepInfo; HRESULT hrInvoke; // If the variant is equal to Nothing, then it can be argued // with certainty that it does not have a default property! // hence we return DISP_E_MEMBERNOTFOUND for this case. if (V_DISPATCH(pVarOut) == NULL) hrInvoke = DISP_E_MEMBERNOTFOUND; else { VariantInit(&varResolved); hrInvoke = V_DISPATCH(pVarOut)->Invoke( DISPID_VALUE, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET | DISPATCH_METHOD, &dispParamsNoArgs, &varResolved, &ExcepInfo, NULL); } if (FAILED(hrInvoke)) { if (hrInvoke != DISP_E_EXCEPTION) hrInvoke = ::ReportError(hrInvoke); // for DISP_E_EXCEPTION, SetErrorInfo has already been called VariantClear(pVarOut); return hrInvoke; } // The correct code to restart the loop is: // // VariantClear(pVar) // VariantCopy(pVar, &varResolved); // VariantClear(&varResolved); // // however, the same affect can be achieved by: // // VariantClear(pVar) // *pVar = varResolved; // VariantInit(&varResolved) // // this avoids a copy. The equivalence rests in the fact that // *pVar will contain the pointers of varResolved, after we // trash varResolved (WITHOUT releasing strings or dispatch // pointers), so the net ref count is unchanged. For strings, // there is still only one pointer to the string. // // NOTE: the next iteration of the loop will do the VariantInit. VariantClear(pVarOut); *pVarOut = varResolved; } return S_OK; }