// // Util.cpp // #include "stdafx.h" #include "Util.h" #include "theapp.h" #include #include LPTSTR lstrchr(LPCTSTR pszString, TCHAR ch) { while (*pszString != _T('\0')) { if (*pszString == ch) return (LPTSTR)pszString; pszString = CharNext(pszString); } return NULL; } LPTSTR lstrdup(LPCTSTR psz) { LPTSTR pszResult = (LPTSTR)malloc((lstrlen(psz)+1) * sizeof(TCHAR)); if (pszResult != NULL) lstrcpy(pszResult, psz); return pszResult; } void ReplaceString(LPTSTR& pszTarget, LPCTSTR pszSource) { free(pszTarget); pszTarget = lstrdup(pszSource); } BOOL MyIsDigit(TCHAR ch) { return ((UINT)ch - (UINT)_T('0')) <= 9; } // A version of atoi that doesn't use the CRT int MyAtoi(LPCTSTR psz) { int result = 0; UINT digit; TCHAR chSign = *psz; if (*psz == _T('-') || *psz == _T('+')) psz += 1; while ((digit = (UINT)((int)*psz - (int)_T('0'))) <= 9) { result = (result * 10) + (int)digit; psz += 1; } if (chSign == _T('-')) result = -result; return result; } // CountChars // // Returns the number of times the given character appears in the // string. // // 2/03/1999 KenSh Created // int CountChars(LPCTSTR psz, TCHAR ch) { int count = 0; while (*psz != _T('\0')) { if (*psz == ch) count++; psz = CharNext(psz); } return count; } // GetFirstToken // // Copies the characters up to but not including the separator char, and // advances the source pointer to the character after the separator char. // Returns TRUE if a token was found, FALSE if not. // BOOL GetFirstToken(LPCTSTR& pszList, TCHAR chSeparator, LPTSTR pszBuf, int cchBuf) { if (pszList == NULL || *pszList == '\0') { *pszBuf = '\0'; return FALSE; } LPTSTR pchComma = lstrchr(pszList, chSeparator); int cchCopy; int cchSkip; if (pchComma == NULL) { cchCopy = lstrlen(pszList); cchSkip = cchCopy; } else { cchCopy = (int)(pchComma - pszList); cchSkip = cchCopy + 1; } cchCopy += 1; if (cchCopy > cchBuf) cchCopy = cchBuf; lstrcpyn(pszBuf, pszList, cchCopy); pszList += cchSkip; return TRUE; } // Use this function for initializing multiple DLL procs // pszFunction names is a series of null-separated proc names, followed by an extra null BOOL LoadDllFunctions(LPCTSTR pszDll, LPCSTR pszFunctionNames, FARPROC* prgFunctions) { UINT uPrevMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); HINSTANCE hInst = LoadLibrary(pszDll); SetErrorMode(uPrevMode); if (hInst == NULL) { ASSERT(FALSE); return FALSE; } while (*pszFunctionNames != '\0') { *prgFunctions = GetProcAddress(hInst, pszFunctionNames); if (*prgFunctions == NULL) { ASSERT(FALSE); return FALSE; } pszFunctionNames += (lstrlenA(pszFunctionNames) + 1); prgFunctions += 1; } return TRUE; } int MakePath(LPTSTR pszBuf, LPCTSTR pszFolder, LPCTSTR pszFileTitle) { lstrcpy(pszBuf, pszFolder); int cch = lstrlen(pszBuf); if (pszBuf[cch-1] != _T('\\')) pszBuf[cch++] = _T('\\'); lstrcpy(pszBuf + cch, pszFileTitle); return lstrlen(pszBuf); } // pszLinkTarget - where the link will point // pszDescription - link's description // pszFolderPath - path to folder to create file in or fully qualified file path to create // pszFileName - name of file to create in pszFolderPath or NULL to indicate pszFolderPath is already a file path // #ifndef NO_MAKELNKFILE HRESULT MakeLnkFile(CLSID clsid, LPCTSTR pszLinkTarget, LPCTSTR pszDescription, LPCTSTR pszFolderPath, LPCTSTR pszFileName) { HRESULT hresCoInit = CoInitialize(NULL); // we will create a COM object IUnknown *punk; HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hr)) { IShellLinkW * pslW; hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkW, &pslW)); if (SUCCEEDED(hr)) { //WCHAR szBuffer[MAX_PATH]; //SHTCharToUnicode(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer)); pslW->SetPath(pszLinkTarget); if (pszDescription) { //SHTCharToUnicode(pszDescription, szBuffer, ARRAYSIZE(szBuffer)); pslW->SetDescription(pszDescription); } pslW->Release(); } else { IShellLinkA * pslA; hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkA, &pslA)); if (SUCCEEDED(hr)) { char szBuffer[MAX_PATH]; SHTCharToAnsi(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer)); pslA->SetPath(szBuffer); if (pszDescription) { SHTCharToAnsi(pszDescription, szBuffer, ARRAYSIZE(szBuffer)); pslA->SetDescription(szBuffer); } pslA->Release(); } } if (SUCCEEDED(hr)) { IPersistFile *ppf; hr = punk->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); if (SUCCEEDED(hr)) { TCHAR szPath[MAX_PATH]; if (!pszFileName) { MakePath(szPath, pszFolderPath, pszFileName); pszFolderPath = szPath; } //WCHAR szFolderPath[MAX_PATH]; //SHTCharToUnicode(pszFolderPath, szFolderPath, ARRAYSIZE(szFolderPath)); hr = ppf->Save(pszFolderPath, TRUE); ppf->Release(); } } punk->Release(); } if (SUCCEEDED(hresCoInit)) CoUninitialize(); return hr; } #endif // FindPartialPath // // Returns a pointer to the file title preceded by nDepth levels of // directory names (zero == file title only). If the path has less than // nDepth levels, a pointer to the beginning of the string is returned. // NULL is never returned. // // 10/18/1996 KenSh Created // LPTSTR FindPartialPath(LPCTSTR pszFullPath, int nDepth) { #define MAX_SLASHES (MAX_PATH / 2) // No more slashes than this in the path LPTSTR pch; LPTSTR rgpchSlashes[MAX_SLASHES]; int cSlashes = 0; for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch)) { if (*pch == _T('\\') || *pch == _T('/')) { rgpchSlashes[cSlashes++] = pch; } } if (cSlashes > nDepth) { return rgpchSlashes[cSlashes-nDepth-1] + 1; } else { // Not enough slashes - return the full path return (LPTSTR)pszFullPath; } } // FindFileTitle // // Given a full pathname or URL, returns a pointer to the file title. If // the given does not contain path information, a pointer to the beginning // of the string is returned. NULL is never returned. // // 4/19/1996 KenSh Created // LPTSTR FindFileTitle(LPCTSTR pszFullPath) { LPTSTR pch; LPTSTR pchSlash = NULL; for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch)) { if (*pch == _T('\\') || *pch == _T('/')) pchSlash = pch; } if (pchSlash) return pchSlash+1; else return (LPTSTR)pszFullPath; } // FindExtension // // Given a path, returns a pointer to its file extension (the character // following the "."). If there is no extension, the return value is // a pointer to the end of the string ('\0' character). // // 3/04/1996 KenSh Created // 11/17/1997 KenSh Fixed case where path has "." but the filename doesn't // LPTSTR FindExtension(LPCTSTR pszFileName) { // Start with the file title LPTSTR pch = FindFileTitle(pszFileName); LPTSTR pchDot = NULL; // Find the last "." in the filename while (*pch) { if (*pch == _T('.')) pchDot = pch; pch = CharNext(pch); } if (pchDot) return pchDot+1; else return pch; // empty string } // IsFullPath // // Returns nonzero if the given path is a fully qualified path starting // with "X:\" or "\\" // // 5/19/1999 KenSh Created // BOOL IsFullPath(LPCTSTR pszPath) { if ((*pszPath == '\\' && *(pszPath+1) == '\\') || (*pszPath != '\0' && *(pszPath+1) == ':' && *(pszPath+2) == '\\')) { return TRUE; } else { return FALSE; } } void ShowDlgItem(HWND hwndDlg, int nCtrlID, int nCmdShow) { ShowWindow(GetDlgItem(hwndDlg, nCtrlID), nCmdShow); } // GetDlgItemRect // // Retrieves the bounding rect of the dialog item relative to the top left // corner of the dialog's client area. // // 10/13/1997 KenSh Created // HWND GetDlgItemRect(HWND hwndDlg, int nCtrlID, RECT* pRect) { ASSERT(IsWindow(hwndDlg)); ASSERT(pRect); HWND hwndCtrl = GetDlgItem(hwndDlg, nCtrlID); if (hwndCtrl != NULL) { POINT ptTopLeft; ptTopLeft.x = ptTopLeft.y = 0; ClientToScreen(hwndDlg, &ptTopLeft); GetWindowRect(hwndCtrl, pRect); OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y); } return hwndCtrl; } // GetRelativeRect // // Retrieves the bounding rect of the window relative to the top left // corner of its parent client area. // // 1/04/2000 KenSh Created // void GetRelativeRect(HWND hwndCtrl, RECT* pRect) { ASSERT(IsWindow(hwndCtrl)); ASSERT(pRect != NULL); HWND hwndParent = GetParent(hwndCtrl); POINT ptTopLeft = { 0, 0 }; ClientToScreen(hwndParent, &ptTopLeft); GetWindowRect(hwndCtrl, pRect); OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y); } // SetDlgItemRect // // Updates the position and size of a dialog item to the given rectangle, // in coordinates relative to the top left corner of the dialog's client area. // // 3/17/1999 KenSh Created // void SetDlgItemRect(HWND hwndDlg, int nCtrlID, CONST RECT* pRect) { ASSERT(IsWindow(hwndDlg)); ASSERT(GetDlgItem(hwndDlg, nCtrlID)); ASSERT(pRect); SetWindowPos(GetDlgItem(hwndDlg, nCtrlID), NULL, pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top, SWP_NOZORDER | SWP_NOACTIVATE); } // FormatDlgItemText // // Works like wsprintf to change the text of an existing dialog control. // If pszFormat is non-NULL, it contains the formatting string. // If pszFormat is NULL, then the existing control text is used as the // format string. // // 9/22/1999 KenSh Created // BOOL __cdecl FormatDlgItemText(HWND hwnd, int nCtrlID, LPCTSTR pszFormat, ...) { HWND hwndCtrl = GetDlgItem(hwnd, nCtrlID); if (NULL == hwndCtrl) return FALSE; va_list argList; va_start(argList, pszFormat); FormatWindowTextV(hwndCtrl, pszFormat, argList); return TRUE; } // FormatWindowTextV // // Combines the functionality of wvsprintf with SetWindowText, automatically // allocating a buffer large enough to hold the expanded string, and freeing // the buffer after setting the window text. // // 9/22/1999 KenSh Created // void FormatWindowTextV(HWND hwnd, LPCTSTR pszFormat, va_list argList) { LPTSTR pszWindowText = NULL; if (pszFormat == NULL) { int cchWindowText = GetWindowTextLength(hwnd) + 1; pszWindowText = (LPTSTR)malloc(cchWindowText * sizeof(TCHAR)); if (pszWindowText) { GetWindowText(hwnd, pszWindowText, cchWindowText); pszFormat = pszWindowText; } } if (pszFormat) { int cchNeeded = EstimateFormatLength(pszFormat, argList); LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR)); if (pszBuf) { #ifdef UNICODE wvnsprintf(pszBuf, cchNeeded, pszFormat, argList); #else wvsprintf(pszBuf, pszFormat, argList); #endif SetWindowText(hwnd, pszBuf); free(pszBuf); } } if (pszWindowText != NULL) { free(pszWindowText); } } LPTSTR __cdecl LoadStringFormat(HINSTANCE hInstance, UINT nStringID, ...) { LPTSTR pszBuf = NULL; LPTSTR pszFormat = LoadStringAlloc(hInstance, nStringID); if (pszFormat) { va_list argList; va_start(argList, nStringID); int cchNeeded = EstimateFormatLength(pszFormat, argList); LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR)); if (pszBuf) { #ifdef UNICODE wvnsprintf(pszBuf, cchNeeded, pszFormat, argList); #else wvsprintf(pszBuf, pszFormat, argList); #endif } free(pszFormat); } return pszBuf; } // EstimateFormatLength // // Estimates the number of characters needed to format the string, // including the terminating NULL. // // 9/22/1999 KenSh Created // int EstimateFormatLength(LPCTSTR pszFormat, va_list argList) { ASSERT(pszFormat != NULL); int cch = lstrlen(pszFormat) + 1; for (LPCTSTR pch = pszFormat; *pch; pch = CharNext(pch)) { if (*pch == _T('%')) { pch++; if (*pch == _T('-')) // we don't care about left vs. right justification pch++; if (*pch == _T('#')) // prefix hex numbers with 0x { pch++; cch += 2; } if (*pch == _T('0')) // pads with zeroes instead of spaces pch++; if (MyIsDigit(*pch)) { cch += MyAtoi(pch); // this overshoots but that's ok do { pch++; } while (MyIsDigit(*pch)); } switch (*pch) { case _T('s'): cch += lstrlen(va_arg(argList, LPCTSTR)) - 2; break; case _T('c'): case _T('C'): va_arg(argList, TCHAR); cch -= 1; break; case _T('d'): va_arg(argList, int); cch += INT_CCH_MAX - 2; break; case _T('h'): pch++; ASSERT(*pch == _T('d') || *pch == _T('u')); // other forms of 'h' not implemented! cch += SHORT_CCH_MAX - 2; break; case _T('l'): pch++; if (*pch == _T('d') || *pch == _T('i')) cch += LONG_CCH_MAX - 2; else if (*pch == _T('x') || *pch == _T('X')) cch += LONGX_CCH_MAX - 2; else ASSERT(FALSE); // other forms of 'l' not implemented! break; default: ASSERT(FALSE); // other break; } } } return cch; } // CenterWindow // // Centers the given window relative to its parent window. If the parent // is NULL, the window is centered over the desktop excluding the taskbar. // // 9/24/1999 KenSh Created // void CenterWindow(HWND hwnd) { RECT rcWindow; RECT rcDesktop; GetWindowRect(hwnd, &rcWindow); HWND hwndParent = GetParent(hwnd); if (hwndParent == NULL) { SystemParametersInfo(SPI_GETWORKAREA, sizeof(RECT), &rcDesktop, FALSE); } else { GetWindowRect(hwndParent, &rcDesktop); } int cxWindow = rcWindow.right - rcWindow.left; int cyWindow = rcWindow.bottom - rcWindow.top; int x = (rcDesktop.left + rcDesktop.right - cxWindow) / 2; int y = (rcDesktop.top + rcDesktop.bottom - cyWindow) / 2; SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } // FindResourceString // // Returns a pointer to the given string resource in memory, or NULL // if the string does not exist. Note that the string is in Unicode, // and is not NULL-terminated. // // 3/17/1999 KenSh Created // LPCWSTR FindResourceString(HINSTANCE hInstance, UINT nStringID, int* pcchString, WORD wLangID) { ASSERT(pcchString != NULL); *pcchString = 0; HRSRC hRsrc = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE((nStringID/16)+1), wLangID); if (hRsrc == NULL) return NULL; DWORD cbStringTable = SizeofResource(hInstance, hRsrc); HGLOBAL hGlb = LoadResource(hInstance, hRsrc); LPBYTE pbData = (LPBYTE)LockResource(hGlb); LPBYTE pbEnd = pbData + cbStringTable; // Skip strings preceding desired one int iString = (int)nStringID % 16; for (int i = 0; i < iString; i++) { int cch = (int)*((LPWORD)pbData); pbData += sizeof(WORD) + (sizeof(WCHAR) * cch); if (pbData >= pbEnd) return NULL; } if (pbData + sizeof(WORD) >= pbEnd) return NULL; *pcchString = (int)*((LPWORD)pbData); pbData += sizeof(WORD); return (LPCWSTR)pbData; } // GetResourceStringLength // // Finds the given string in the string table, and returns its length // in characters, not including room for the terminating NULL. // // History: // // 3/17/1999 KenSh Created // int GetResourceStringLength(HINSTANCE hInstance, UINT nStringID, WORD wLangID) { int cch; FindResourceString(hInstance, nStringID, &cch, wLangID); return cch; } // LoadStringHelper // // Helper function for LoadStringAllocEx. // // 2/23/1998 KenSh Created // 9/27/1999 KenSh changed alloc method from new[] to malloc // 12/21/1999 KenSh fixed unicode and DBCS bugs // int LoadStringHelper(HINSTANCE hInstance, UINT nID, LPTSTR* ppszBuf, int cchBuf, WORD wLangID) { int cch, cchCopy; LPCWSTR pwszString = FindResourceString(hInstance, nID, &cch, wLangID); if (pwszString == NULL) return 0; if (!(*ppszBuf)) { #ifdef UNICODE cchBuf = 1 + cch; #else cchBuf = 1 + WideCharToMultiByte(CP_ACP, 0, pwszString, cch, NULL, 0, NULL, NULL); #endif *ppszBuf = (LPTSTR)malloc(cchBuf * sizeof(TCHAR)); cchCopy = cch; } else { cchCopy = min(cchBuf-1, cch); } if (*ppszBuf) { #ifdef UNICODE CopyMemory(*ppszBuf, pwszString, cchCopy * sizeof(WCHAR)); (*ppszBuf)[cchCopy] = _T('\0'); #else cchCopy = WideCharToMultiByte(CP_ACP, 0, pwszString, cchCopy, *ppszBuf, cchBuf, NULL, NULL); (*ppszBuf)[cchCopy] = _T('\0'); #endif return cchCopy; } return 0; } // LoadStringAllocEx // // Finds the string resource with the given ID and language, allocates a // buffer using malloc, and copies the string to the buffer. If the // string is not found, NULL is returned. // // 2/24/1998 KenSh Created // LPTSTR LoadStringAllocEx(HINSTANCE hInstance, UINT nID, WORD wLangID) { LPTSTR psz = NULL; LoadStringHelper(hInstance, nID, &psz, 0, wLangID); return psz; } void TrimLeft(LPTSTR pszText) { LPTSTR pch2 = pszText; // will point to first non-space while (*pch2 == _T(' ')) pch2++; // If there's leading space, slide the string down if (pch2 != pszText) { // Note: it's safe to skip CharNext here, since '\0' is immune to DBCS while (_T('\0') != (*pszText++ = *pch2++)) NULL; } } void TrimRight(LPTSTR pszText) { LPTSTR pch2 = NULL; // will point to beginning of trailing space while (*pszText != _T('\0')) { if (*pszText == _T(' ')) { if (pch2 == NULL) pch2 = pszText; } else { // found more non-space, reset the trailing-space pointer pch2 = NULL; } pszText = CharNext(pszText); } // Truncate the trailing spaces, if any if (pch2 != NULL) *pch2 = _T('\0'); } // RegDeleteKeyAndSubKeys // // Does what RegDeleteKey should do. (Actually a single call to RegDeleteKey // will do this in Win95, but not in NT, according to the docs. Should see // if this gets fixed in NT5.) // // 2/24/1998 KenSh Created // DWORD RegDeleteKeyAndSubKeys(HKEY hkey, LPCTSTR pszSubKey) { #if 0 // This might be faster in Win95 than doing it manually, but bigger. OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) #endif { HKEY hSubKey; LONG err = RegOpenKeyEx(hkey, pszSubKey, 0, KEY_ALL_ACCESS, &hSubKey); if (ERROR_SUCCESS == err) { DWORD dwNumSubKeys; RegQueryInfoKey(hSubKey, NULL, NULL, NULL, &dwNumSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); for (DWORD iSubKey = dwNumSubKeys; iSubKey > 0; iSubKey--) { TCHAR szSubKey[260]; DWORD cchSubKey = _countof(szSubKey); if (ERROR_SUCCESS == RegEnumKeyEx(hSubKey, iSubKey-1, szSubKey, &cchSubKey, NULL, NULL, NULL, NULL)) { RegDeleteKeyAndSubKeys(hSubKey, szSubKey); } } RegCloseKey(hSubKey); } } return RegDeleteKey(hkey, pszSubKey); } // LoadFile // // Loads the file and null-terminates the copy in memory. The memory // is allocated via malloc(). // // 4/05/1996 KenSh Created // 8/27/1996 KenSh Improved error checking // 4/21/1997 KenSh Tightened up a bit // 2/01/1998 KenSh Append a null-terminating byte // 9/29/1999 KenSh use malloc instead of new [] // LPBYTE LoadFile(LPCTSTR pszFileName, DWORD* pdwFileSize /*=NULL*/) { HANDLE hFile; LPBYTE pData = NULL; DWORD dwFileSize = 0; hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (hFile == INVALID_HANDLE_VALUE) goto done; dwFileSize = GetFileSize(hFile, NULL); ASSERT(dwFileSize != 0xFFFFFFFF); // this shouldn't ever happen for valid hFile pData = (LPBYTE)malloc(dwFileSize + 1); if (!pData) goto done; DWORD cbRead; if (!ReadFile(hFile, pData, dwFileSize, &cbRead, NULL)) { free(pData); pData = NULL; goto done; } pData[dwFileSize] = 0; done: if (pdwFileSize) *pdwFileSize = dwFileSize; if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); return pData; } // DrawHollowRect // // Draws a hollow rectangle in the current background color. // // 2/06/1998 KenSh Created // void DrawHollowRect(HDC hdc, const RECT* pRect, int cxLeft, int cyTop, int cxRight, int cyBottom) { RECT rcCopy; RECT rcNewCoords; int i; CopyRect(&rcCopy, pRect); SetRect(&rcNewCoords, pRect->right - cxRight, pRect->bottom - cyBottom, pRect->left + cxLeft, pRect->top + cyTop); // Do each side in turn : right, bottom, left, top for (i = 0; i < 4; i++) { LONG coordSave = ((LONG*)&rcCopy)[i]; ((LONG*)&rcCopy)[i] = ((LONG*)&rcNewCoords)[i]; DrawFastRect(hdc, &rcCopy); ((LONG*)&rcCopy)[i] = coordSave; } } void DrawFastRect(HDC hdc, const RECT* pRect) { COLORREF crTextSave = SetTextColor(hdc, GetBkColor(hdc)); ExtTextOut(hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, pRect, TEXT(" "), 1, NULL); SetTextColor(hdc, crTextSave); } int GetFontHeight(HFONT hFont) { HDC hdcT = GetDC(NULL); HFONT hFontSave = (HFONT)SelectObject(hdcT, hFont); TEXTMETRIC tm; GetTextMetrics(hdcT, &tm); SelectObject(hdcT, hFontSave); ReleaseDC(NULL, hdcT); return tm.tmHeight; } HRESULT MyGetSpecialFolderPath(int nFolder, LPTSTR pszPath) { LPITEMIDLIST pidl; HRESULT hr; if (SUCCEEDED(hr = SHGetSpecialFolderLocation(NULL, nFolder, &pidl))) { hr = SHGetPathFromIDList(pidl, pszPath) ? S_OK : E_FAIL; LPMALLOC pMalloc; if (SUCCEEDED(SHGetMalloc(&pMalloc))) { pMalloc->Free(pidl); pMalloc->Release(); } } return hr; }