Saturday, October 25, 2014

#1 2007-02-15 04:29:37 am

gannet
Member
Registered: 2007-02-15
Posts: 69

Long delay when tell Finder from drop handler

I have a drop area in my application where I can drop files and folders but I've noticed that if I tell Finder to do anything - anything at all - from within any drop handler (or from any other method further down the call stack from the drop handler) then there is a long delay of about 10 seconds during which both the application and the Finder become unresponsive before the Finder actually does what is asked. A search of these forums found this topic where someone else seemed to be having the same problem but apparently worked around it by using 'ignoring', which is not suitable in my case. I did manage to come up with one workaround of setting a boolean in the drop handler instead and then letting the idle handler pick it up after the drop handler finishes, though there is still a delay of up to second before the idle handler kicks in. The real question is why does this happen and is there any real solution to it?

Offline

 

#2 2007-02-15 02:25:06 pm

SuperMacGuy
Member
From: Amish Country, Lancaster, PA
Registered: 2004-06-23
Posts: 416
Website

Re: Long delay when tell Finder from drop handler

can you replace any of the Finder stuff with System Events? i had this same problem, when reading a lot of files that were in a folder i dropped on. But i used sys events and the delay is practically 0 now, unless the list is big (over 100 items maybe?). but not all finder commands are duplicated in sys events.


Scripts for InDesign: http://chris.paveglio.com
ASOC & Cocoa, specializing in InDesign, Photoshop, Ai, FileMaker

Offline

 

#3 2007-02-15 05:04:50 pm

gannet
Member
Registered: 2007-02-15
Posts: 69

Re: Long delay when tell Finder from drop handler

The one and only call I make to the Finder is to get 'entire contents' of folders. That doesn't exist in System Events but perhaps I could make a method to recursively get contents? Though I can't seem to work out how to simply get the contents of a folder. Still, I'd really like to know why it happens. Everything works fine if I drop files onto the app's icon or open files from within the app. It's just the drop handler that's causing the delay.

Offline

 

#4 2007-02-19 08:41:20 am

SuperMacGuy
Member
From: Amish Country, Lancaster, PA
Registered: 2004-06-23
Posts: 416
Website

Re: Long delay when tell Finder from drop handler

Heh, that's the one command *I* would also like to be able to use for a drag n drop and it was slow for me.
tongue


Scripts for InDesign: http://chris.paveglio.com
ASOC & Cocoa, specializing in InDesign, Photoshop, Ai, FileMaker

Offline

 

#5 2007-02-19 09:00:11 am

StefanK
Member
From: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 10610
Website

Re: Long delay when tell Finder from drop handler

gannet wrote:

The one and only call I make to the Finder is to get 'entire contents' of folders. .

Hi gannet,

this is exactly the reason for the delay.
"Entire contents" is quite slow, depending on the depth of the folder structure.
In many cases the shell commands find or mdfind (using spotlight) are much faster


regards

Stefan

Offline

 

#6 2007-02-19 10:04:06 am

jobu
Member
From: phxaz
Registered: 2004-01-17
Posts: 908
Website

Re: Long delay when tell Finder from drop handler

Using 'tell finder anything' in a drop handler has been a problem since the beginning.  I'm pretty sure that's the topic that led me to macscripter, in fact. tongue  Oh, the memories.  Anyways, you do need to find alternate methods if you want to do this in a reasonable amount of time.  There (at one time) was also a quirk/bug that made apps hang or crash when searching the root directory.  Assuming you're doing this in ASStudio, try checking out the NSFileManager methods "directoryContentsAtPath:" and "subpathsAtPath:" to get this sort of information.  Unlike the finder, it just returns an array of names of files and/or folders as strings.  No references, which is what's likely making the finder approach more cumbersome.

Applescript:

set fileNames to (call method "directoryContentsAtPath:" of (call method "defaultManager" of class "NSFileManager") with parameter "/Users/jed/Desktop/"))

...EDIT...
OK, I did look into this a bit more, and I changed my original post to the working example above.  It returns a list of whatever is at the given path.  It does return all files, including invisibles, so you'll have to account for that.  Also, since it doesn't return references you'll have to construct a path for each item by joining the original path and the item.
...END EDIT...
j

Last edited by jobu (2007-02-19 10:55:30 am)


The saddest thing I ever did see was a woodpecker peckin' at a plastic tree.
- Shel Silverstein

Offline

 

#7 2007-02-19 08:47:38 pm

