Why is trying to open a TOpenDialog spawning a ton of threads?
Asked Answered
D

1

16

I've got a very simple form with a TOpenDialog and a button on it. When I press the button, it calls Execute on the dialog. If I watch in the debugger, the act of opening the dialog box spawns something like 14 threads, and they don't go away when I close the dialog box either.

Anyone have any idea what's going on with that?

Darb answered 10/9, 2011 at 17:10 Comment(19)
Only 14? It feels like an entire operating system is booting... Personally, I have never understood why it takes so long to display a file dialog.Romie
And +1, for you made me learn something new. This remarkable delay only appears when you run an app through the debugger.Romie
I remember reading that the common file dialogs end up loading all the shell extensions so those DLLs could be doing god knows what.Kiethkiev
@Luke: OK, that makes sense. Do you remember reading about any way to unload them afterwards?Darb
@Mason: My guess is that you don't need to care about them at all. After all, neither Microsoft nor Embarcadero are stupid... And, as I just learned, the terrible delay only appears when you run your program through the debugger. Do you have any particular reason why this is an issue for you?Romie
@Andreas: Because eventually some of those threads terminate, and when they do it makes my sound pop, which is very annoying.Darb
The shell spins up threads to enumerate the folders. If you have 14 new threads you should remove some shell extensions.Slemmer
Hm... I get 23 new threads. And I very seldom install things.Romie
@Mason: if that popping sound is somewhat predictable, you could try finding the offending thread using Procmon. Look what threadid's get loaded, wait for the sound to occur and search for any .wav in the output. (By default, the threadid's are not visible in procmon's output, you'll have to add them). If you find the offending thread, you can start making some assumptions as to what it is and why it is occuring.Justificatory
@Lieven: it's not a thread, it's any and all of them. I'm playing music, (unrelated to all these Explorer threads,) and when they clean up they make the music pop.Darb
@Mason: I don't understand why that would happen... Old computer and/or OS (that is, XP)?Romie
@Mason - iTunes by any chance?Justificatory
@Andreas: OK, on further testing, apparently it's only happening under the debugger. So not such a big deal after all.Darb
@Lieven: No, libmodplug actually.Darb
@Mason - iTunes add's a system sound for a Page Load Complete. This also made some kind of popping sound driving me nuts (axloadcomplete.wav to be exact). Removing the sound from the event resolved that for me and I used the method described to find what was producing the sound in the first place. If it's not bothering you, it's not worth the time trying to search if libmodplug has an identical feature.Justificatory
@Lieven: No, it's nothing like that. Libmodplug is a simple music decoding library, and the only sounds it produces are the ones that are in the music file.Darb
That's the answer, @Luke. Please post it as an answer.Wondawonder
Windows shell happens, what else? You got fully functional explorer view in your dialog, hence the overhead.Unceremonious
@Mason Wheeler: Increase priority of your music playing thread.Willman
I
8

Imagine you want to show your friends how beautiful the Pacific Northwest is. You decide to set off on a trip to snap a few photos of sunset over the Pacific. What you really care about is the image files making their way home, where they can be uploaded to the Facebook. In reality the camera, lenses and the tripod need to be hauled over the Olympics and back. You also need to bring the photographer (yourself) who will set the camera up and press the shutter. The photographer needs to be moved there and back in relative comfort, so you take a seat on which the photographer will rest while making the trip. This seat is enclosed in a shiny metal box with a bunch of other metal, glass and rubber parts some of which are turning and reciprocating. In the end, about two tons of stuff (and a living human being) taking a multi-hour trip, burning gallons of hydrocarbon liquid -- with the goal of moving a few bits of information from the shore to the internet.

Exactly the same thing happens with your application. When the user wants to open a file using "Open File" dialog box, the user expects to be able to:

  • navigate to the directory containing the file (The directory may be on local hard drive or CD/DVD/BR or network drive or archive, etc. The media may be encrypted or compressed, which needs to be displayed differently. The media may not be plugged in, for which the user might need to be prompted. The media may require user's credentials, which have to be asked);
  • connect to a new directory using its URI/UNC (map the drive);
  • search the directory for some keywords;
  • copy/delete/rename some files;
  • see the list of the files in that directory;
  • preview the content of each file in the directory;
  • select which file to open;
  • change his/her mind and decide not to open the file;
  • do many other file-related things.

The OS lets all this to happen by essentially giving your process most of the Windows Explorer functionality. And some of it has to happen in the background, otherwise the users will complain about how unresponsive the Open File dialog is. The obvious way to run some tasks in background is to run them on different threads. So that's what we see.

What about the threads left behind, you ask? Well, some of them are left there for the case the user will decide to open another file: it saves a lot of time, traffic and typing in this case. That custom authentication used for this one particular process last time? -- stored. The preview icons for those pesky PDFs? -- still there. The length and bitrate for every movie in the directory? -- still available, no need to re-parse them.

Of course the threads did not just magically appear by themselves. Check out how many DLLs have been mapped into the process. Looking at some of them one can get quite an interesting picture of what functionality has been added.

Another interesting way to look at it would be to dump the callstacks at the moment every thread gets created. This shows which DLL (and sometimes which object) created them. Here's how a x64 Win7 creates all the threads. One can find the Explorer frame's thread getting created; some OLE activity which will be used to instantiate file filters, some of which can generate preview icons, overlays and tooltips; few threads belonging to search subsystem; shell's device enumerator (so if the user plugs in a new device, it will automatically appear in the open dialog); shell network monitor (ditto) and other stuff.

The good news is it happens fast and doesn't add too much overhead to your process. Most of the threads spend most of the time waiting for some seldom events (like USB key being plugged in), so the CPU doesn't spend any time executing them. Each thread consumes 1MB of virtual address space in your process, but only few 4Kb pages of actual physical memory. And most, if not all of those DLLs did not use any disk bandwidth to be loaded: they were already in RAM, so they just got mapped into your process for almost free.

In the end the user got a whole lot of useful functionality in a snappy UI, while the process had to do very little to achieve all that.

Into answered 14/9, 2011 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.