Windows2000/private/inet/urlmon/download/getlpath.cxx
2020-09-30 17:12:32 +02:00

314 lines
12 KiB
C++

#include <cdlpch.h>
#pragma hdrstop
#include <winnls.h>
#include <shlobj.h>
CMutexSem g_mxsCDLGetLongPathNameGlobals;
// stolen from shellp.h
#define ILCreateFromPathORD 157
#define ILFreeORD 155
// stolen from shsemip.h
typedef LPITEMIDLIST (WINAPI *ILCreateFromPathPtr)(LPCWSTR pszPath);
typedef void (WINAPI *ILFreePtr)(LPITEMIDLIST pidl);
// We'll use this if we're running on Memphis or NT5
#ifdef UNICODE
#define STR_GETLONGPATHNAMEW "GetLongPathNameW"
typedef DWORD (WINAPI *GetLongPathNameWPtr)( LPCWSTR lpszShortPath,
LPWSTR lpszLongPath,
DWORD cchBuffer
);
#else
#define STR_GETLONGPATHNAMEA "GetLongPathNameA"
typedef DWORD (WINAPI *GetLongPathNameAPtr)( LPCSTR lpszShortPath,
LPSTR lpszLongPath,
DWORD cchBuffer
);
#endif
STATIC ILCreateFromPathPtr s_pfnILCreate;
STATIC ILFreePtr s_pfnILFree;
STATIC GetLongPathNameAPtr s_pfnGetLongPathNameA;
#define cKnownDirs 5
STATIC struct KnownDirsMap {
BOOL m_bInited;
LPTSTR m_aszCaches[cKnownDirs];
struct _tagKDMap {
TCHAR szShort[MAX_PATH];
int cchShort;
TCHAR szCanonical[MAX_PATH];
int cchCanonical;
} m_aKDMap[cKnownDirs];
int IndexKnownDirs( LPTSTR szName )
{
int i;
for ( i = 0; i < cKnownDirs; i++ ) {
// we only want to compare out through the cache folder itself
BOOL fMatch = (m_aKDMap[i].cchShort != 0 &&
CompareString( LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
m_aKDMap[i].szShort,
m_aKDMap[i].cchShort,
szName,
m_aKDMap[i].cchShort ) == 2)
||
(m_aKDMap[i].cchCanonical != 0 &&
CompareString( LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
m_aKDMap[i].szCanonical,
m_aKDMap[i].cchCanonical,
szName,
m_aKDMap[i].cchCanonical ) == 2);
if ( fMatch )
break;
}
if ( i >= cKnownDirs )
i = -1; // signal a miss
return i;
};
} s_kdMap = {
FALSE,
{
"\\Occache\\",
"\\OC Cache\\",
"\\Downloaded ActiveX Controls\\",
"\\Downloaded Components\\",
"\\Downloaded Program Files\\"
},
{
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 }
}
};
STATIC BOOL IsCanonicalName( LPCTSTR szName )
{
// simple test - if there's a ~ in it, it has a contraction in it
// and is therefore non-canonical
for ( ; *szName != '\0' && *szName != '~'; szName++ );
return *szName != '~';
}
STATIC DWORD s_CDLGetLongPathName( LPTSTR szLong, LPCTSTR szShort, DWORD cchBuffer)
{
HRESULT hr = E_FAIL;
HMODULE hmodS32;
HMODULE hmodK32;
DWORD cchLong = 0;
// Don't jump through all these hoops if there aren't any contractions in it.
if ( IsCanonicalName( szShort ) ) {
lstrcpyn( szLong, szShort, cchBuffer );
return lstrlen( szLong );
}
hmodS32 = LoadLibrary( "SHELL32.DLL" );
hmodK32 = LoadLibrary( "KERNEL32.DLL" );
// Set up our globals with short and long versions of the base cache path
if ( hmodS32 && hmodK32 ) {
#ifdef UNICODE
s_pfnGetLongPathNameW = (GetLongPathNameWPtr)GetProcAddress(hmodK32, (LPCSTR)STR_GETLONGPATHNAMEW );
#else
s_pfnGetLongPathNameA = (GetLongPathNameAPtr)GetProcAddress(hmodK32, (LPCSTR)STR_GETLONGPATHNAMEA );
#endif
s_pfnILCreate = (ILCreateFromPathPtr)GetProcAddress(hmodS32, (LPCSTR)ILCreateFromPathORD );
s_pfnILFree = (ILFreePtr)GetProcAddress(hmodS32, (LPCSTR)ILFreeORD );
if ( s_pfnILCreate != NULL && s_pfnILFree != NULL ) {
// We need to initialize our static know directories table.
// To be safe, we'll grab a mutex while we do the dirty deed.
{ // constrain the scope of the mutex
CLock lck(g_mxsCDLGetLongPathNameGlobals);
if ( !s_kdMap.m_bInited ) {
TCHAR szWinDir[MAX_PATH];
int i;
GetWindowsDirectory( szWinDir, MAX_PATH );
for ( i = 0; i < cKnownDirs; i++ ) {
lstrcpy( s_kdMap.m_aKDMap[i].szCanonical, szWinDir );
lstrcat( s_kdMap.m_aKDMap[i].szCanonical, s_kdMap.m_aszCaches[i] );
s_kdMap.m_aKDMap[i].cchCanonical = lstrlen( s_kdMap.m_aKDMap[i].szCanonical );
GetShortPathName( s_kdMap.m_aKDMap[i].szCanonical,
s_kdMap.m_aKDMap[i].szShort, MAX_PATH );
s_kdMap.m_aKDMap[i].cchShort = lstrlen( s_kdMap.m_aKDMap[i].szShort );
}
s_kdMap.m_bInited = TRUE;
}
}
TCHAR *pch;
TCHAR *szT = new CHAR[MAX_PATH];
if ( szShort != NULL && szT != NULL && s_kdMap.m_bInited ) {
LPITEMIDLIST pidl = NULL;
// Okay, kids, now this gets fun.
// If we're on Memphis or NT5, we can simply call GetLongPathName
// to get the canonical format.
// If not, we've a bit more work ahead of us.
// If the path is not down into one of the dowload cache folders,
// we can use shell32 functions to create the long path. This involves
// generating a pidl for the file, then converting the pidl back into a path.
// If the path goes down into one of the cache directories, we're in
// a bit of trouble, because OC cache does not implement
// IShellFolder::ParseDisplayName, so we cannot generate a proper pidl.
// In that case, we use prefab paths to these known directories
// and tack on the long name of the file.
#ifdef UNICODE
// NOTE: the prototypes in Winbase.h are deceiving - look at the argument types,
// NOT at the argument names, the output parameter is second!
if ( s_pfnGetLongPathNameW ) {
hr = (s_pfnGetLongPathNameW( szShort, szLong, MAX_PATH ) != 0)? S_OK : E_FAIL;
#else
if ( s_pfnGetLongPathNameA ) {
hr = (s_pfnGetLongPathNameA( szShort, szLong, MAX_PATH ) != 0)? S_OK : E_FAIL;
#endif
} else {
LPTSTR szFileName;
if ( GetFullPathName( szShort, MAX_PATH, szT, &szFileName ) ) {
int iKnownDir = s_kdMap.IndexKnownDirs( szT );
if ( iKnownDir >= 0 ) {
WIN32_FIND_DATA wfd;
int cchBase = lstrlen(szT) - lstrlen(szFileName);
// okay, it's in one of our caches
if ( !IsCanonicalName( szFileName ) ) {
HANDLE hfind = FindFirstFile( szShort, &wfd );
if ( hfind != INVALID_HANDLE_VALUE ) {
szFileName = wfd.cFileName;
} else
szFileName = NULL;
}
if ( szFileName != NULL ) {
lstrcpy( szLong, s_kdMap.m_aKDMap[iKnownDir].szCanonical );
// chop off szT right before the file name.
// if this is longer than the known dir name, we have a
// conflict.* subdirectory and must add this before the file
if ( cchBase != s_kdMap.m_aKDMap[iKnownDir].cchShort &&
cchBase != s_kdMap.m_aKDMap[iKnownDir].cchCanonical ) {
LPTSTR szConflict;
CHAR chT = szT[cchBase];
szT[cchBase] = '\0';
// search back from before the file name.
for ( szConflict = &szT[cchBase - 2];
*szConflict != '\\'; szConflict-- );
szConflict++; // we already have a '\'
lstrcat( szLong, szConflict );
szT[cchBase] = chT;
}
lstrcat( szLong, szFileName );
hr = S_OK;
}
} else {
#ifndef UNICODE
WCHAR *szwT = new WCHAR[MAX_PATH];
if ( szwT &&
MultiByteToWideChar( CP_ACP, 0, szShort, -1, szwT, MAX_PATH ) )
pidl = s_pfnILCreate( szwT );
delete szwT;
#else
pidl = s_pfnILCreate( szShort );
#endif
if ( pidl != NULL ) {
// Now we get the shell to turn the item id list back into
// a path rich in long file names, which is our canonical form
if ( SHGetPathFromIDList( pidl, szT ) ) {
if ( szLong != NULL ) {
#ifdef UNICODE
LPWSTR szLongW;
hr = Ansi2Unicode( szT, &szLongW );
if ( SUCCEEDED(hr) ) {
StrCpyNW( szLong, szLongW, cchBuffer );
delete szLongW;
}
#else
//BUFFER OVERRUN lstrcpyA( szLong, szT );
StrCpyN( szLong, szT, cchBuffer );
hr = S_OK;
#endif
}
}
s_pfnILFree( pidl );
} // if we got the pidl
} // else we're getting shell32 to do the dirty-work
} // if we got the full path/file name
} // else we can't use GetLongPathName
delete szT;
} // if we can get out temp string
}
FreeLibrary( hmodS32 );
FreeLibrary( hmodK32 );
}
return ((hr==S_OK)? lstrlen(szLong) : 0);
}
DWORD WINAPI CDLGetLongPathNameA( LPSTR szLong, LPCSTR szShort, DWORD cchBuffer)
{
#ifndef UNICODE
return s_CDLGetLongPathName( szLong, szShort, cchBuffer );
#else
HRESULT hr;
LPWSTR szShortW;
TCHAR szLongT[MAX_PATH];
hr = Ansi2Unicode( szShort, &szShortW );
if ( SUCCEEDED(hr) ) {
hr = s_CDLGetLongPathName( szLongT, szShortW, MAX_PATH );
if ( SUCCEEDED(hr) ) {
LPSTR szLongA;
hr = Unicode2Ansi( szLongT, &szLongA );
if ( SUCCEEDED(hr) ) {
lstrcpynA( szLong, szLongA, cchBuffer );
delete szLongA;
}
}
delete szShortW;
}
return hr;
#endif
}
DWORD WINAPI CDLGetLongPathNameW( LPWSTR szLong, LPCWSTR szShort, DWORD cchBuffer)
{
#ifdef UNICODE
return s_CDLGetLongPathName( szLong, szShort, cchBuffer );
#else
HRESULT hr;
LPSTR szShortA;
TCHAR szLongT[MAX_PATH];
hr = Unicode2Ansi( szShort, &szShortA );
if ( SUCCEEDED(hr) ) {
hr = s_CDLGetLongPathName( szLongT, szShortA, cchBuffer );
if ( SUCCEEDED(hr) ) {
LPWSTR szLongW;
hr = Ansi2Unicode( szLongT, &szLongW );
if ( SUCCEEDED(hr) ) {
StrCpyNW( szLong, szLongW, cchBuffer );
delete szLongW;
}
}
delete szShortA;
}
return hr;
#endif
}