Closed Bug 156422 Opened 24 years ago Closed 22 years ago

Win32's nsILocalFile.reveal() (used by Reveal Location) doesn't select file

Categories

(Core Graveyard :: File Handling, defect)

x86
Windows 95
defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: neil, Assigned: deanis74)

References

()

Details

(Keywords: fixed-aviary1.0.1, fixed1.7.6)

Attachments

(2 files, 4 obsolete files)

Possibly a Windows 95 thing because I noticed that the code looked very complicated especially in comparison with the Perl that I use: sub reveal { system "$ENV{'windir'}\\explorer.exe /n,/select,$_[0]" }
Ummm... wow, that's some complicated code! What Neil suggests works great for me on Win2K, and is a lot faster than what we currently do, because of the up to 2 second wait. Running explorer directly with "/select" works for folders as well. Both of these work fine for me: explorer /n,/select,c:\winnt\explorer.exe explorer /n,/select,c:\winnt
This isn't just Win95, it's not working for me on Win2K either. What I meant in my last comment is that Neil's suggestion works fine for me on Win2K. I think we should simplify the code a lot and go with that. Oh wait, but what if the user has a custom shell installed? Does our current code even work with that?
Dean, in the case of a custom shell, then running explorer will (if memory serves me correctly) still open an explorer window with the file selected, which is probably what we want anyway. Are you in a position to write a patch for this?
Taking.
Assignee: law → dean_tessman
Attached patch cvs diff -u xpcom/io (obsolete) — Splinter Review
Patch incorporating Neil's suggestion. If the file is not found, an error is raised that "The path '<path and filename>' does not exist or is not a directory." If you ask me, this code is much simpler and less prone to errors. Oh, and I think it works.
Reviews?
Keywords: patch, review
Comment on attachment 92317 [details] [diff] [review] cvs diff -u xpcom/io >- LONG r = (LONG) ::ShellExecute( NULL, "open", path.get(), NULL, NULL, SW_SHOWNORMAL); >- if (r < 32) >+ LONG r; >+ r = (LONG) ::ShellExecute(NULL, "open", explorerPath.get(), explorerParams.get(), >+ NULL, SW_SHOWNORMAL); >+ if (r < 32) >+ rv = NS_ERROR_FAILURE; Dean, you may have copied bad code, but my docs say that ShellExecute returns <= 32 on error. Also, why didn't you inline the LONG r? I noticed that you seem to use 4-space indentation; is that consistent with the rest of the file? The old code appeared to use 2-space indentation.
1. Yes, you're right. I copied the existing code but it should check for <= 32. 2. You mean 'if ::ShellExecute() <= 32 return NS_ERROR_FAILURE'? Good question. Again, I copied from the existing code. 3. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ Most of the file follows the tab width of 4, but Reveal and Launch don't.
Dean, that's fine by me.
Attached patch take 2 (obsolete) — Splinter Review
1. No longer use a variable to handle the return value from ::ShellExecute(). 2. No longer use the rv variable, as there are only three return points, and one already uses the variable ret. 3. Changed < 32 to <= 32. 4. Pass ::GetForegroundWindow() to ::ShellExecute which, according to how I read the docs, should make the foreground window the owner of any messageboxes (eg. file not found). In practice, this doesn't make a lick of difference on Win2K. The messagebox still comes up without a parent.
Attachment #92317 - Attachment is obsolete: true
> 4. Pass ::GetForegroundWindow() to ::ShellExecute which, according to how I > read the docs, should make the foreground window the owner of any messageboxes > (eg. file not found). In practice, this doesn't make a lick of difference on > Win2K. The messagebox still comes up without a parent. Other uses of ShellExecute in Mozilla all appear to pass NULL so which is better, consistency or correctness? :-)
Makes no difference to me.
Aw man, I could've sworn I cc:ed Bill when I took this bug from him. Bill, there's a working, simple patch for this. Any thoughts?
Comment on attachment 92530 [details] [diff] [review] take 2 Need to double-quote the filename, to account for filenames with spaces, commas, etc.
Attachment #92530 - Flags: needs-work+
1. Always wrap the filename in double quotes, to handle long filenames. 2. Remove the const declarations. These are used only once, no need for the extra code. 3. Remove passing ::GetForegroundWindow() as hWnd to ::ShellExecute(), to keep in line with other uses of ::ShellExecute() in the app. Ready for r=/sr=.
Attachment #92530 - Attachment is obsolete: true
> Need to double-quote the filename, to account for filenames with spaces, > commas, etc. FYI it appears to work with or without quotes on Windows 95, e.g. C:\WINDOWS\explorer.exe /n,/select,C:\Program Files\Paint Shop Pro\psp.exe
Yes, it works for names with spaces but doesn't work for names with commas in them. c:\winnt\explorer.exe /n,/select,c:\temp\test,1.txt fails, while c:\winnt\explorer.exe /n,/select,"c:\temp\test,1.txt" works. So there are at least some filenames that need to be quoted, so I just quote them all.
Silly me for not reading all of the text that I quoted. /Slaps wrist/
Comment on attachment 94582 [details] [diff] [review] double-quote the filename I think the lines: + explorerParams.Append(""""); are wrong. That translates to two empty strings concatenated, I believe. I think they should be: + explorerParams.Append("\""); With that fixed, r=law.
Attachment #94582 - Flags: review+
Wow, that's just dumb of me! Yes, I'll definitely change that for the final patch. Who can sr= this?
Comment on attachment 94582 [details] [diff] [review] double-quote the filename well, I guess this is ok but I'm not a huge fan of executing explorer directly. There must be a way to do this in-process, no? I guess I'm concerned about some sort of security exploit involving executing explorer directly. Maybe we should get mitch in on this. also, the """" looks odd to me. Are you sure you didn't mean "\"\""? because to me, """" looks like two empty strings concatenated together, similar to "a""b"
cc'ing mstoltz for security stuff (mitch - see my previous comment) - do we have a "secure" way of executing external processes - i.e. filtering valid filenames, etc?
> also, the """" looks odd to me. Are you sure you didn't mean "\"\""? because to > me, """" looks like two empty strings concatenated together, similar to "a""b" Alec, yes. See comment 19 and 20. It should be "\"". I've spent too much time in other languages lately, and I forgot the C way of doing it.
Incidentally, though, """" adds a double quote for me, not a blank string. It has the same effect as "\"".
I am concerned about the security of this change. Let's hold off on checking this in until we make sure it can't be used for anything sneaky.
I don't know how it can do anything "sneaky", since it uses "/select". The existing code does a ShellExecute(), and if you ask me the things it does are a lot more risky than what this code proposes.
Mitchell, Alec, and Bill, have you thought about the security implications of this? I maintain that it's a safe way of doing things, and I believe that Windows probably does this internally.
Actually Windows doesn't even bother to verify the path to explorer.exe :-)
QA Contact: sairuh → petersen
Can we decide if this is a security concern or not? I'm fine with the new code, and it sounds like Neil too.
I'm not sure if calling Explorer is a security risk or not. We probably need to make sure we filter the parameters we pass, to make sure they can't do anything nasty, but we don't have a standardized way of doing that. CCing Darin; he may know more about calling Explorer.
Looking at the registry, the "Explore" context menu for folders uses this command: explorer /e,/root,{f39a0dc0-9cc8-11d0-a599-00c04fd64433},%L
Strange, I have (both 95 and 2000) C:\WINDOWS\Explorer.exe /e,/idlist,%I,%L
I've got the same thing I mentioned on my work and home machines, both running Windows 2000. That wasn't my point, though. I was hoping to address the question of whether it's OK to do this, and I haven't heard any security problems from using the Explore command, so I don't think what we're doing is anything out of the ordinary.
I've been asked to comment. Not that I have any uncommon knowledge about this. But it seems to me you just want to be extra careful to use the full path to Explorer.exe and to the target file, and to not send any surprising parameters to Explorer. And none of that buffer overflow stuff. The patch looks fine to me. If it makes anyone feel any better, there's a page on MSDN about "security issues of calling ShellExecute." It says what I just said. Or maybe it's the other way around. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/sec_shell.asp
Blocks: 211894
Comment on attachment 94582 [details] [diff] [review] double-quote the filename We need this to fix bug 211894.
Attachment #94582 - Flags: superreview?(alecf)
Comment on attachment 94582 [details] [diff] [review] double-quote the filename woah woah. I was going to say that I'm considering this.. but then I saw + explorerParams.Append(""""); This is the same as + explorerParams.Append("" ""); which is the same as + explorerParams.Append(""); which appends nothing. I think what you meant was + explorerParams.Append("\""); but even better might be + explorerParams.Append('\"'); I still want to know if there is a safer way of calling explorer, and I'm trying to figure out why the old way wasn't good. I realize it was complex, but it relied on system APIs rather than passing specific (undocumented?) parameters to explorer.
Attachment #94582 - Flags: superreview?(alecf) → superreview-
> woah woah. I was going to say that I'm considering this.. but then I saw > + explorerParams.Append(""""); > > This is the same as > + explorerParams.Append("" ""); > > which is the same as > + explorerParams.Append(""); > >which appends nothing. Really? In my testing it appended a double quote (") (see comment 24). Regardless, I'm fine changing that to use whatever syntax, that's minor. One of the reasons the old method isn't good is because it waits a maximum of only two seconds for the Explorer window to open before selecting the file. We could pump that up to 10 seconds, even 100 seconds, but because it's on a set timer there's always the chance that Explorer will take longer to open than we're waiting.
Status: NEW → ASSIGNED
Attached patch patch 4 (obsolete) — Splinter Review
Since Dean seems to be busy, I've taken his patch as the base and updated it. The parameter /n is now not passed to explorer. This way explorer will reuse any existing window which is already showing that folder. If there is no existing window or existing windows are showing other folders, it will create a new one. This is more user friendly behaviour I think. One question, should Reveal() always show the parent folder of an item, including the case of a directory? i.e. should Reveal() on C:\windows, (a) open an explorer window showing the contents of c:\windows, or (b) open an explorer window showing c:\ with windows selected. This patch does (a) which seems to be more natural. I've verified that this patch also fixes bug 211894.
Attached patch patch 5 (obsolete) — Splinter Review
Attachment #149388 - Attachment is obsolete: true
Attachment #149389 - Flags: superreview?(alecf)
Attachment #149389 - Flags: review?(neil.parkwaycc.co.uk)
Comment on attachment 149389 [details] [diff] [review] patch 5 >+ nsresult rv; >+ >+ rv = ResolveAndStat(PR_TRUE); Any reason why you separated out the declaration and removed the comment? >+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) >+ return rv; >+ // If there is no existing explorer window *showing that folder* then this will >+ // open a new window, otherwise it will reuse the existing window. >+ // If the resolved path is a directory then we want to open it rather than >+ // opening the parent and selecting it, so we don't append the /select option. >+ nsCAutoString explorerParams; >+ if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above >+ explorerParams.Append("/select,"); Sigh... this works fine in Windows 95 but on my W2K server it doesn't select the file if the folder is already open :-( >+ if (::ShellExecute(::GetForegroundWindow(), "open", explorerPath.get(), I'm still not keen on GetForegroundWindow() because it may not be ours. >+ explorerParams.get(), NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32) This indentation is confusing... an extra 4 spaces perhaps? >+ return NS_ERROR_FAILURE;
>+ nsresult rv; >+ >+ rv = ResolveAndStat(PR_TRUE); >Any reason why you separated out the declaration and removed the >comment? Just personal style. I tend to separate variable declarations like nsresult when I use the declared variable many times through the body of the function. I find it makes it easier to see the declaration rather than having it buried, and if the code gets moved around I don't get compile errors because I'm now using the variable before it's declared. Since it's a simple stack variable it's no more efficient one way or the other. The comment I just removed because it matches the style of all other ResolveAndStat calls. Easily replaced if you'd prefer. >+ // opening the parent and selecting it, so we don't append the /select option. >+ nsCAutoString explorerParams; >+ if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above >+ explorerParams.Append("/select,"); > Sigh... this works fine in Windows 95 but on my W2K server it > doesn't select the file if the folder is already open :-( I find the existing code doesn't always work either. We could always change it so that we use the "/n" option. If we are opening a new window then it should always select the file. >+ if (::ShellExecute(::GetForegroundWindow(), "open", explorerPath.get(), I'm still not keen on GetForegroundWindow() because it may not be ours. >+ explorerParams.get(), NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32) > This indentation is confusing... an extra 4 spaces perhaps? Will do.
(In reply to comment #41) > The comment I just removed because it matches the style of all other > ResolveAndStat calls. Easily replaced if you'd prefer. Fine, although strangely they all seem to say nsresult rv = ResolveAndStat(PR_TRUE); ;-) > We could always change it so that we use the "/n" option. > If we are opening a new window then it should always select the file. OK, but just to clarify, this is just in the /select case. >> I'm still not keen on GetForegroundWindow() because it may not be ours. No comment on this one?
> nsresult rv = ResolveAndStat(PR_TRUE); Ok. I'll join the lusting throng and do it that way too. >> I'm still not keen on GetForegroundWindow() because it may not be ours. > No comment on this one? Sorry, I missed that one. I just copied the existing code, but I also didn't like that code when I saw it. What about if we just parent any dialog to the desktop window instead? GetDesktopWindow()
> >> I'm still not keen on GetForegroundWindow() because it may not be ours. > > No comment on this one? > > Sorry, I missed that one. I just copied the existing code, but I also didn't > like that code when I saw it. What about if we just parent any dialog to the > desktop window instead? GetDesktopWindow() Based on comment 10 and comment 11, I say pass NULL. I'm also fine with using /n all the time. That would give us predictable behavior.
The way I've found the win32 API is that NULL for a hwnd is the same as GetDesktopWindow() when it needs a parent anyhow, so passing NULL for the parent is no different to passing the desktop window anyhow. I have no problem with that change. As for always using "/n", I think that if we should only use it when we are specifying the "/select" parameter, since it seems that 2K doesn't select an object when it doesn't open the window. I guess this would be consistent with the Microsoft aim to prevent non-active windows from modifying the current users actions. i.e. if Mozilla isn't active at the time that it tries to open the window, then it shouldn't try to modify the currently selected object in explorer. This behaviour changed in 2K. I have no problemm with specifying /n when we also specify /select. However, when we are opening a folder (is what I have specified is the correct behaviour?) then we should not specify /n. Is this agreeable?
I can live with that.
Attached patch patch 6Splinter Review
Patch with requested changes.
Attachment #149389 - Attachment is obsolete: true
Attachment #149464 - Flags: superreview?(alecf)
Attachment #149464 - Flags: review?(neil.parkwaycc.co.uk)
Attachment #149389 - Flags: superreview?(alecf)
Attachment #149389 - Flags: review?(neil.parkwaycc.co.uk)
Attachment #149464 - Flags: review?(neil.parkwaycc.co.uk) → review+
To address alecf's concerns in comment 36: > I still want to know if there is a safer way of calling explorer Using the full path to explorer.exe in the system directory is as safe as we can get. All of Microsoft's own code either does this or just calls "explorer.exe" without a path. Additionally the "explorer.exe" file is protected by Windows File Protection on Win2K+, so there shouldn't be much chance of trojans on those platforms. > I'm trying to figure out why the old way wasn't good. I realize > it was complex, but it relied on system APIs rather than > passing specific (undocumented?) parameters to explorer. It is complex, it uses undocumented API and it doesn't always work. This way is simple, it uses documented parameters to explorer <http://support.microsoft.com/support/kb/articles/Q130/5/10.asp> and it works. I've searched for an in-process method of doing this but I can't find a way for *all* windows versions. Microsoft must have seen that this functionality was missing and has added the SHOpenFolderAndSelectItems() function, but available only in XP.
Comment on attachment 149464 [details] [diff] [review] patch 6 sorry for the delay, I was out of town. thanks for doing the research on this one... sr=alecf
Attachment #149464 - Flags: superreview?(alecf) → superreview+
As I lack CVS write access, I would greatly appreciate if someone would check this in for me. Regards, Brodie. Note that this fixes this bug as well as bug 211894.
Fix checked in.
Status: ASSIGNED → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Comment on attachment 149464 [details] [diff] [review] patch 6 since this fixes bug 211894, this would be good to have on the 1.7 branch
Attachment #149464 - Flags: approval1.7.6?
Request blocking flag, see comment 52.
Flags: blocking1.7.6?
Thanks for bringing this to our attention, blocking 1.7.6/aviary1.0.1 per drivers.
Flags: blocking1.7.6?
Flags: blocking1.7.6+
Flags: blocking-aviary1.0.1+
Fix checked into aviary1.0.1 branch
Comment on attachment 149464 [details] [diff] [review] patch 6 retroactive a=caillon for 1.0.1. We will also need this for 1.7.6 so a=caillon for that as well.
Attachment #149464 - Flags: approval1.7.6?
Attachment #149464 - Flags: approval1.7.6+
Attachment #149464 - Flags: approval-aviary1.0.1+
retroactively marked, perhaps, but drivers consensus was reached prior to checkin. Now checked in to 1.7 branch as well.
Keywords: fixed1.7.6
Depends on: 670068
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: