In a future version, we will make GetClip/SetClip a bit more robust by having it wait and retry a few times on failure.
What you're seeing happens because the clipboard is a shared resource and can only be open by one program at a time. After you set the clipboard, any programs which monitor it will open it and have a quick look at it, which means if you rapidly set/get the clipboard in succession, either or both can fail.
This sounds worse than it is, however. The busy period is usually only for a split second after the clipboard is modified, and the clipboard should only be modified by a user action, and also only read from by a user action as well (except with clipboard monitoring tools, which will handle the busy situation as it is normal for them to encounter it).
If it happens more often or with normal use of the clipboard, it can indicate a poorly written program is holding the clipboard open for long periods of time (possibly forgetting to close it at all, or not closing it as soon as it could before doing other work).
However, while the future change will fix this demo script in most cases*, that you're running into the problem makes me suspect you're writing a script that uses the clipboard as a conduit for data to be sent between two scripts or a script and a command. If so, that is absolutely not what the clipboard should be used for, and it would be better to change to another method (e.g. storing the data in a variable or temp file).
Nothing should trash what's in the clipboard to get something done (unless the user has asked for and wants the clipboard to be changed, of course) and the clipboard is inherently unreliable as a conduit between pieces of code which do things faster than people can click/type (which is the speed the not-very-well-designed clipboard API was made to work at, back in the 1980s or whenever).
(*It'll still fail if the clipboard is held open for a second, or keeps being re-opened by something at just the wrong times.)
A user in the german forum asked how to get rid of trailing blanks in clipboarded text, so it can be used correctly with {clip} in a Rename command he uses often. I then added the TRIM functionality to the ClipboardEx script command, to allow easy removing of unwanted characters whenever needed. I liked the idea to provide a script command solution, because that would keep him cosy in the old-button world. o)
ClipboardEx TRIM
Rename TO "{clip}_foobar.txt"
Thanks for explaining why we see the described, much appreciated. I also understand that the clipboard is not the right tool to move data between programs or scripts. In this case though, trimming the clipboard feels kind of "ok" to me, since you don't need to touch the actual command in use. But I also totally see, that handling the clipboard this way can lead to unexpected results.
To get around the issue for now and most of the time, I guess I can wait and see for myself if the value I set has arrived?
Like so? That's probably not much different compared to what you would code into DO?
var clipData = "myclipdata", i=0
while (1){
DOpus.SetClip(clipData);
DOpus.Sleep(20);
if (DOpus.GetClip("text") == clipData) break;
if (i++>100) throw new Error("Setting clipboard failed.");
}
That makes sense. A command to modify what's in the clipboard is one of the few times when you indeed would want a script to get and then set the clipboard.
I'm surprised if a clipboard-trim script is running into problems regularly, though, unless there's something on the system which is doing strange things with the clipboard (which can happen). It's usually only a problem if you set-then-get, not if you get-then-set.
(The retry loop there could work as a temporary workaround, but it's also doing a set-then-get with a fixed delay, which could mean that the cure itself keeps triggering the problem, running into the other program(s) that are reacting to the set and generally taking about the same amount of time on each attempt.)
It might be worth running Clipboard Owner Debug to see if something is keeping the clipboard open more than it should on your system.
Wah, the snippet was wrong, setting the clipboard was meant to happen before the loop, then wait for things to settle within.
var clipData = "myclipdata", i=0
DOpus.SetClip(clipData);
while (1){
if (DOpus.GetClip("text") == clipData) break;
if (i++>100) throw new Error("Setting clipboard failed.");
DOpus.Sleep(20);
}
I ran your Clipboard Owner Debug tool, nice one! o) But it did not show any unusual things or owner-times going on, despite "dropbox.exe" and "mstsc.exe" being listed. I can see why "mstsc.exe" shows up (RDP session running), but wonder what "dropbox.exe" is doing there. Suspicious.
That seems reasonable. It may also make sense to stop when GetClip returns any non-empty string, without checking for a match.
The SetClip part can still fail, but it's much less likely since the problem is usually triggered when the clipboard changes, not before. (Still, the fix we have in the pipeline will automatically retry both.)
I see, I will change that to "succeed" on any string returned.
Maybe offtopic or not:
In case I run SetClip("data") in a loop without pausing, it seems to work quite ok everytime (returns "undefined" each time and at least never throws an exception). But if SetClip() is called with no arguments (to clear the clipboard), it seems it raises an exception occassionally. I was not able to find the clipboard still containing data in either case, so I wonder why it raises the exception. The GetClip() on the other hand, does not throw an exception, even though it obviously fails quite often. Maybe that is something that can be "adjusted" as well if time allows. o)
Output:
0 not failed
1 not failed
2 failed
3 not failed
4 failed
5 not failed
6 failed
7 not failed
8 failed
9 not failed
10 failed
11 not failed
12 not failed
13 not failed
14 failed
15 failed
Yes of course, I do not make use of / rely on the returned values from SetClip(), I was just testing.
It's more the exceptions that need care if these have any meaning, so do they tell me anything?
0 not failed
1 not failed
2 failed
3 failed
4 not failed
5 failed
6 not failed
7 failed
8 not failed
9 not failed
10 not failed
11 not failed
12 not failed
13 not failed
14 not failed
15 not failed
PS: Adding DOpus.Delay(5); into the try{} block, right above SetClip() seems to fix the exceptions.
It looks like if SetClip() is called too early, it will break.