Bug 156422 Win32's nsILocalFile.reveal() doesn't select file p=dean_tessman r=law (tweaked by brofield@jellycan.com r=me) sr=alecf

This commit is contained in:
neil%parkwaycc.co.uk 2004-06-02 08:19:42 +00:00
Родитель f7bc60ff82
Коммит f3b448e4ef
1 изменённых файлов: 28 добавлений и 168 удалений

Просмотреть файл

@ -22,6 +22,7 @@
*
* Contributor(s):
* Doug Turner <dougt@netscape.com>
* Dean Tessman <dean_tessman@hotmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -68,6 +69,7 @@
#include "nsITimelineService.h"
#include "nsAutoLock.h"
#include "SpecialSystemDirectory.h"
// _mbsstr isn't declared in w32api headers but it's there in the libs
#ifdef __MINGW32__
@ -2167,178 +2169,36 @@ nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
NS_IMETHODIMP
nsLocalFile::Reveal()
{
nsresult rv = NS_OK;
PRBool isDirectory = PR_FALSE;
nsCAutoString path;
nsAutoString unicodePath;
// make sure mResolvedPath is set
nsresult rv = ResolveAndStat(PR_TRUE);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
return rv;
IsDirectory(&isDirectory);
if (isDirectory)
{
GetNativePath(path);
}
else
{
nsCOMPtr<nsIFile> parent;
GetParent(getter_AddRefs(parent));
if (parent)
{
parent->GetNativePath(path);
parent->GetPath(unicodePath);
}
}
// use the full path to explorer for security
nsCOMPtr<nsILocalFile> winDir;
rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(winDir));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString explorerPath;
rv = winDir->GetNativePath(explorerPath);
NS_ENSURE_SUCCESS(rv, rv);
explorerPath.Append("\\explorer.exe");
// Remember the current fg window.
HWND origWin, fgWin;
origWin = fgWin = ::GetForegroundWindow();
// Always open a new window for files because Win2K doesn't appear to select
// the file if a window showing that folder was already open. If the resolved
// path is a directory then instead of opening the parent and selecting it,
// we open the directory itself.
nsCAutoString explorerParams;
if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above
explorerParams.Append("/n,/select,");
explorerParams.Append('\"');
explorerParams.Append(mResolvedPath);
explorerParams.Append('\"');
// use the app registry name to launch a shell execute....
LONG r = (LONG) ::ShellExecute( NULL, "open", path.get(), NULL, NULL, SW_SHOWNORMAL);
if (r < 32)
return NS_ERROR_FAILURE;
// If this is a directory, then we don't need to select a file.
if (isDirectory)
if (::ShellExecute(NULL, "open", explorerPath.get(), explorerParams.get(),
NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32)
return NS_ERROR_FAILURE;
return NS_OK;
// Resources we may need to free when done.
IShellFolder *desktopFolder = 0;
IMalloc *shellMalloc = 0;
IShellFolder *folder = 0;
LPITEMIDLIST folder_pidl = 0;
LPITEMIDLIST file_pidl = 0;
LPITEMIDLIST win95_file_pidl = 0;
HMODULE shell32 = 0;
// We break out of this do/while non-loop at any point where we have to give up.
do {
// Wait for the window to open. We wait a maximum of 2 seconds.
// If we get the wrong window, that will be dealt with below.
for (int iter = 10; iter; iter--)
{
fgWin = ::GetForegroundWindow();
if (fgWin != origWin)
break; // for loopo
::Sleep(200);
}
// If we failed to locate the new window, give up.
if (origWin == fgWin)
break; // do/while
// Now we have the explorer window. We need to send it the "select item"
// message (which isn't trivial, so buckly your seat belt)...
// We need the explorer's process id.
DWORD pid = 0;
::GetWindowThreadProcessId(fgWin, &pid);
// Get desktop folder.
HRESULT rc = ::SHGetDesktopFolder(&desktopFolder);
if (!desktopFolder)
break;
// Get IMalloc interface to use for shell pidls.
rc = ::SHGetMalloc(&shellMalloc);
if (!shellMalloc)
break;
// Convert folder path to pidl. This requires the Unicode path name.
// It returns a pidl that must be freed via shellMalloc->Free.
ULONG eaten = 0;
rc = desktopFolder->ParseDisplayName( 0,
0,
(LPOLESTR)unicodePath.get(),
&eaten,
&folder_pidl,
0 );
if (!folder_pidl)
break;
// Now get IShellFolder interface for the folder we opened.
rc = desktopFolder->BindToObject( folder_pidl,
0,
IID_IShellFolder,
(void**)&folder );
if (!folder)
break;
// Now get file name pidl from that folder.
nsAutoString unicodeLeaf;
if (NS_FAILED(GetLeafName(unicodeLeaf)))
break;
rc = folder->ParseDisplayName( 0,
0,
(LPOLESTR)unicodeLeaf.get(),
&eaten,
&file_pidl,
0 );
if (!file_pidl)
break;
// We need the module handle for shell32.dll.
shell32 = ::GetModuleHandle("shell32.dll");
if (!shell32)
break;
// Allocate shared memory copy of pidl. This uses the undocumented "SHAllocShared"
// function. Note that it is freed automatically after the ::SendMessage so we
// don't have to free it.
static HANDLE(WINAPI*SHAllocShared)(LPVOID,ULONG,DWORD) = (HANDLE(WINAPI*)(LPVOID,ULONG,DWORD))::GetProcAddress(shell32, (LPCTSTR)520);
HANDLE pidlHandle = 0;
if (SHAllocShared)
{
// We need the size of the pidl, which we get via another undocumented
// API: "ILGetSize".
UINT (WINAPI*ILGetSize)(LPCITEMIDLIST) = (UINT(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)152);
if (!ILGetSize)
break;
pidlHandle = SHAllocShared((void*)(ITEMIDLIST*)file_pidl,
ILGetSize(file_pidl),
pid);
if (!pidlHandle)
break;
}
else
{
// On Win95, there is no SHAllocShared. Instead, we clone the file's pidl in
// the shell's global heap (via ILGlobalClone) and pass that.
LPITEMIDLIST(WINAPI*ILGlobalClone)(LPCITEMIDLIST) = (LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)20);
if (!ILGlobalClone)
break;
win95_file_pidl = ILGlobalClone(file_pidl);
if (!win95_file_pidl)
break;
// Arrange so that this pidl is passed on the ::SendMessage.
pidlHandle = win95_file_pidl;
}
// Send message to select this file.
::SendMessage(fgWin,
WM_USER+5,
SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE,
(LPARAM)pidlHandle );
} while ( PR_FALSE );
// Clean up (freeing stuff as needed, in reverse order).
if (win95_file_pidl)
{
// We need to free this using ILGlobalFree, another undocumented API.
static void (WINAPI*ILGlobalFree)(LPCITEMIDLIST) = (void(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32,(LPCTSTR)156);
if (ILGlobalFree)
ILGlobalFree(win95_file_pidl);
}
if (file_pidl)
shellMalloc->Free(file_pidl);
if (folder_pidl)
shellMalloc->Free(folder_pidl);
if (folder)
folder->Release();
if (shellMalloc)
shellMalloc->Release();
if (desktopFolder)
desktopFolder->Release();
return rv;
}