gannet
Member
Registered: 2007-02-15
Posts: 69

Re: Long delay when tell Finder from drop handler

Okay, thanks for that code, so I should use some sort of recursive method to get entire contents like that? The 'entire contents' command is so handy see, because not only do I not have to worry about making recursive methods to get contents of subfolders as well but it also excludes all invisibles (like Icon files), which is the desired behaviour in this case.

And just to make it quite clear to everyone, the 'entire contents' command does not cause any noticeable delay. It works perfectly when dropping onto the app icon (the open handler) or opening files from within the app. It's the act of calling the Finder from within the drop handler that is causing the delay. If I remove the entire contents command and put some other call to the Finder like 'display dialog "Hi"' then the delay will still happen when the drop handler is used.

Offline

 

#8 2007-02-22 06:33:28 pm

gannet
Member
Registered: 2007-02-15
Posts: 69

Re: Long delay when tell Finder from drop handler

I've just realised what's probably causing all this. When you drop files from the Finder to somewhere, the Finder waits for a response from the target (with a timeout of 20 seconds) so it can tell if the drop was successful or not and spring the files back where they came from if it was unsuccessful. So while the Finder is waiting for this response you can't tell it to do anything else and have no choice but to wait for it to timeout before you can continue. Even if you don't make any call to the Finder though, if whatever you're doing with the files takes longer than 20 seconds then the Finder will assume the drop was unsuccessful (and will still be frozen for those 20 seconds).

So you really need to return from the drop handler as soon as possible, regardless of what you're doing, but what's the best way to do that if you want to start a long process on the files after the drop handler returns? Is there some way to call a subroutine but then carry on with the current routine without waiting for the subroutine to finish?

Offline

 

#9 2007-04-06 02:08:15 pm

MarkDouma
Member
From: Grand Rapids, MI USA
Registered: 2003-11-07
Posts: 79
Website

Re: Long delay when tell Finder from drop handler

As jobu mentioned, this has long been a problem since the beginning of time. I spent several weeks back in late 2003/early 2004 trying to make heads or tails of this in working on my dfontifier application.

What essentially is happening is that the Finder initiates a drag-and-drop operation of files to your application. If you involve the Finder in any way during the processing of those files, your application immediately tries handing those files back to the Finder, but the trouble is, the Finder is still busy with the drag-and-drop event and handing the files off to your application, which is then, once again, trying to shove them back to the Finder, which is again, trying to.....and so on, ad infinitum. You basically get this infinite loop type thing, where everything freezes for 10 to 20 seconds and then suddenly your app and the Finder seem to snap out of it and start doing what you want.

My solution back then was to try to use System Events as much as possible to process files. The trouble I ran into back then, however, was that System Events in its earlier versions was still rather buggy. Actually, I'm sure there are still some bugs (I think I noticed one the other week when trying to set the file type and creator codes of files on Intel-based Macs: the file type and creator codes of the files were set to the byte-swapped opposite of what I specified. For example, a font suitcase has a file type of 'FFIL' and creator code of 'DMOV', and when using System Events to set files to that on an Intel Mac, the files actually ended up with a file type of 'LIFF' and creator code of 'VOMD'. One more thing to watch out for). System Events has gotten a bit better since then so you might try using it. Though I had no clue how it worked at the time, I also ended up using NSFileManager calls to set file type and creator code of files. (All the bugs in AppleScript is what eventually drove me to learn Objective-C and C; though AppleScript does have it's advantages for some things).

Anyway, hope this helps....

Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Offline

 

#10 2007-04-06 02:24:08 pm

MarkDouma
Member
From: Grand Rapids, MI USA
Registered: 2003-11-07
Posts: 79
Website

Re: Long delay when tell Finder from drop handler

gannet wrote:

I've just realised what's probably causing all this. When you drop files from the Finder to somewhere, the Finder waits for a response from the target (with a timeout of 20 seconds) so it can tell if the drop was successful or not and spring the files back where they came from if it was unsuccessful. So while the Finder is waiting for this response you can't tell it to do anything else and have no choice but to wait for it to timeout before you can continue. Even if you don't make any call to the Finder though, if whatever you're doing with the files takes longer than 20 seconds then the Finder will assume the drop was unsuccessful (and will still be frozen for those 20 seconds).

So you really need to return from the drop handler as soon as possible, regardless of what you're doing, but what's the best way to do that if you want to start a long process on the files after the drop handler returns? Is there some way to call a subroutine but then carry on with the current routine without waiting for the subroutine to finish?

