1

I have a Delphi (BDS 2006) application which sends keystrokes to QuickBooks accounting software to traverse QuickBooks forms (invoices), copy text from the current edit control to the Windows clipboard (to gather data), do some calculations based on the gathered data, and finally write results on the form by sending keystrokes.

This application has been developed over a number of years, uses extensive (for me at least) Windows API techniques to identify the foreground window, focused window, etc., and is used by dozens of users worldwide...which I only tell you as evidence that it works on a lot of systems.

But not all. Lately I'm getting a lot of reports of failures, on Windows 7 systems (the version of QuickBooks doesn't seem to matter). Debugging versions sent to the customers who've reported problems show that it is not copying anything to the clipboard--though it still seems to be able to do everything else (send keystrokes to traverse the form, and keystrokes to paste in the calculation result...which unfortunately, is now always zero because no data was gathered.)

Here's the code I use to send a WM_COPY message to the edit control window in QuickBooks. (We can't get this code to fail here, on either XP or Windows 7 systems--but it doesn't work for several users.)

var
  iResult : DWORD;
begin
  ...
      //Edit control has the focus on the QB form, so try to copy its contents
  if SendMessageTimeout(Wnd, WM_COPY, 0, 0,
      SMTO_ABORTIFHUNG or SMTO_NORMAL,
      2000,
      iResult) = 0 then begin                 //0 = Failed or timed out

        //NOTE:  Users DO NOT get the following message--the 
        //SendMessageTimeout() simply returns without error, as if the 
        //WM_COPY is being sent correctly.

    ShowMessage('SendMessageTimeout FAILED');
    Abort;    
  end;

            //At this point, the clipboard has nothing on it, on users'
            //machines where it fails to work.
  ...   
end;

Not wanting to wear out the patience of the end users to whom we're sending debug versions, I'm looking for ideas before we send out anything else for them to try/test...

Notes/Questions:

  • All other keystrokes are sent via SendInput, and they work fine. I believe we began using SendMessageTimeout(WM_COPY) instead of sending Ctrl-C as a keystroke for speed reasons--it allowed us to immediately access the clipboard on return, instead of waiting an unknown/indefinite amout of time for the Ctrl-C to be processed by QuickBooks.

  • I believe we've asked users to try RunAs...Administrator on our application, but that had no effect (I'll have to verify that's been done).

  • I'm wondering if the problem could be due to UAC conflicts? Our application currently is not digitally signed and uses no manifest. I've been reading about adding a manifest with UIAccess=True in it. But if our application can already send keystrokes to QuickBooks without problems, would setting UIAccess=True have any effect on allowing the SendMessageTimeout() to succeed? And will I need to use a digital cert. to get the UIAccess setting to have any effect?

  • If SendMessage won't work without digitally signing & UIAccess in the manifest, is it possible we could fall back to sending Ctrl-C as a keystroke? (I wouldn't think so; surely Microsoft wouldn't allow that end-run around a security concept.)

I'd appreciate any comments to straighten out my thinking...

