0

We currently have a couple of clients reporting that our copy-to-clipboard functionality in our Delphi XE2 Win32 app isn't working, and pasting into Excel afterwards results in the old clipboard content being pasted.

We are just copying CSV-like text using Clipboard.AsText, and added some debugging to ensure that we weren't getting silent crashes in the routines building up our CSV string. There's no exception handling around it, so any exceptions should just be displayed to the user (nothing reported).

Has anyone encountered anything like this before, and if so what are the best solutions for it? I notice that in C#-land, according to How to handle blocked Clipboard and other oddities the default behaviour there is to retry 10x with a 100ms delay. Unless that behaviour is written into the WinAPI the Delphi wrapper doesn't seem to do this?

I assume that this is a user-machine problem rather than something in our app, though they apparently don't experience this in other software. In-house we have one user that has the first Ctrl-C fail, but the second succeed (fairly reliably), though testing over 5 other machines didn't show anything.

Due to comments on missing code (I didn't put any originally, as there's nothing really there) - pseudocode implementation is

Data := BuildCSVString();
Clipboard.AsText := Data;
StatusBar.Caption := 'Data copied to clipboard';

So not much scope for failure.

Users see the StatusBar GUI update, so the Clipboard routine is running. Reports are that the clipboard remains unchanged, NOT that it is cleared, so the BuildCSVString routine isn't failing and returning '', either.

There's not much scope to build a reproducible bit of code here (otherwise I'd have probably fixed it by now), I was just wondering whether anyone else had come across this and the best way to solve it.

Community
  • 1
  • 1
Matt Allwood
  • 1,448
  • 12
  • 25
  • Hard to debug without code. That you have an in-house user with reported failures (needing to re-try) sounds like there is indeed an error in your code. Are you sure the code is actually executing in all cases? Maybe you're not catching the keyboard command because of a focus issue - how can we know? Only you can debug your code. I'd start with your reproducible in-house user's problem - set up a remote debug session and see what happens. – J... Nov 19 '15 at 14:59
  • 1
    So what does `BuildCSVString();` look like? How do you know it isn't rebuilding an old string? – J... Nov 19 '15 at 16:39
  • It iterates over a table and builds a CSV. It may as well be a string constant or returning a random set of characters for the sake of this question. I can guarantee that it's not the issue as it works on everyone else's machine, and it's not the sort of code that would work on one machine but not others – Matt Allwood Nov 20 '15 at 09:44

1 Answers1

0

Rule out excel by having the user paste into Notepad immediately after Excel appears to fail. This will tell you whether it's a problem with copying the data, or whether Excel is having trouble pasting the data. Or, if there is a hybrid clipboard issue, where data appears on the clipboard in multiple formats, from different sources (can happen).

Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
  • 2
    Also, since Windows Vista, if the Delphi app is running as administrator, the data put on the clipboard may not be accessible to applications not running with administrative privileges. – Stijn Sanders Nov 19 '15 at 14:47
  • @StijnSanders That's something I wasn't aware of. AFAIK there's no reason to run our app with Admin rights, but I've only inherited this codebase 2 weeks ago, so need to check up. I'll ask at tomorrows standup. – Matt Allwood Nov 19 '15 at 15:54
  • @StijnSanders If that was the problem then it would never work - it would not be intermittent, I should think. – J... Nov 19 '15 at 16:41
  • 1
    The `TClipboard.AsText` setter should raise an exception if the data cannot be placed on the clipboard. That being said, note that `AsText` in D2009+ uses the `CF_UNICODETEXT` format for text data, but CSV data should use the `CFSTR_CSV` format instead (use `RegisterClipboardFormat('Csv')` to get the format ID at runtime). Also, Excel is a little particular about the encoding of CSV data. It should be in UTF-8 instead of UTF-16 (you will have to use `TClipboard.SetAsHandle()` for that), and Excel works better when tab is used for the cell separator and LF for the row separator. – Remy Lebeau Nov 19 '15 at 18:21
  • 2
    You can (and should) have multiple formats on the clipboard at the same time, so you could put both `CF_UNICODETEXT` and `CFSTR_CSV` on the clipboard at the same time, and let Excel (or any other spreadsheet app, for that matter) decide which format it wants to use. Excel also supports an XML Spreadsheet format. – Remy Lebeau Nov 19 '15 at 18:24
  • @RemyLebeau Thanks - I've never really dealt with the clipboard formats before, so something I need to look into before committing. I think it may be a nicer solution for the user if it prevents them having to do the 'Text to columns' routine after pasting. Unsure whether it will fix the underlying issue of clipboard not being filled, but worth trying – Matt Allwood Nov 20 '15 at 09:49
  • Thinking about this further, the route I'd likely take is to just provide a tab-delimited form rather than a CSV and pass it as CF_UNICODETEXT, so it's not likely to solve the problem of the clipboard not getting filled. It is a suggestion that would improve the user-experience though, so I'll probably implement it. Confirmation that exceptions should be raised on a lock is useful though, although we're not seeing any. – Matt Allwood Nov 20 '15 at 11:40
  • Thanks for the tips, @RemyLebeau It was enough for my requirements to cast the string to `pAnsiChar`, and call via the hack: `TClipboardHack(Clipboard).SetBuffer(CF_CSV, p^, strlen(p));` – bvj Jul 12 '16 at 01:30
  • @bvj: You *should* be using the public `SetAsHandle()` method instead of the protected `SetBuffer()` method. `SetBuffer()` allocates an `HGLOBAL` handle via `GlobalAlloc()`, copies your input data into it, and then assigns that `HGLOBAL` to the clipboard the same way `SetAsHandle()` does. So you may as well use `SetAsHandle()` directly and not have to use a hack at all. – Remy Lebeau Jul 12 '16 at 01:35