I not exactly sure how I'd accomplish this in AppleScript, so I can tell you what I'd try to do in Objective-C; maybe jobu can "translate" it to an equivalent in AppleScript. Basically, everything in OS X is "event-based". Apps sit at rest (most of the time) until the user initiates some event which causes a response. What if during the on drop handler, you simply recorded the list of files that were dropped in a global type variable. Then return yes, and worry about actually processing the files from within the "on idle" handler. Use a global variable flag like "needToProcessFiles" to determine whether during idle events you should just return 1 and check back in a second, or processFiles(), for example. Basically, this is like doing

[droppedFiles release];
droppedFiles = [[[sender draggingPasteboard] objectForKey:NSFilenamesPboardType] retain];
[self performSelector:@selector(processFiles:) withObject:self afterDelay:0];

rather than:

[droppedFiles release];
droppedFiles = [[[sender draggingPasteboard] objectForKey:NSFilenamesPboardType] retain];
[self processFiles:droppedFiles];



The performSelector: method allows your application event stack to return to the base level before starting the next command; also, any other operations that may have built up in the event queue can be slipped in and performed before the performSelector: executes.

Anyhow, posting this FWIW....

Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Offline

 

#11 2007-04-18 01:31:08 am

gannet
Member
Registered: 2007-02-15
Posts: 69

Re: Long delay when tell Finder from drop handler

Thanks Mark, I actually did end up using the idle handler to do the processing (as I mentioned in the first post) though I have to wait up to a second for the idle handler to activate. So this is the best that can be done, is it? One second delay is acceptable, I just thought it unlikely that there would be no other solution.

Now I have a problem of the program crashing after it finishes doing its thing on a Mac OS 10.3 system and I can't see anything that might possibly be causing it hmm

Last edited by gannet (2007-04-18 01:33:49 am)

Offline

 

#12 2009-04-06 04:33:40 pm

daehl
Member
Registered: 2007-03-08
Posts: 32

Re: Long delay when tell Finder from drop handler

I just encountered the "on drop long delay" for the first time today while working on a script application.

I'm trying to update a table view with a list of files once a folder is drag & dropped onto a button.

I have noticed that the delay occurs regardless of whether I target the "Finder", "System Events" or even a "do shell script "ls -l"" command.
In each case my application hangs for about 20 seconds like what's described in this thread.

I notice the messages here are a bit old, has there been any recent developments in working around this problem? I suppose I should try to figure out how to use an NSFileManager call method to make this work. If anyone has any advice or examples I'd appreciate the assistance.

I'm trying to find all visible text files that begin with a specified string. So this is what I was using up until I noticed the delay:

Applescript:

tell application "System Events"
   set R to name of every file in (thePath as alias) whose file type is "TEXT" and name begins with pImageID
end tell

I tried substituting the following, hoping that might avoid the "long delay" but it doesn't...

Applescript:

