//////////////////////////////////////////////////////////////////////////////////// // // File: globals.cpp // // History: 16-Nov-00 markder Created. // // Desc: This file contains miscellaneous helper functions. // //////////////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include //////////////////////////////////////////////////////////////////////////////////// // // Func: PrintError, PrintErrorStack, Print // // Desc: Helper functions for console output // //////////////////////////////////////////////////////////////////////////////////// TCHAR gszT[1024]; // Global buffer for console output void _cdecl PrintError( LPCTSTR pszFmt, ...) { va_list arglist; va_start(arglist, pszFmt); StringCchVPrintf(gszT, ARRAYSIZE(gszT), pszFmt, arglist); gszT[1023] = _T('\0'); // ensure null termination va_end(arglist); _tprintf(_T("\nNMAKE : U8604: 'ShimDBC': %s"), gszT); } void PrintErrorStack() { CString csError; INT_PTR i, j; Print(_T("\n\nErrors were encountered during compilation:\n")); for (i = g_rgErrors.GetSize() - 1; i >= 0; i--) { csError.Empty(); j = g_rgErrors.GetSize() - i; while(--j) { csError += _T(" "); } csError += g_rgErrors[i]; csError += _T("\n"); PrintError(csError); } } void _cdecl Print( LPCTSTR pszFmt, ...) { va_list arglist; if (g_bQuiet) return; va_start(arglist, pszFmt); StringCchVPrintf(gszT, ARRAYSIZE(gszT), pszFmt, arglist); gszT[1023] = _T('\0'); // ensure null termination va_end(arglist); _tprintf(_T("%s"), gszT); } //////////////////////////////////////////////////////////////////////////////////// // // Func: StringToDword // // Desc: Converts a string to a DWORD. Handles the 0x prefix for hex strings. // DWORD StringToDword( CString cs) { DWORD dwRet; cs.MakeLower(); if (cs.Left(2) == _T("0x")) { _stscanf(cs, _T("0x%x"), &dwRet); } else { dwRet = _ttol(cs); } return dwRet; } //////////////////////////////////////////////////////////////////////////////////// // // Func: StringToULong // // Desc: Converts a string to a unsigned long. // ULONG StringToULong( LPCTSTR lpszVal) { TCHAR* pEnd; return _tcstoul(lpszVal, &pEnd, 0); } //////////////////////////////////////////////////////////////////////////////////// // // Func: StringToMask // // Desc: Converts a string to a mask, with some checking // BOOL StringToMask( LPDWORD pdwMask, LPCTSTR lpszVal ) { DWORD dwMask = 0; LPCTSTR pVal; BOOL bSuccess; TCHAR* pEnd = NULL; pVal = lpszVal + _tcsspn(lpszVal, _T(" \t")); dwMask = (DWORD)_tcstoul(pVal, &pEnd, 0); if (dwMask == 0) { // suspicious, possibly check errno if (errno != 0) { goto errHandle; } } // // if a mask is ending with some garbage -- it's an error // if (pEnd && *pEnd != _T('\0') && !_istspace(*pEnd)) { goto errHandle; } if (pdwMask) { *pdwMask = dwMask; } return TRUE; errHandle: SDBERROR_FORMAT((_T("Failed to parse \"%s\"\n"), lpszVal)); return FALSE; } //////////////////////////////////////////////////////////////////////////////////// // // Func: StringToQword // // Desc: Converts a string to a 64-bit ULONGLONG (aka QWORD). Handles the 0x // prefix for hex strings. // ULONGLONG StringToQword( CString cs) { ULONGLONG ullRet; cs.MakeLower(); if (cs.Left(2) == _T("0x")) { _stscanf(cs, _T("0x%I64x"), &ullRet); } else { ullRet = _ttoi64(cs); } return ullRet; } //////////////////////////////////////////////////////////////////////////////////// // // Func: VersionToQword // // Desc: Converts a version string to a 64-bit ULONGLONG (aka QWORD). // Version strings are of the form xx.xx.xx.xx, where each of // numbers is turned into a word and combined into a single // quad word. If a portion of the version string is a * or is // missing, it is stored as 0xFFFF. // BOOL VersionToQword( LPCTSTR lpszVersion, ULONGLONG* pullRet ) { BOOL bSuccess = FALSE; ULONG ulPart; LPTSTR pEnd = NULL; int i; *pullRet = 0; for (i = 0; i < 4; i++) { ulPart = (WORD)0xFFFF; // // skip whitespace // lpszVersion += _tcsspn(lpszVersion, _T(" \t")); if (*lpszVersion == _T('*')) { // // we expect to see either *\0 or *.xxx // so move past * // pEnd = (LPTSTR)(lpszVersion + 1); } else { // // not a wildcard - if we have not reached the end of the string, // keep parsing numbers // if (*lpszVersion) { pEnd = NULL; ulPart = _tcstol(lpszVersion, &pEnd, 0); // // check to see that the part was converted properly // if (pEnd == NULL) { SDBERROR_FORMAT((_T("Internal error, failed to parse \"%s\"\n"), lpszVersion)); goto eh; } } } if (pEnd == NULL) { break; } // // skip whitespace first // pEnd += _tcsspn(pEnd, _T(" \t")); // // at this point we should be at the end of // the string OR at the '.' // if (*pEnd && *pEnd != _T('.')) { SDBERROR_FORMAT((_T("Bad version specification, parsing stopped at \"%s\"\n"), pEnd)); goto eh; } lpszVersion = (*pEnd == _T('.') ? pEnd + 1 : pEnd); *pullRet = (*pullRet << 16) | ((WORD)ulPart); } bSuccess = TRUE; eh: return bSuccess; } BOOL VersionQwordToString( OUT CString& rString, ULONGLONG ullVersion ) { // we do conversion to string int i; WORD wPart; CString csPart; ULONGLONG ullMask = (((ULONGLONG)0xFFFF) << 48); ULONGLONG ullPart; rString.Empty(); for (i = 0; i < 4; ++i) { ullPart = ullVersion & ullMask; ullVersion = (ullVersion << 16) | (WORD)0xFFFF; // // get the part into the lower portion // wPart = (WORD)(ullPart >> 48); if (wPart == (WORD)0xFFFF) { csPart = _T('*'); } else { csPart.Format(_T("%hu"), wPart); } if (i > 0) { rString += _T('.'); } rString += csPart; if (ullVersion == (ULONGLONG)-1) { break; } } return TRUE; } //////////////////////////////////////////////////////////////////////////////////// // // Func: TrimParagraph // // Desc: Trims extra whitespace from a string // // CString TrimParagraph(CString csInput) { CString csOutput; long i; // Expand CString's buffer to size of input csOutput.GetBuffer(csInput.GetLength()); csOutput.ReleaseBuffer(); for (i = 0; i < csInput.GetLength(); i++) { TCHAR c = csInput.GetAt(i); if (_istspace(c)) { if (csOutput.GetLength() == 0) continue; if (csOutput.Mid(csOutput.GetLength() - 1) == _T(' ') || csOutput.Mid(csOutput.GetLength() - 4, 4) == _T("BR/>") || csOutput.Mid(csOutput.GetLength() - 3, 3) == _T("P/>")) continue; csOutput += _T(' '); continue; } if (csInput.Left(3) == _T(" tag // csOutput.TrimRight(); } csOutput += c; } csOutput.TrimLeft(); csOutput.TrimRight(); return csOutput; } //////////////////////////////////////////////////////////////////////////////////// // // Func: ReplaceStringNoCase // // Desc: Replaces all instances of lpszFindThis with lpszReplaceWithThis // within csText (case insensitive). // VOID ReplaceStringNoCase(CString& csText, LPCTSTR lpszFindThis, LPCTSTR lpszReplaceWithThis) { LPTSTR lpszBuffer; LPTSTR lpszFind; if (0 == csText.GetLength()) { return; } CString strFindNoCase(lpszFindThis); lpszBuffer = csText.GetBuffer(csText.GetLength()); strFindNoCase.MakeUpper(); do { lpszFind = StrStrI(lpszBuffer, strFindNoCase); if (NULL != lpszFind) { memcpy(lpszFind, (LPCTSTR)strFindNoCase, strFindNoCase.GetLength() * sizeof(*lpszFind)); lpszBuffer = lpszFind + strFindNoCase.GetLength(); } } while (NULL != lpszFind); // now that all the instances of the lpszFindThis had been replaced with // the upper-cased version... do a regular replace csText.ReleaseBuffer(); csText.Replace(strFindNoCase, lpszReplaceWithThis); } //////////////////////////////////////////////////////////////////////////////////// // // Func: MakeFullPath // // Desc: Creates a full path from a (possible) relative one. Uses // GetCurrentDirectory to prepend the passed in string. // CString MakeFullPath(CString cs) { CString csNewPath; DWORD dwCurDirSize; LPTSTR lpszCurDir; return cs; #if 0 // // Check if it's already a full path // if (cs.Mid(1, 1) == _T(":") || cs.Left(2) == _T("\\\\")) { // // This is either a UNC full path or a DOS full path. // Drop out. // return cs; } dwCurDirSize = GetCurrentDirectory(0, NULL); lpszCurDir = csNewPath.GetBuffer(dwCurDirSize); if (0 == GetCurrentDirectory(dwCurDirSize, lpszCurDir)) { // // Something really weird happened. Not sure how to error out. // return cs; } csNewPath.ReleaseBuffer(); if (csNewPath.Right(1) != _T("\\")) { csNewPath += _T("\\"); } csNewPath += cs; return csNewPath; #endif } //////////////////////////////////////////////////////////////////////////////////// // // Func: GetByteStringSize // // Desc: Parses a 'byte string' (used in declarations) to determine // how many bytes it contains. // DWORD GetByteStringSize( CString csBytes) { DWORD dwByteCount = 0; BOOL bOnByte = FALSE; csBytes.MakeUpper(); for (long i = 0; i < csBytes.GetLength(); i++) { if (_istxdigit(csBytes.GetAt(i))) { if (!bOnByte) { dwByteCount++; bOnByte = TRUE; } } else if (_istspace(csBytes.GetAt(i))) { bOnByte = FALSE; } else { SDBERROR_FORMAT((_T("Unrecognized byte character '%c' in block:\n%s\n"), csBytes.GetAt(i), ((LPCTSTR)csBytes)+i)); return 0xFFFFFFFF; } } return dwByteCount; } //////////////////////////////////////////////////////////////////////////////////// // // Func: GetBytesFromString // // Desc: Parses a 'byte string' (used in declarations) into an actual // memory block. // DWORD GetBytesFromString( CString csBytes, BYTE* pBuffer, DWORD dwBufferSize) { csBytes.MakeUpper(); CString csByte; DWORD dwRequiredBufferSize; LONG nFirstByteChar = -1; DWORD dwBufferCursor = 0; DWORD dwByte; dwRequiredBufferSize = GetByteStringSize(csBytes); if (dwRequiredBufferSize < dwBufferSize || dwRequiredBufferSize == 0xFFFFFFFF) { return dwRequiredBufferSize; } for (long i = 0; i < csBytes.GetLength() + 1; i++) { if (_istxdigit(csBytes.GetAt(i))) { if (nFirstByteChar == -1) { nFirstByteChar = i; } } else if (_istspace(csBytes.GetAt(i)) || csBytes.GetAt(i) == _T('\0')) { if (nFirstByteChar != -1) { csByte = csBytes.Mid(nFirstByteChar, i - nFirstByteChar); _stscanf(csByte, _T("%x"), &dwByte); memcpy(pBuffer + dwBufferCursor++, &dwByte, sizeof(BYTE)); } nFirstByteChar = -1; } else { SDBERROR_FORMAT((_T("Unrecognized byte character '%c' in block:\n%s\n"), csBytes.GetAt(i), ((LPCTSTR)csBytes)+i)); return 0xFFFFFFFF; } } return dwRequiredBufferSize; } //////////////////////////////////////////////////////////////////////////////////// // // Func: DecodeString // // Desc: Decodes a list of strings with flags // // [00] [00] [00] [00] // ^ Arch1 Arch2 Arch3 // |- flags // // syntax for the runtime platform: // [!] ( [!] STR1 [; STR2 ] ... ) // example: // !STR1 - will evaluate to TRUE when the the string is NOT STR1 // !(STR1; STR2) - will evaluate to TRUE when the string doesn't contain STR1 or STR2 // BOOL DecodeString(LPCTSTR pszStr, LPDWORD pdwMask, PFNGETSTRINGMASK pfnGetStringMask) { BOOL bNot = FALSE; BOOL bBracket = FALSE; LPTSTR pEnd; TCHAR chSave; DWORD dwElement; BOOL bNotElement; DWORD dwMask = 0; INT nElement = 0; BOOL bSuccess = FALSE; pszStr += _tcsspn(pszStr, _T(" \t")); // // Got the first char // if (*pszStr == _T('!')) { // // Peek ahead and see whether we have a bracket // pEnd = (LPTSTR)(pszStr + 1); pEnd += _tcsspn(pEnd, _T(" \t")); if (*pEnd == '(') { // global not bNot = TRUE; pszStr = pEnd; } else { // local NOT -- so jump to parsing it goto ParseStart; } } if (*pszStr == _T('(')) { // bracket, we need to find closing one too ++pszStr; bBracket = TRUE; } ParseStart: do { dwElement = 0; pszStr += _tcsspn(pszStr, _T(" ;,\t")); if (*pszStr == _T('\0') || *pszStr == _T(')')) { break; } bNotElement = (*pszStr == _T('!')); if (bNotElement) { pszStr++; } // find the end of this token pEnd = _tcspbrk(pszStr, _T(" \t;,)")); if (pEnd != NULL) { chSave = *pEnd; *pEnd = _T('\0'); } dwElement = (*pfnGetStringMask)(pszStr); if (pEnd) { *pEnd = chSave; } if (dwElement == OS_SKU_NONE) { goto HandleError; } if (bNotElement) { dwElement ^= 0xFFFFFFFF; } dwMask |= dwElement; pszStr = pEnd; } while (pEnd); // when pEnd == NULL -- it was the last token if (bBracket && (!pszStr || *pszStr != ')')) { // we expected a bracket here goto HandleError; } if (bNot) { dwMask ^= 0xFFFFFFFF; } *pdwMask = dwMask; bSuccess = TRUE; HandleError: if (!bSuccess) { SDBERROR_FORMAT((_T("Failed to decode \"%s\"\n"), pszStr)); } return bSuccess; } //////////////////////////////////////////////////////////////////////////////////// // // Func: DecodeRuntimePlatformString // // Desc: Decodes a list of Platform Strings with flags // // [00] [00] [00] [00] // ^ Arch1 Arch2 Arch3 // |- flags // // syntax for the runtime platform: // [!] ( [!] Platform1 [; Platform2 ] ... ) // example: // !(IA64; !X86) - will evaluate to TRUE when it's NOT IA64 (native) and X86 // (IA3264; X86) - will evaluate to TRUE when it's running on X86 or 32-bit subsystem on IA64 // (AMD64; IA3264) - will evaluate to TRUE when it's running on AMD64 or 32-bit subsystem on ia64 // BOOL DecodeRuntimePlatformString(LPCTSTR pszPlatform, LPDWORD pdwRuntimePlatform) { BOOL bNot = FALSE; BOOL bBracket = FALSE; LPTSTR pEnd; TCHAR chSave; DWORD dwElement; DWORD dwNotElementFlag; DWORD dwRuntimePlatform = 0; INT nElement = 0; BOOL bSuccess = FALSE; pszPlatform += _tcsspn(pszPlatform, _T(" \t")); // got the first char if (*pszPlatform == _T('!')) { // peek ahead and see whether we have a bracket pEnd = (LPTSTR)(pszPlatform + 1); pEnd += _tcsspn(pEnd, _T(" \t")); if (*pEnd == '(') { // global not bNot = TRUE; pszPlatform = pEnd; } else { // local NOT -- so jump to parsing it goto ParseStart; } } if (*pszPlatform == _T('(')) { // bracket, we need to find closing one too ++pszPlatform; bBracket = TRUE; } ParseStart: do { dwElement = 0; pszPlatform += _tcsspn(pszPlatform, _T(" ;,\t")); if (*pszPlatform == _T('\0') || *pszPlatform == _T(')')) { break; } dwNotElementFlag = (*pszPlatform == _T('!')) ? RUNTIME_PLATFORM_FLAG_NOT_ELEMENT : 0; // find the end of this token pEnd = _tcspbrk(pszPlatform, _T(" \t;,)")); if (pEnd != NULL) { chSave = *pEnd; *pEnd = _T('\0'); } dwElement = GetRuntimePlatformType(pszPlatform); if (pEnd) { *pEnd = chSave; } if (dwElement == PROCESSOR_ARCHITECTURE_UNKNOWN) { goto HandleError; } dwElement |= dwNotElementFlag | RUNTIME_PLATFORM_FLAG_VALID; if (nElement >= 3) { goto HandleError; } // now shift dwElement <<= (nElement * 8); ++nElement; // on to the next element dwRuntimePlatform |= dwElement; pszPlatform = pEnd; } while(pEnd); // when pEnd == NULL -- it was the last token if (bBracket && (!pszPlatform || *pszPlatform != ')')) { // we expected a bracket here goto HandleError; } if (bNot && nElement > 1) { dwRuntimePlatform |= RUNTIME_PLATFORM_FLAG_NOT; } *pdwRuntimePlatform = dwRuntimePlatform; bSuccess = TRUE; HandleError: return bSuccess; } //////////////////////////////////////////////////////////////////////////////////// // // Func: ReadName // // Desc: Wrapper to read the name attribute from an XML node. // BOOL ReadName( IXMLDOMNode* pNode, CString* pcsName) { BOOL bSuccess = FALSE; if (!GetAttribute(_T("NAME"), pNode, pcsName)) { SDBERROR_FORMAT((_T("NAME attribute required:\n%s\n\n"), GetXML(pNode))); goto eh; } bSuccess = TRUE; eh: return TRUE; } BOOL ReadLangID(IXMLDOMNode* pNode, SdbDatabase* pDB, CString* pcsLangID) { if (!GetAttribute(_T("LANGID"), pNode, pcsLangID)) { if (!pDB->m_csCurrentLangID.GetLength()) { SDBERROR_FORMAT(( _T("Tag requires LANGID attribute if there is no LANGID on the DATABASE node\n%s\n"), GetXML(pNode))); return FALSE; } *pcsLangID = pDB->m_csCurrentLangID; } return TRUE; } BOOL FilterOSVersion(DOUBLE flOSVersion, CString csOSVersionSpec, LPDWORD lpdwSPMask) { DOUBLE flVerXML; CString csTemp; long nBeg, i, nIndSP; int nSPVersion; TCHAR chSP; BOOL bFilter = TRUE; DWORD dwSPMask; if (flOSVersion == 0.0 || csOSVersionSpec.IsEmpty()) { *lpdwSPMask = 0xFFFFFFFF; return FALSE; } *lpdwSPMask = 0; nBeg = 0; for (i = 0; i <= csOSVersionSpec.GetLength(); i++) { if (csOSVersionSpec.GetAt(i) == _T('\0') || csOSVersionSpec.GetAt(i) == _T(';')) { csTemp = csOSVersionSpec.Mid(nBeg, i - nBeg); nBeg = i + 1; if (csTemp.GetLength() == 0) { continue; } dwSPMask = 0xFFFFFFFF; nSPVersion = -1; nIndSP = -1; if ((nIndSP = csTemp.Find('.')) != -1) { if ((nIndSP = csTemp.Find('.', nIndSP + 1)) != -1) { CString csSP = csTemp.Right(csTemp.GetLength() - nIndSP - 1); chSP = csTemp.GetAt(nIndSP); csTemp.SetAt(nIndSP, 0); nSPVersion = _ttoi(csSP); } } if (csTemp.Left(2) == _T("gt")) { if (csTemp.Left(3) == _T("gte")) { flVerXML = _tcstod(csTemp.Right(csTemp.GetLength() - 3), NULL); if (flOSVersion >= flVerXML) { bFilter = FALSE; } if (nSPVersion != -1 && flOSVersion == flVerXML) { dwSPMask = 0xFFFFFFFF - (1 << nSPVersion) + 1; } } else { flVerXML = _tcstod(csTemp.Right(csTemp.GetLength() - 2), NULL); if (flOSVersion > flVerXML) { bFilter = FALSE; } if (nSPVersion != -1 && flOSVersion == flVerXML) { bFilter = FALSE; dwSPMask = 0xFFFFFFFF - (1 << (nSPVersion + 1)) + 1; } } } else if (csTemp.Left(2) == _T("lt")) { if (csTemp.Left(3) == _T("lte")) { flVerXML = _tcstod(csTemp.Right(csTemp.GetLength() - 3), NULL); if (flOSVersion <= flVerXML) { bFilter = FALSE; } if (nSPVersion != -1 && flOSVersion == flVerXML) { dwSPMask = 0xFFFFFFFF - (1 << (nSPVersion + 1)) + 1; dwSPMask ^= 0xFFFFFFFF; } } else { flVerXML = _tcstod(csTemp.Right(csTemp.GetLength() - 2), NULL); if (flOSVersion < flVerXML) { bFilter = FALSE; } if (nSPVersion != -1 && flOSVersion == flVerXML) { bFilter = FALSE; dwSPMask = 0xFFFFFFFF - (1 << nSPVersion) + 1; dwSPMask ^= 0xFFFFFFFF; } } } else { if (flOSVersion == _tcstod(csTemp, NULL)) { bFilter = FALSE; if (nSPVersion != -1) { dwSPMask = (1 << nSPVersion); } } } if (nIndSP != -1) { csTemp.SetAt(nIndSP, chSP); *lpdwSPMask |= dwSPMask; } } } if (*lpdwSPMask == 0) { *lpdwSPMask = 0xFFFFFFFF; } return bFilter; } VOID ExpandEnvStrings(CString* pcs) { LPTSTR lpszBuf; DWORD cchReqBufSize; CString cs(*pcs); cchReqBufSize = ExpandEnvironmentStrings(cs, NULL, 0); lpszBuf = pcs->GetBuffer(cchReqBufSize); ExpandEnvironmentStrings(cs, lpszBuf, cchReqBufSize); pcs->ReleaseBuffer(); } BOOL MakeUTCTime(CString& cs, time_t* pt) { BOOL bSuccess = FALSE; CString csTZ; // // Set TZ environment variable to override locale // settings so that date/time conversion routines // never do any localizations. // csTZ = _tgetenv(_T("TZ")); csTZ = _T("TZ=") + csTZ; _tputenv(_T("TZ=UTC0")); _tzset(); COleDateTime odt; SYSTEMTIME st; CTime time; if (!odt.ParseDateTime(cs)) { goto eh; } if (!odt.GetAsSystemTime(st)) { goto eh; } time = st; *pt = time.GetTime(); bSuccess = TRUE; eh: _tputenv(csTZ); _tzset(); return bSuccess; } BOOL ParseLanguageID(LPCTSTR pszLanguage, DWORD* pdwLanguageID) { LPCTSTR pch; LPTSTR pend = NULL; BOOL bSuccess = FALSE; BOOL bBracket = FALSE; DWORD dwLangID = 0; pch = _tcschr(pszLanguage, TEXT('[')); if (NULL != pch) { bBracket = TRUE; ++pch; } else { pch = pszLanguage; } while (_istspace(*pch)) { ++pch; } dwLangID = _tcstoul(pch, &pend, 0); if (dwLangID == 0) { goto cleanup; } if (pend != NULL) { bSuccess = bBracket ? (_istspace(*pend) || *pend == TEXT(']')) : (_istspace(*pend) || *pend == TEXT('\0')); } cleanup: if (bSuccess) { *pdwLanguageID = dwLangID; } return bSuccess; } BOOL ParseLanguagesString(CString csLanguages, CStringArray* prgLanguages) { BOOL bSuccess = FALSE, bExistsAlready = FALSE; int nLastSemicolon = -1, i, j; CString csLangID; for (i = 0; i <= csLanguages.GetLength(); i++) { if (csLanguages[i] == _T(';') || csLanguages[i] == _T('\0')) { csLangID = csLanguages.Mid(nLastSemicolon + 1, i - nLastSemicolon - 1); csLangID.TrimLeft(); csLangID.TrimRight(); csLangID.MakeUpper(); bExistsAlready = FALSE; for (j = 0; j < prgLanguages->GetSize(); j++) { if (prgLanguages->GetAt(j) == csLangID) { bExistsAlready = TRUE; break; } } if (!bExistsAlready) { prgLanguages->Add(csLangID); } nLastSemicolon = i; } } bSuccess = TRUE; return bSuccess; } CString GetGUID(REFGUID guid) { CString csRet; LPOLESTR lpszGUID = NULL; StringFromCLSID(guid, &lpszGUID); csRet = lpszGUID; CoTaskMemFree(lpszGUID); return csRet; } CString ProcessShimCmdLine( CString& csCommandLine, GUID& guidDB, TAGID tiShimRef ) { // // find whether we have anything to expand in csCommandLine // LPCTSTR pch; int nIndex; CString csNewCmdLine = csCommandLine; CString csToken; int nIndexStart = 0; int nIndexEnd; while (nIndexStart < csNewCmdLine.GetLength()) { nIndex = csNewCmdLine.Find(_T('%'), nIndexStart); if (nIndex < 0) { goto Done; } nIndexEnd = csNewCmdLine.Find(_T('%'), nIndex + 1); if (nIndexEnd < 0) { goto Done; } // // we matched a token, see whether it's something we're interested in // csToken = csNewCmdLine.Mid(nIndex + 1, nIndexEnd - nIndex - 1); if (0 == csToken.CompareNoCase(_T("DBINFO"))) { csToken.Format(_T("-d%ls -t0x%lx"), (LPCTSTR)GetGUID(guidDB), tiShimRef); // // replace the token with csToken // csNewCmdLine.Delete(nIndex, nIndexEnd - nIndex + 1); csNewCmdLine.Insert(nIndex, csToken); // // adjust our position for scanning // nIndexEnd = nIndex + csToken.GetLength() - 1; // one char before the end of this token } nIndexStart = nIndexEnd + 1; } Done: return csNewCmdLine; }