Mark Wilsdorf
  • 751
  • 1
  • 5
  • 18
  • 4
    Perhaps a silly question, but if you have an edit control, and wish to know what it contains, wouldn't it be easier to send `WM_GETTEXTLENGTH`/`WM_GETTEXT` instead of having it copy its contents to the clipboard? –  Dec 07 '12 at 22:43
  • 1
    Yes, you have to digitally sign your executable if you use "uiAccess=true". Whether or not that will solve the issue, I do not know. – Remy Lebeau Dec 07 '12 at 22:45
  • I agree with hvd that it would be cleaner to use wm_getText. As for the problem at hand, could it be that the text for some unknown reason isn't selected in the text box you are sending the message to? – 500 - Internal Server Error Dec 07 '12 at 22:49
  • 3
    Just as a comment: Why are you resorting to this instead of using the published QuickBooks API? It has an XML-based way of transferring info into and out of QuickBooks to other applications, and it's a lot less kludgy and requires a lot fewer hurdles to be jumped. It's been around since at least 2000; I was involved in the early testing and actually wrote an app (for a client of mine) that was sold via the QB app store at the time. – Ken White Dec 07 '12 at 22:52
  • 1
    Does the client run QuickBooks elevated? – David Heffernan Dec 07 '12 at 22:59
  • Have you used the GetLastError function to see what possible error you are running into? – Tom A Dec 07 '12 at 23:22
  • @hvd - I believe we tried WM_GET___ some long time ago, but yeah, that's certainly worth a try. – Mark Wilsdorf Dec 07 '12 at 23:42
  • @Ken - the QuickBooks SDK doesn't provide live access to anything in QuickBooks--only to stored data. We do calculations live on QuickBooks forms--which is why our users love us! – Mark Wilsdorf Dec 07 '12 at 23:48
  • @David - I'll have to double-check with these users, but since some run QuickBooks in a network config. I assume at least some are running as Standard users. – Mark Wilsdorf Dec 07 '12 at 23:49
  • @Tom - I haven't...but since the function always returns 0 I wouldn't expect GetLastError to hold an error code value. – Mark Wilsdorf Dec 07 '12 at 23:50
  • Always return non-zero, you mean, presumably. – 500 - Internal Server Error Dec 08 '12 at 00:25
  • @500 - I said it wrong. I mean *never* returns 0. If the return value were 0 the users would get an error message. None get a message. – Mark Wilsdorf Dec 08 '12 at 14:03
  • see: https://stackoverflow.com/questions/360289/looking-for-an-alternative-to-windows-messages-used-in-inter-process-communicati – Gabriel Feb 01 '19 at 14:12

2 Answers2

4

This might be related to "User Interface Privilege Isolation" (UIPI) instead of UAC. Check the integrity level of each process. A lower-integrity process is not allowed to send window messages to a higher-integrity process, unless the higher-integrity process explicitly allows it by calling ChangeWindowMessageFilter/Ex().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Wouldn't the SendMessageTimeout call fail in case it was an access rights issue? – 500 - Internal Server Error Dec 07 '12 at 22:51
  • @500 - I don't think so; I believe it would just return 0 (from what I've read today). – Mark Wilsdorf Dec 07 '12 at 23:51
  • @Remy - So in that case, if I understand, the solution is to digitally sign my application, include UIAccess=True in the manifest, and force limited-access users to see the UAC elevation prompt? If so, they won't appreciate it (thanks Microsoft), because the application is often run in a network config. of 5+ users...most of whom are Standard users. – Mark Wilsdorf Dec 07 '12 at 23:55
  • @Mark Wilsdorf You can try sending Ctrl-C instead of making UIAccess=True. – Torbins Dec 08 '12 at 09:42
  • 1
    By the way, digitally signed application with UIAccess=True won't trigger any UAC prompts if it's run from trusted location. – Torbins Dec 08 '12 at 09:44
  • @Torbins - Thanks, I wasn't clear on that. That helps a great deal. – Mark Wilsdorf Dec 08 '12 at 14:07
  • After more testing I believe UIPI is what bit us, so I'm giving Remy the credit. – Mark Wilsdorf Dec 08 '12 at 23:49
  • For all this talk of `uiaccess`, you need to sign your program and also put it in `system32`. The later is a game breaker surely. – David Heffernan Dec 13 '12 at 16:20
  • @DavidHeffernan: an app that is marked as `"uiAccess=true"` does not need to be put in the `system32` folder, just any folder that is writable only to admin users, such as `ProgramFiles`. – Remy Lebeau Dec 13 '12 at 22:42
  • My source was here: http://blogs.msdn.com/b/oldnewthing/archive/2012/12/13/10377004.aspx although your explanation makes more sense. – David Heffernan Dec 13 '12 at 22:57
  • @DavidHeffernan: my source is here: http://msdn.microsoft.com/en-us/library/bb625963.aspx: "The application must be installed in a local folder application directory that is writeable only by administrators, such as the Program Files directory. The allowed directories for UI automation applications are: %ProgramFiles% and its subdirectories. %WinDir% and its subdirectories, except a few subdirectories that are excluded because standard users have write access." – Remy Lebeau Dec 13 '12 at 23:20
0

Can you check in this systems Skype plugin for Internet Explorer (IE-Options-Programs-Add ons). There is a bugy version of this plugin who mess data on clipboard. If this plugin is installed, remove and test.