Sweden-Number/dlls/shlwapi/path.c

1563 lines
40 KiB
C
Raw Normal View History

/*
* Path Functions
*
* Copyright 1999, 2000 Juergen Schmied
* Copyright 2001, 2002 Jon Griffiths
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winreg.h"
2004-10-05 20:07:14 +02:00
#include "winternl.h"
#define NO_SHLWAPI_STREAM
#include "shlwapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
/* Get a function pointer from a DLL handle */
#define GET_FUNC(func, module, name, fail) \
do { \
if (!func) { \
if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
if (!func) return fail; \
} \
} while (0)
/* DLL handles for late bound calls */
static HMODULE SHLWAPI_hshell32;
/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
typedef BOOL (WINAPI *fnpIsNetDrive)(int);
static fnpIsNetDrive pIsNetDrive;
HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
/*************************************************************************
* PathBuildRootA [SHLWAPI.@]
*
* Create a root drive string (e.g. "A:\") from a drive number.
*
* PARAMS
* lpszPath [O] Destination for the drive string
*
* RETURNS
* lpszPath
*
* NOTES
* If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
*/
LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
{
TRACE("(%p,%d)\n", lpszPath, drive);
if (lpszPath && drive >= 0 && drive < 26)
{
lpszPath[0] = 'A' + drive;
lpszPath[1] = ':';
lpszPath[2] = '\\';
lpszPath[3] = '\0';
}
return lpszPath;
}
/*************************************************************************
* PathBuildRootW [SHLWAPI.@]
*
* See PathBuildRootA.
*/
LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
{
TRACE("(%p,%d)\n", lpszPath, drive);
if (lpszPath && drive >= 0 && drive < 26)
{
lpszPath[0] = 'A' + drive;
lpszPath[1] = ':';
lpszPath[2] = '\\';
lpszPath[3] = '\0';
}
return lpszPath;
}
/*************************************************************************
* PathRemoveArgsA [SHLWAPI.@]
*
* Strip space separated arguments from a path.
*
* PARAMS
* lpszPath [I/O] Path to remove arguments from
*
* RETURNS
* Nothing.
*/
void WINAPI PathRemoveArgsA(LPSTR lpszPath)
{
TRACE("(%s)\n",debugstr_a(lpszPath));
if(lpszPath)
{
LPSTR lpszArgs = PathGetArgsA(lpszPath);
if (*lpszArgs)
lpszArgs[-1] = '\0';
else
{
LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
if(*lpszLastChar == ' ')
*lpszLastChar = '\0';
}
}
}
/*************************************************************************
* PathRemoveArgsW [SHLWAPI.@]
*
* See PathRemoveArgsA.
*/
void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
{
TRACE("(%s)\n",debugstr_w(lpszPath));
if(lpszPath)
{
LPWSTR lpszArgs = PathGetArgsW(lpszPath);
if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
lpszArgs[-1] = '\0';
}
}
/*************************************************************************
* @ [SHLWAPI.4]
*
* Unicode version of PathFileExistsDefExtA.
*/
BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
{
static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0},
{ '.', 'c', 'o', 'm', 0},
{ '.', 'e', 'x', 'e', 0},
{ '.', 'b', 'a', 't', 0},
{ '.', 'l', 'n', 'k', 0},
{ '.', 'c', 'm', 'd', 0},
{ 0, 0, 0, 0, 0} };
TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);
if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
return FALSE;
if (dwWhich)
{
LPCWSTR szExt = PathFindExtensionW(lpszPath);
if (!*szExt || dwWhich & 0x40)
{
size_t iChoose = 0;
int iLen = lstrlenW(lpszPath);
if (iLen > (MAX_PATH - 5))
return FALSE;
while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
{
lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
if (PathFileExistsW(lpszPath))
return TRUE;
iChoose++;
dwWhich >>= 1;
}
*(lpszPath + iLen) = (WCHAR)'\0';
return FALSE;
}
}
return PathFileExistsW(lpszPath);
}
/*************************************************************************
* @ [SHLWAPI.3]
*
* Determine if a file exists locally and is of an executable type.
*
* PARAMS
* lpszPath [I/O] File to search for
* dwWhich [I] Type of executable to search for
*
* RETURNS
* TRUE If the file was found. lpszPath contains the file name.
* FALSE Otherwise.
*
* NOTES
* lpszPath is modified in place and must be at least MAX_PATH in length.
* If the function returns FALSE, the path is modified to its original state.
* If the given path contains an extension or dwWhich is 0, executable
* extensions are not checked.
*
* Ordinals 3-6 are a classic case of MS exposing limited functionality to
* users (here through PathFindOnPathA()) and keeping advanced functionality for
* their own developers exclusive use. Monopoly, anyone?
*/
BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
{
BOOL bRet = FALSE;
TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);
if (lpszPath)
{
WCHAR szPath[MAX_PATH];
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
bRet = PathFileExistsDefExtW(szPath, dwWhich);
if (bRet)
WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
}
return bRet;
}
/*************************************************************************
* SHLWAPI_PathFindInOtherDirs
*
* Internal helper for SHLWAPI_PathFindOnPathExA/W.
*/
static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
{
static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
static const WCHAR szPath[] = { 'P','A','T','H','\0'};
DWORD dwLenPATH;
LPCWSTR lpszCurr;
WCHAR *lpszPATH;
WCHAR buff[MAX_PATH];
TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich);
/* Try system directories */
GetSystemDirectoryW(buff, MAX_PATH);
if (!PathAppendW(buff, lpszFile))
return FALSE;
if (PathFileExistsDefExtW(buff, dwWhich))
{
lstrcpyW(lpszFile, buff);
return TRUE;
}
GetWindowsDirectoryW(buff, MAX_PATH);
if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
return FALSE;
if (PathFileExistsDefExtW(buff, dwWhich))
{
lstrcpyW(lpszFile, buff);
return TRUE;
}
GetWindowsDirectoryW(buff, MAX_PATH);
if (!PathAppendW(buff, lpszFile))
return FALSE;
if (PathFileExistsDefExtW(buff, dwWhich))
{
lstrcpyW(lpszFile, buff);
return TRUE;
}
/* Try dirs listed in %PATH% */
dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);
if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR))))
return FALSE;
GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
lpszCurr = lpszPATH;
while (lpszCurr)
{
LPCWSTR lpszEnd = lpszCurr;
LPWSTR pBuff = buff;
while (*lpszEnd == ' ')
lpszEnd++;
while (*lpszEnd && *lpszEnd != ';')
*pBuff++ = *lpszEnd++;
*pBuff = '\0';
if (*lpszEnd)
lpszCurr = lpszEnd + 1;
else
lpszCurr = NULL; /* Last Path, terminate after this */
if (!PathAppendW(buff, lpszFile))
{
HeapFree(GetProcessHeap(), 0, lpszPATH);
return FALSE;
}
if (PathFileExistsDefExtW(buff, dwWhich))
{
lstrcpyW(lpszFile, buff);
HeapFree(GetProcessHeap(), 0, lpszPATH);
return TRUE;
}
}
HeapFree(GetProcessHeap(), 0, lpszPATH);
return FALSE;
}
/*************************************************************************
* @ [SHLWAPI.5]
*
* Search a range of paths for a specific type of executable.
*
* PARAMS
* lpszFile [I/O] File to search for
* lppszOtherDirs [I] Other directories to look in
* dwWhich [I] Type of executable to search for
*
* RETURNS
* Success: TRUE. The path to the executable is stored in lpszFile.
* Failure: FALSE. The path to the executable is unchanged.
*/
BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
{
WCHAR szFile[MAX_PATH];
WCHAR buff[MAX_PATH];
TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
if (!lpszFile || !PathIsFileSpecA(lpszFile))
return FALSE;
MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
/* Search provided directories first */
if (lppszOtherDirs && *lppszOtherDirs)
{
WCHAR szOther[MAX_PATH];
LPCSTR *lpszOtherPath = lppszOtherDirs;
while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
{
MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
PathCombineW(buff, szOther, szFile);
if (PathFileExistsDefExtW(buff, dwWhich))
{
WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
return TRUE;
}
lpszOtherPath++;
}
}
/* Not found, try system and path dirs */
if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
{
WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
return TRUE;
}
return FALSE;
}
/*************************************************************************
* @ [SHLWAPI.6]
*
* Unicode version of PathFindOnPathExA.
*/
BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
{
WCHAR buff[MAX_PATH];
TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
if (!lpszFile || !PathIsFileSpecW(lpszFile))
return FALSE;
/* Search provided directories first */
if (lppszOtherDirs && *lppszOtherDirs)
{
LPCWSTR *lpszOtherPath = lppszOtherDirs;
while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
{
PathCombineW(buff, *lpszOtherPath, lpszFile);
if (PathFileExistsDefExtW(buff, dwWhich))
{
lstrcpyW(lpszFile, buff);
return TRUE;
}
lpszOtherPath++;
}
}
/* Not found, try system and path dirs */
return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
}
/*************************************************************************
* PathFindOnPathA [SHLWAPI.@]
*
* Search a range of paths for an executable.
*
* PARAMS
* lpszFile [I/O] File to search for
* lppszOtherDirs [I] Other directories to look in
*
* RETURNS
* Success: TRUE. The path to the executable is stored in lpszFile.
* Failure: FALSE. The path to the executable is unchanged.
*/
BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
{
TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
}
/*************************************************************************
* PathFindOnPathW [SHLWAPI.@]
*
* See PathFindOnPathA.
*/
BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
{
TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
}
/*************************************************************************
* PathCompactPathExA [SHLWAPI.@]
*
* Compact a path into a given number of characters.
*
* PARAMS
* lpszDest [O] Destination for compacted path
* lpszPath [I] Source path
* cchMax [I] Maximum size of compacted path
* dwFlags [I] Reserved
*
* RETURNS
* Success: TRUE. The compacted path is written to lpszDest.
* Failure: FALSE. lpszPath is undefined.
*
* NOTES
* If cchMax is given as 0, lpszDest will still be NUL terminated.
*
* The Win32 version of this function contains a bug: When cchMax == 7,
* 8 bytes will be written to lpszDest. This bug is fixed in the Wine
* implementation.
*
* Some relative paths will be different when cchMax == 5 or 6. This occurs
* because Win32 will insert a "\" in lpszDest, even if one is
* not present in the original path.
*/
BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
UINT cchMax, DWORD dwFlags)
{
BOOL bRet = FALSE;
TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
if (lpszPath && lpszDest)
{
WCHAR szPath[MAX_PATH];
WCHAR szDest[MAX_PATH];
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
szDest[0] = '\0';
bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
}
return bRet;
}
/*************************************************************************
* PathCompactPathExW [SHLWAPI.@]
*
* See PathCompactPathExA.
*/
BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
UINT cchMax, DWORD dwFlags)
{
static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
LPCWSTR lpszFile;
DWORD dwLen, dwFileLen = 0;
TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
if (!lpszPath)
return FALSE;
if (!lpszDest)
{
WARN("Invalid lpszDest would crash under Win32!\n");
return FALSE;
}
*lpszDest = '\0';
if (cchMax < 2)
return TRUE;
dwLen = lstrlenW(lpszPath) + 1;
if (dwLen < cchMax)
{
/* Don't need to compact */
memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
return TRUE;
}
/* Path must be compacted to fit into lpszDest */
lpszFile = PathFindFileNameW(lpszPath);
dwFileLen = lpszPath + dwLen - lpszFile;
if (dwFileLen == dwLen)
{
/* No root in psth */
if (cchMax <= 4)
{
while (--cchMax > 0) /* No room left for anything but ellipses */
*lpszDest++ = '.';
*lpszDest = '\0';
return TRUE;
}
/* Compact the file name with ellipses at the end */
cchMax -= 4;
memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
lstrcpyW(lpszDest + cchMax, szEllipses);
return TRUE;
}
/* We have a root in the path */
lpszFile--; /* Start compacted filename with the path separator */
dwFileLen++;
if (dwFileLen + 3 > cchMax)
{
/* Compact the file name */
if (cchMax <= 4)
{
while (--cchMax > 0) /* No room left for anything but ellipses */
*lpszDest++ = '.';
*lpszDest = '\0';
return TRUE;
}
lstrcpyW(lpszDest, szEllipses);
lpszDest += 3;
cchMax -= 4;
*lpszDest++ = *lpszFile++;
if (cchMax <= 4)
{
while (--cchMax > 0) /* No room left for anything but ellipses */
*lpszDest++ = '.';
*lpszDest = '\0';
return TRUE;
}
cchMax -= 4;
memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
lstrcpyW(lpszDest + cchMax, szEllipses);
return TRUE;
}
/* Only the root needs to be Compacted */
dwLen = cchMax - dwFileLen - 3;
memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
lstrcpyW(lpszDest + dwLen, szEllipses);
lstrcpyW(lpszDest + dwLen + 3, lpszFile);
return TRUE;
}
/*************************************************************************
* PathIsDirectoryA [SHLWAPI.@]
*
* Determine if a path is a valid directory
*
* PARAMS
* lpszPath [I] Path to check.
*
* RETURNS
* FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
* FALSE if lpszPath is invalid or not a directory.
*
* NOTES
* Although this function is prototyped as returning a BOOL, it returns
* FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
*
*| if (PathIsDirectoryA("c:\\windows\\") == TRUE)
*| ...
*
* will always fail.
*/
BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
{
DWORD dwAttr;
TRACE("(%s)\n", debugstr_a(lpszPath));
if (!lpszPath || PathIsUNCServerA(lpszPath))
return FALSE;
if (PathIsUNCServerShareA(lpszPath))
{
FIXME("UNC Server Share not yet supported - FAILING\n");
return FALSE;
}
if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
return FALSE;
return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
}
/*************************************************************************
* PathIsDirectoryW [SHLWAPI.@]
*
* See PathIsDirectoryA.
*/
BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
{
DWORD dwAttr;
TRACE("(%s)\n", debugstr_w(lpszPath));
if (!lpszPath || PathIsUNCServerW(lpszPath))
return FALSE;
if (PathIsUNCServerShareW(lpszPath))
{
FIXME("UNC Server Share not yet supported - FAILING\n");
return FALSE;
}
if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
return FALSE;
return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
}
/*************************************************************************
* PathFileExistsAndAttributesA [SHLWAPI.445]
*
* Determine if a file exists.
*
* PARAMS
* lpszPath [I] Path to check
* dwAttr [O] attributes of file
*
* RETURNS
* TRUE If the file exists and is readable
* FALSE Otherwise
*/
BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
{
UINT iPrevErrMode;
DWORD dwVal = 0;
TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);
if (dwAttr)
*dwAttr = INVALID_FILE_ATTRIBUTES;
if (!lpszPath)
return FALSE;
iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
dwVal = GetFileAttributesA(lpszPath);
SetErrorMode(iPrevErrMode);
if (dwAttr)
*dwAttr = dwVal;
return (dwVal != INVALID_FILE_ATTRIBUTES);
}
/*************************************************************************
* PathFileExistsAndAttributesW [SHLWAPI.446]
*
* See PathFileExistsA.
*/
BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
{
UINT iPrevErrMode;
DWORD dwVal;
TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);
if (!lpszPath)
return FALSE;
iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
dwVal = GetFileAttributesW(lpszPath);
SetErrorMode(iPrevErrMode);
if (dwAttr)
*dwAttr = dwVal;
return (dwVal != INVALID_FILE_ATTRIBUTES);
}
/*************************************************************************
* PathIsContentTypeA [SHLWAPI.@]
*
* Determine if a file is of a given registered content type.
*
* PARAMS
* lpszPath [I] File to check
* lpszContentType [I] Content type to check for
*
* RETURNS
* TRUE If lpszPath is a given registered content type,
* FALSE Otherwise.
*
* NOTES
* This function looks up the registered content type for lpszPath. If
* a content type is registered, it is compared (case insensitively) to
* lpszContentType. Only if this matches does the function succeed.
*/
BOOL WINAPI PathIsContentTypeA(LPCSTR path, LPCSTR content_type)
{
char buf[MAX_PATH];
DWORD size = sizeof(buf);
LPCSTR ext;
TRACE("(%s,%s)\n", debugstr_a(path), debugstr_a(content_type));
if(!path) return FALSE;
if(!(ext = PathFindExtensionA(path)) || !*ext) return FALSE;
if(SHGetValueA(HKEY_CLASSES_ROOT, ext, "Content Type", NULL, buf, &size)) return FALSE;
return !lstrcmpiA(content_type, buf);
}
/*************************************************************************
* PathIsContentTypeW [SHLWAPI.@]
*
* See PathIsContentTypeA.
*/
BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
{
static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
LPCWSTR szExt;
DWORD dwDummy;
WCHAR szBuff[MAX_PATH];
TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
!SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
REG_NONE, szBuff, &dwDummy) &&
!wcsicmp(lpszContentType, szBuff))
{
return TRUE;
}
return FALSE;
}
/*************************************************************************
* PathIsSystemFolderA [SHLWAPI.@]
*
* Determine if a path or file attributes are a system folder.
*
* PARAMS
* lpszPath [I] Path to check.
* dwAttrib [I] Attributes to check, if lpszPath is NULL.
*
* RETURNS
* TRUE If lpszPath or dwAttrib are a system folder.
* FALSE If GetFileAttributesA() fails or neither parameter is a system folder.
*/
BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
{
TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib);
if (lpszPath && *lpszPath)
dwAttrib = GetFileAttributesA(lpszPath);
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
!(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
return FALSE;
return TRUE;
}
/*************************************************************************
* PathIsSystemFolderW [SHLWAPI.@]
*
* See PathIsSystemFolderA.
*/
BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
{
TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib);
if (lpszPath && *lpszPath)
dwAttrib = GetFileAttributesW(lpszPath);
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
!(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
return FALSE;
return TRUE;
}
/*************************************************************************
* PathMakePrettyA [SHLWAPI.@]
*
* Convert an uppercase DOS filename into lowercase.
*
* PARAMS
* lpszPath [I/O] Path to convert.
*
* RETURNS
* TRUE If the path was an uppercase DOS path and was converted,
* FALSE Otherwise.
*/
BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
{
LPSTR pszIter = lpszPath;
2004-09-25 02:29:47 +02:00
TRACE("(%s)\n", debugstr_a(lpszPath));
if (!pszIter)
return FALSE;
if (*pszIter)
{
do
{
if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
return FALSE; /* Not DOS path */
pszIter++;
} while (*pszIter);
pszIter = lpszPath + 1;
while (*pszIter)
{
*pszIter = tolower(*pszIter);
pszIter++;
}
}
return TRUE;
}
/*************************************************************************
* PathMakePrettyW [SHLWAPI.@]
*
* See PathMakePrettyA.
*/
BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
{
LPWSTR pszIter = lpszPath;
2004-09-25 02:29:47 +02:00
TRACE("(%s)\n", debugstr_w(lpszPath));
if (!pszIter)
return FALSE;
if (*pszIter)
{
do
{
if (iswlower(*pszIter))
return FALSE; /* Not DOS path */
pszIter++;
} while (*pszIter);
pszIter = lpszPath + 1;
while (*pszIter)
{
*pszIter = towlower(*pszIter);
pszIter++;
}
}
return TRUE;
}
/*************************************************************************
* PathCompactPathA [SHLWAPI.@]
*
* Make a path fit into a given width when printed to a DC.
*
* PARAMS
* hDc [I] Destination DC
* lpszPath [I/O] Path to be printed to hDc
* dx [I] Desired width
*
* RETURNS
* TRUE If the path was modified/went well.
* FALSE Otherwise.
*/
BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
{
BOOL bRet = FALSE;
TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
if (lpszPath)
{
WCHAR szPath[MAX_PATH];
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
bRet = PathCompactPathW(hDC, szPath, dx);
WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
}
return bRet;
}
/*************************************************************************
* PathCompactPathW [SHLWAPI.@]
*
* See PathCompactPathA.
*/
BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
{
static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
BOOL bRet = TRUE;
HDC hdc = 0;
WCHAR buff[MAX_PATH];
SIZE size;
DWORD dwLen;
TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
if (!lpszPath)
return FALSE;
if (!hDC)
hdc = hDC = GetDC(0);
/* Get the length of the whole path */
dwLen = lstrlenW(lpszPath);
GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
if ((UINT)size.cx > dx)
{
/* Path too big, must reduce it */
LPWSTR sFile;
DWORD dwEllipsesLen = 0, dwPathLen = 0;
sFile = PathFindFileNameW(lpszPath);
if (sFile != lpszPath) sFile--;
/* Get the size of ellipses */
GetTextExtentPointW(hDC, szEllipses, 3, &size);
dwEllipsesLen = size.cx;
/* Get the size of the file name */
GetTextExtentPointW(hDC, sFile, lstrlenW(sFile), &size);
dwPathLen = size.cx;
2004-10-05 20:07:14 +02:00
if (sFile != lpszPath)
{
LPWSTR sPath = sFile;
BOOL bEllipses = FALSE;
/* The path includes a file name. Include as much of the path prior to
* the file name as possible, allowing for the ellipses, e.g:
* c:\some very long path\filename ==> c:\some v...\filename
*/
lstrcpynW(buff, sFile, MAX_PATH);
do
{
DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
dwTotalLen += size.cx;
if (dwTotalLen <= dx)
break;
sPath--;
if (!bEllipses)
{
bEllipses = TRUE;
sPath -= 2;
}
} while (sPath > lpszPath);
if (sPath > lpszPath)
{
if (bEllipses)
{
lstrcpyW(sPath, szEllipses);
lstrcpyW(sPath+3, buff);
}
bRet = TRUE;
goto end;
}
lstrcpyW(lpszPath, szEllipses);
lstrcpyW(lpszPath+3, buff);
bRet = FALSE;
goto end;
2004-10-05 20:07:14 +02:00
}
/* Trim the path by adding ellipses to the end, e.g:
* A very long file name.txt ==> A very...
*/
dwLen = lstrlenW(lpszPath);
2004-10-05 20:07:14 +02:00
if (dwLen > MAX_PATH - 3)
dwLen = MAX_PATH - 3;
lstrcpynW(buff, sFile, dwLen);
do {
dwLen--;
GetTextExtentPointW(hDC, buff, dwLen, &size);
} while (dwLen && size.cx + dwEllipsesLen > dx);
if (!dwLen)
{
DWORD dwWritten = 0;
dwEllipsesLen /= 3; /* Size of a single '.' */
/* Write as much of the Ellipses string as possible */
while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
{
*lpszPath++ = '.';
dwWritten += dwEllipsesLen;
dwLen++;
}
*lpszPath = '\0';
bRet = FALSE;
}
else
{
lstrcpyW(buff + dwLen, szEllipses);
lstrcpyW(lpszPath, buff);
}
}
end:
if (hdc)
ReleaseDC(0, hdc);
return bRet;
}
/*************************************************************************
* SHLWAPI_UseSystemForSystemFolders
*
* Internal helper for PathMakeSystemFolderW.
*/
static BOOL SHLWAPI_UseSystemForSystemFolders(void)
{
static BOOL bCheckedReg = FALSE;
static BOOL bUseSystemForSystemFolders = FALSE;
if (!bCheckedReg)
{
bCheckedReg = TRUE;
/* Key tells Win what file attributes to use on system folders */
if (SHGetValueA(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
"UseSystemForSystemFolders", 0, 0, 0))
bUseSystemForSystemFolders = TRUE;
}
return bUseSystemForSystemFolders;
}
/*************************************************************************
* PathMakeSystemFolderA [SHLWAPI.@]
*
* Set system folder attribute for a path.
*
* PARAMS
* lpszPath [I] The path to turn into a system folder
*
* RETURNS
* TRUE If the path was changed to/already was a system folder
* FALSE If the path is invalid or SetFileAttributesA() fails
*/
BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
{
BOOL bRet = FALSE;
TRACE("(%s)\n", debugstr_a(lpszPath));
if (lpszPath && *lpszPath)
{
WCHAR szPath[MAX_PATH];
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
bRet = PathMakeSystemFolderW(szPath);
}
return bRet;
}
/*************************************************************************
* PathMakeSystemFolderW [SHLWAPI.@]
*
* See PathMakeSystemFolderA.
*/
BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
{
DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
WCHAR buff[MAX_PATH];
TRACE("(%s)\n", debugstr_w(lpszPath));
if (!lpszPath || !*lpszPath)
return FALSE;
/* If the directory is already a system directory, don't do anything */
GetSystemDirectoryW(buff, MAX_PATH);
if (!wcscmp(buff, lpszPath))
return TRUE;
GetWindowsDirectoryW(buff, MAX_PATH);
if (!wcscmp(buff, lpszPath))
return TRUE;
/* "UseSystemForSystemFolders" Tells Win what attributes to use */
if (SHLWAPI_UseSystemForSystemFolders())
dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
return FALSE;
/* Change file attributes to system attributes */
dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
}
/*************************************************************************
* PathUnmakeSystemFolderA [SHLWAPI.@]
*
* Remove the system folder attributes from a path.
*
* PARAMS
* lpszPath [I] The path to remove attributes from
*
* RETURNS
* Success: TRUE.
* Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
2003-03-18 19:35:48 +01:00
* SetFileAttributesA() fails.
*/
BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
{
DWORD dwAttr;
TRACE("(%s)\n", debugstr_a(lpszPath));
if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
return FALSE;
dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
return SetFileAttributesA(lpszPath, dwAttr);
}
/*************************************************************************
* PathUnmakeSystemFolderW [SHLWAPI.@]
*
* See PathUnmakeSystemFolderA.
*/
BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
{
DWORD dwAttr;
TRACE("(%s)\n", debugstr_w(lpszPath));
if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
return FALSE;
dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
return SetFileAttributesW(lpszPath, dwAttr);
}
/*************************************************************************
* PathSetDlgItemPathA [SHLWAPI.@]
*
* Set the text of a dialog item to a path, shrinking the path to fit
* if it is too big for the item.
*
* PARAMS
* hDlg [I] Dialog handle
* id [I] ID of item in the dialog
* lpszPath [I] Path to set as the items text
*
* RETURNS
* Nothing.
*
* NOTES
* If lpszPath is NULL, a blank string ("") is set (i.e. The previous
* window text is erased).
*/
VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
{
WCHAR szPath[MAX_PATH];
2002-10-25 05:12:32 +02:00
TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));
if (lpszPath)
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
else
szPath[0] = '\0';
PathSetDlgItemPathW(hDlg, id, szPath);
}
/*************************************************************************
* PathSetDlgItemPathW [SHLWAPI.@]
*
* See PathSetDlgItemPathA.
*/
VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
{
WCHAR path[MAX_PATH + 1];
HWND hwItem;
RECT rect;
HDC hdc;
HGDIOBJ hPrevObj;
2002-10-25 05:12:32 +02:00
TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));
if (!(hwItem = GetDlgItem(hDlg, id)))
return;
if (lpszPath)
lstrcpynW(path, lpszPath, ARRAY_SIZE(path));
else
path[0] = '\0';
GetClientRect(hwItem, &rect);
hdc = GetDC(hDlg);
hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));
if (hPrevObj)
{
PathCompactPathW(hdc, path, rect.right);
SelectObject(hdc, hPrevObj);
}
ReleaseDC(hDlg, hdc);
SetWindowTextW(hwItem, path);
}
/*************************************************************************
* PathIsNetworkPathA [SHLWAPI.@]
*
* Determine if the given path is a network path.
*
* PARAMS
* lpszPath [I] Path to check
*
* RETURNS
2003-03-18 19:35:48 +01:00
* TRUE If lpszPath is a UNC share or mapped network drive, or
* FALSE If lpszPath is a local drive or cannot be determined
*/
BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
{
int dwDriveNum;
TRACE("(%s)\n",debugstr_a(lpszPath));
if (!lpszPath)
return FALSE;
if (*lpszPath == '\\' && lpszPath[1] == '\\')
return TRUE;
dwDriveNum = PathGetDriveNumberA(lpszPath);
if (dwDriveNum == -1)
return FALSE;
GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
return pIsNetDrive(dwDriveNum);
}
/*************************************************************************
* PathIsNetworkPathW [SHLWAPI.@]
*
* See PathIsNetworkPathA.
*/
BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
{
int dwDriveNum;
TRACE("(%s)\n", debugstr_w(lpszPath));
if (!lpszPath)
return FALSE;
if (*lpszPath == '\\' && lpszPath[1] == '\\')
return TRUE;
dwDriveNum = PathGetDriveNumberW(lpszPath);
if (dwDriveNum == -1)
return FALSE;
GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
return pIsNetDrive(dwDriveNum);
}
/*************************************************************************
* PathIsDirectoryEmptyA [SHLWAPI.@]
*
* Determine if a given directory is empty.
*
* PARAMS
* lpszPath [I] Directory to check
*
* RETURNS
2003-03-18 19:35:48 +01:00
* TRUE If the directory exists and contains no files,
* FALSE Otherwise
*/
BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
{
BOOL bRet = FALSE;
TRACE("(%s)\n",debugstr_a(lpszPath));
if (lpszPath)
{
WCHAR szPath[MAX_PATH];
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
bRet = PathIsDirectoryEmptyW(szPath);
}
return bRet;
}
/*************************************************************************
* PathIsDirectoryEmptyW [SHLWAPI.@]
*
* See PathIsDirectoryEmptyA.
*/
BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
{
static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
WCHAR szSearch[MAX_PATH];
DWORD dwLen;
HANDLE hfind;
BOOL retVal = TRUE;
WIN32_FIND_DATAW find_data;
TRACE("(%s)\n",debugstr_w(lpszPath));
if (!lpszPath || !PathIsDirectoryW(lpszPath))
return FALSE;
lstrcpynW(szSearch, lpszPath, MAX_PATH);
PathAddBackslashW(szSearch);
dwLen = lstrlenW(szSearch);
if (dwLen > MAX_PATH - 4)
return FALSE;
lstrcpyW(szSearch + dwLen, szAllFiles);
hfind = FindFirstFileW(szSearch, &find_data);
if (hfind == INVALID_HANDLE_VALUE)
return FALSE;
do
{
if (find_data.cFileName[0] == '.')
{
if (find_data.cFileName[1] == '\0') continue;
if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue;
}
retVal = FALSE;
break;
}
while (FindNextFileW(hfind, &find_data));
FindClose(hfind);
return retVal;
}
/*************************************************************************
* PathFindSuffixArrayA [SHLWAPI.@]
*
* Find a suffix string in an array of suffix strings
*
* PARAMS
* lpszSuffix [I] Suffix string to search for
* lppszArray [I] Array of suffix strings to search
* dwCount [I] Number of elements in lppszArray
*
* RETURNS
2003-03-18 19:35:48 +01:00
* Success: The index of the position of lpszSuffix in lppszArray
* Failure: 0, if any parameters are invalid or lpszSuffix is not found
*
* NOTES
* The search is case sensitive.
* The match is made against the end of the suffix string, so for example:
2003-03-18 19:35:48 +01:00
* lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
*/
LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
{
size_t dwLen;
int dwRet = 0;
TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);
if (lpszSuffix && lppszArray && dwCount > 0)
{
dwLen = strlen(lpszSuffix);
while (dwRet < dwCount)
{
size_t dwCompareLen = strlen(*lppszArray);
if (dwCompareLen < dwLen)
{
if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
return *lppszArray; /* Found */
}
dwRet++;
lppszArray++;
}
}
return NULL;
}
/*************************************************************************
* PathFindSuffixArrayW [SHLWAPI.@]
*
* See PathFindSuffixArrayA.
*/
LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
{
size_t dwLen;
int dwRet = 0;
TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);
if (lpszSuffix && lppszArray && dwCount > 0)
{
dwLen = lstrlenW(lpszSuffix);
while (dwRet < dwCount)
{
size_t dwCompareLen = lstrlenW(*lppszArray);
if (dwCompareLen < dwLen)
{
if (!wcscmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
return *lppszArray; /* Found */
}
dwRet++;
lppszArray++;
}
}
return NULL;
}
/*************************************************************************
* PathUndecorateA [SHLWAPI.@]
*
* Undecorate a file path
*
* PARAMS
* lpszPath [I/O] Path to remove any decoration from
*
* RETURNS
* Nothing
*
* NOTES
2003-03-18 19:35:48 +01:00
* A decorations form is "path[n].ext" where "n" is an optional decimal number.
*/
void WINAPI PathUndecorateA(char *path)
{
char *ext, *skip;
TRACE("(%s)\n", debugstr_a(path));
if (!path) return;
ext = PathFindExtensionA(path);
if (ext == path || ext[-1] != ']') return;
skip = ext - 2;
while (skip > path && '0' <= *skip && *skip <= '9')
skip--;
if (skip > path && *skip == '[' && skip[-1] != '\\')
memmove(skip, ext, strlen(ext) + 1);
}
/*************************************************************************
* PathUndecorateW [SHLWAPI.@]
*
* See PathUndecorateA.
*/
void WINAPI PathUndecorateW(WCHAR *path)
{
WCHAR *ext, *skip;
TRACE("(%s)\n", debugstr_w(path));
if (!path) return;
ext = PathFindExtensionW(path);
if (ext == path || ext[-1] != ']') return;
skip = ext - 2;
while (skip > path && '0' <= *skip && *skip <= '9')
skip--;
if (skip > path && *skip == '[' && skip[-1] != '\\')
memmove(skip, ext, (wcslen(ext) + 1) * sizeof(WCHAR));
}
/*************************************************************************
* @ [SHLWAPI.440]
*
* Find localised or default web content in "%WINDOWS%\web\".
*
* PARAMS
* lpszFile [I] File name containing content to look for
* lpszPath [O] Buffer to contain the full path to the file
* dwPathLen [I] Length of lpszPath
*
* RETURNS
* Success: S_OK. lpszPath contains the full path to the content.
* Failure: E_FAIL. The content does not exist or lpszPath is too short.
*/
HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
{
WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
HRESULT hRet;
TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen);
MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH);
szPath[0] = '\0';
hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
return hRet;
}
/*************************************************************************
* @ [SHLWAPI.441]
*
* Unicode version of SHGetWebFolderFilePathA.
*/
HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
{
static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
DWORD dwLen, dwFileLen;
LANGID lidSystem, lidUser;
TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);
/* Get base directory for web content */
dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
dwLen--;
dwFileLen = lstrlenW(lpszFile);
if (dwLen + dwFileLen + ARRAY_SIZE(szWeb) >= dwPathLen)
return E_FAIL; /* lpszPath too short */
lstrcpyW(lpszPath+dwLen, szWeb);
dwLen += ARRAY_SIZE(szWeb);
dwPathLen = dwPathLen - dwLen; /* Remaining space */
lidSystem = GetSystemDefaultUILanguage();
lidUser = GetUserDefaultUILanguage();
if (lidSystem != lidUser)
{
if (dwFileLen + ARRAY_SIZE(szWebMui) < dwPathLen)
{
/* Use localised content in the users UI language if present */
wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
lstrcpyW(lpszPath + dwLen + ARRAY_SIZE(szWebMui), lpszFile);
if (PathFileExistsW(lpszPath))
return S_OK;
}
}
/* Fall back to OS default installed content */
lstrcpyW(lpszPath + dwLen, lpszFile);
if (PathFileExistsW(lpszPath))
return S_OK;
return E_FAIL;
}