set x to every paragraph in (do shell script "ls -l " & POSIX path of thePath)
set text item delimiters to ""
set R to {}
repeat with i in x
   set o to offset of ":" in (i's contents)
   if o > 0 and i's contents begins with "-" then
       set end of R to (characters (o + 4) thru -1 of i's contents) as string
   end if
end repeat

Last edited by daehl (2009-04-07 03:07:37 pm)


Filed under: System

Offline

 

#13 2009-04-09 10:48:05 am

Matt-Boy
Member
Registered: 2005-10-21
Posts: 480

Re: Long delay when tell Finder from drop handler

I have been reading this post and it finally occurred to me that I DID have this problem once. I got around it by concluding the drop event and THEN dealing with the files and searching for sub-folders and all that. It sounds like that is what you ended up doing based on your first post.

I did it by setting a variable to true that an on idle loop is looking for. The drop happens, the variable for go ahead with this event is set to true, the drop event concludes returning true and then the on idle loop says, OK the variable is true so go to the subroutine that processes the file/folder that was dropped.

Here is some of my code if anyone wants to see the details of how to set that up:


Applescript:

property these_items : {}
property printAndPdfIsGo : false
property countCharFileNameIsGo : false

on activated theObject --Make sure the app is in Idle mode after any user cancels
   idle
end activated

on idle theObject --Call the appropriate sub-routine when the variable has been set to true by the on drop handler
   if printAndPdfIsGo then my printAndPDF()
   if countCharFileNameIsGo then my countCharFileNameRoutine()
   return 1
end idle

on open theFilesFromDropOnAppIcon
   --Only need this if the user can drag and drop files on the app icon itself.
end open
on awake from nib theObject
   if class of theObject is image view then
       tell theObject to register drag types {"file names"}
   end if
end awake from nib
on drag entered theObject drag info dragInfo
   set image frame style of theObject to button frame --This makes the drop area button change appearance when you drag a file over it
end drag entered
on drag exited theObject drag info dragInfo
   set image frame style of theObject to groove frame --This sets the button back if you stop dragging over it
end drag exited
on conclude drop theObject drag info dragInfo
   --Overrides default drop behavior
end conclude drop

-- The "drop" event handler is called when the appropriate type of data is dropped onto the object. Info about the drop is contained in the "dragInfo" object.
on drop theObject drag info dragInfo
   set image frame style of theObject to groove frame
   -- Get the list of data types on the pasteboard
   set dataTypes to types of pasteboard of dragInfo
   -- We are only interested in "file names" data type
   if "file names" is in dataTypes then
       -- Initialize the list of files to an empty list
       set theFilesFromDragDropEvent to {}
       -- We want the data as a list of file names, so set the preferred type to "file names"
       set preferred type of pasteboard of dragInfo to "file names"
       -- Get the list of files from the pasteboard
       set theFilesFromDragDropEvent to contents of pasteboard of dragInfo
       -- Make sure we have at least one item
       if (count of theFilesFromDragDropEvent) > 0 then
           --Convert list of POSIX file paths to list of aliases to match plain AppleScript On Open behavior
           set these_items to {}
           repeat with thisConvertPath in theFilesFromDragDropEvent
               set these_items to these_items & ((POSIX file thisConvertPath) as alias)
           end repeat

           --Turn on variable for the appropriate sub-routine. This variable is referenced in the On Idle loop
           if name of theObject is "dropzone_PrintPDF" then --This is the Main InDesign Print and PDF dropzone
               set printAndPdfIsGo to true
           else
               set printAndPdfIsGo to false
           end if

           if name of theObject is "dropzone_CountCharacters" then --This is the Count Characters in file name dropzone
               set countCharFileNameIsGo to true
           else
               set countCharFileNameIsGo to false
           end if

       else --No file so return false on drop
           set preferred type of pasteboard of dragInfo to ""
           return false
       end if
       set preferred type of pasteboard of dragInfo to ""
       return true
   else
       -- Set the preferred type back to the default
       set preferred type of pasteboard of dragInfo to ""
       return false
   end if
end drop

on countCharFileNameRoutine()
   set countCharFileNameIsGo to false
--Here is where you would start your processing of the dropped files and folders. The below just processes the first item
   set AppleScript's text item delimiters to ":"
   set TempFileName to last text item of ((item 1 of these_items) as string)
   set AppleScript's text item delimiters to ""
   activate
   display dialog ("Number of characters: " & (count of characters in TempFileName)) buttons {"OK"}
   beep 1
end countCharFileNameRoutine

I'm sure this isn't the only or even best way to do this but it worked well for my app.

Model: iMac Intel 10.5.5
Browser: Firefox 3.0.2
Operating System: Mac OS X (10.5)

Last edited by Matt-Boy (2009-04-09 10:50:23 am)


Filed under: drop

Offline

 

#14 2009-04-10 01:10:20 pm

daehl
Member
Registered: 2007-03-08
Posts: 32

Re: Long delay when tell Finder from drop handler

Thanks for the example Matt-Boy.

Myself, I'd like to avoid using an idle handler to bypass this problem. It just seems wasteful and inefficient to me. I've tried to develop an alternative using Jobu's call method example.  I've made a bit of progress:

Applescript:

set theFolderPath to POSIX path of (path to desktop) -- an example of a folder posix path to search

set fileNames to (call method "directoryContentsAtPath:" of (call method "defaultManager" of class "NSFileManager") with parameter theFolderPath)
set R to {}
repeat with i in fileNames
   set ipath to theFolderPath & i's contents
   set attr to (call method "fileAttributesAtPath:traverseLink:" of (call method "defaultManager" of class "NSFileManager") with parameter ipath)
   if attr's |NSFileType| = "NSFileTypeRegular" then set end of R to ipath
end repeat

This code allows collects a list (R) of posix paths for all "files" in a folder.  And it does NOT incur the "long-delay" when run within an 'on drop' handler. Ultimately, I'd like to filter the results by file type, but I can't yet find a method that can identifies files by their (HFS) file type. Though actually, I'd be satisfied if I could just narrow it down to "documents" (and ignore aliases, folders and invisible items)

I notice that Apple's NSFileManager_Class documentation lists a File Attribute Key named "NSFileHFSTypeCode" but the method "fileAttributesAtPath:traverseLink:" does not seem to return it.

There's got to be a way to access it, but I'm still at the bottom of the Cocoa learning curve, and am only slowly making progress on my own.

Again, if anyone with Cocoa experience can offer suggestions, I'd be very appreciative. Thanks!

Last edited by daehl (2009-04-10 02:20:42 pm)

Offline

 

#15 2009-04-10 03:55:39 pm

daehl
Member
Registered: 2007-03-08
Posts: 32

Re: Long delay when tell Finder from drop handler

daehl wrote:

I notice that Apple's NSFileManager_Class documentation lists a File Attribute Key named "NSFileHFSTypeCode" but the method "fileAttributesAtPath:traverseLink:" does not seem to return it.

Oh, wait. It seems that method fileAttributesAtPath:traverseLink: DOES return a File Attribute Key "NSFileHFSTypeCode" (but only for "NSFileTypeRegular" NSFileTypes), And it returns them as a 32-bit unsigned integer!

So I created a quick handler to convert from 32-bit unsigned integer to text:

Applescript:

on coerce32BitIntToText(theNumber)
   set R to {0, 0, 0, 0}
   repeat with i from 4 to 1 by -1
       set n to theNumber mod 256
       set item i of R to ASCII character n
       set theNumber to (theNumber - n) / 256
   end repeat
   return R as string
end getHFSType

and then I can filter my results by HFS file type:

Applescript:

set theFolderPath to POSIX path of (path to desktop) -- an example of a folder posix path to search

set fileNames to (call method "directoryContentsAtPath:" of (call method "defaultManager" of class "NSFileManager") with parameter theFolderPath)
set R to {}
repeat with i in fileNames
set ipath to theFolderPath & i's contents
set attr to (call method "fileAttributesAtPath:traverseLink:" of (call method "defaultManager" of class "NSFileManager") with parameter ipath)
if attr's |NSFileType| = "NSFileTypeRegular" and my coerce32BitIntToText(attr's |NSFileHFSTypeCode|) = "TEXT" then set end of R to ipath
end repeat

Problem solved!

Now I can drop a folder onto a button and get a list of matching "TEXT" files from within that folder without the "long delay". Woohoo!

Last edited by daehl (2009-04-10 04:03:11 pm)

Offline

 

#16 2010-11-18 06:08:09 pm

gannet
Member
Registered: 2007-02-15
Posts: 69

Re: Long delay when tell Finder from drop handler

I just came up with a new solution to this that avoids any delay! The whole problem is an issue of threading. If AS Studio had easy thread support then you could just start the process in a new thread and let the drop handler return immediately. So how can we do this? "ignoring application responses" never seems to work. My solution looks something like this:

Applescript:


global DroppedFiles

on open filenames
   if filenames is {} then
       process(DroppedFiles)
   else
       process(filenames)
   end
end

on drop theObject drag info dragInfo
   set preferred type of pasteboard of dragInfo to "file names"
   set DroppedFiles to contents of pasteboard of dragInfo
   do shell script "osascript -e 'tell application \"" & (path to me) & "\" to open {}' &>/dev/null &"
   return true
end

This is taking advantage of the fact that we can spin off a shell script by piping all output to /dev/null and backgrounding the task. And the shell script uses osascript to tell this app to run the open handler with an empty list. The open handler looks for the empty list, and processes the DroppedFiles instead of filenames it might normally open. It's still a sort of kludge but it means there is no delay, rather than the up-to-1-second delay from the idle method.

[edit] Just noticed that DJ Bazzie Wazzie discusses threading with osascript in a recent topic in unScripted smile. The problem here is a bit simpler though - all we're trying to do is trigger some unique event in a non-blocking way. Or get our app to open the files in a non-blocking way - NSWorkspace's openFile method can do this for one file, but not multiple.

Last edited by gannet (2010-11-25 11:07:18 pm)

Offline

 

Board footer

Powered by FluxBB

[ Generated in 0.048 seconds, 10 queries executed ]

RSS (new topics) RSS (active topics)