Tuesday, October 25, 2011

Gotcha of the Day: Scripting a Non-Scripting Friendly Windows App

One of my clients wants to automate a process he usually has his team do manually. According to the docs, you can run the command like so:

  c:\app\foo.exe /silent input.txt

Invoking this from PHP should be trivial. Just invoke:

 exec("c:\\app\\foo.exe /silent $input");

But, there are a least two hurdles that kept this from being so simple:

(1) /silent, to the developers of this application, apparently meant "mostly silent." For reasons that I can only imagine, when you run foo.exe, it runs silently until the end, when it pops up a dialog box explaining that it's done and how many records it processed. This, of course, causes the above PHP command to hang, because the application never truly finishes. When I think about how absurd this decision was, to make /silent not really be silent, I can't help but work myself up into a rage. Amazing. What kind of amateur program would whip up an app that was almost, but purposely not, scriptable? Anyway, that's my reality, so no use in getting too upset about it.

(2) The application relies on various registry settings being properly set. Even then you can provide the input on the command line, another directory needs to be set for the /silent to work. Without having the correct registry settings, /silent is ignored, and a verbose wizard interface is launched. Exactly what you don't want to have happen from a PHP script.

Not being much of a Windows system person, I really wasn't quite sure how to attack solving this problem. I found PHP code for tweaking the registry, but the Windows dialog thing was still a road block. Until somehow, I managed to trip over AutoHotKey again. Previously, I used AutoHotKey to develop a quick shortcut for inserting the current date in Google Docs. Now I was starting to think I may be able to leverage some of the more sophisticated features.

My first solution involved AutoHotKey's SetTimer function. I attached a timer that checked every 300 milliseconds for the presence of the "all done" dialog box. When it found it, it closed it automatically. To my utter amazement, the code worked beautifully. Well, mostly beautifully. I quickly ran into the problem that running AutoHotKey from the command line was monitoring the user Ben's session, whereas, the PHP is running under the System user. That meant that just kicking off AutoHotKey as Ben, wasn't sufficient for detecting a random window in a PHP process.

After much fiddling, I arrived at a solution that I'm generally pleased with. Rather than using a timer, I have my PHP script call a single shot AutoHotKey script. This script, in turn, kicks off foo.exe. That is, my PHP file says:

 exec("c:\\apps\AutoHotKey\\AutoHotKey /f c:\\apps\foo_launcher.ahk");

And foo_launcher.ahk consists of:

;; Kick off foo.exe and deal with the Windows based annoyances that keep
;; it from being cleanly scriptable.

;; This is critical, without it, the correct window was never visible.
DetectHiddenWindows, On

Gosub Setup
Gosub Go

      , Software\Foo\Settings, LoadDirectory
      , C:\app\foo\incoming

      , Software\Foo\Settings, SaveDirectory
      , C:\app\foo\outgoing

  Run, C:\app\foo.exe /silent c:\app\foo\incoming\data.txt
  WinWait, Foo Finished

Essentially, the script starts off by setting various registry settings, kicks off foo.exe and waits around for the window to appear. When it's found, it poofs it, and we're done.

Because the script is run directly, I don't need to worry about having the the host Windows box pre-configured to use AutoHotKey. Nor do I need to worry that AutoHotKey may die and my PHP script will be broken.

A Few Words About AutoHotKey

AutoHotKey really is an amazing tool. It may be up there with tools like Netcat or Apache mod_rewrite -- terse and confusing to learn, but exceedingly powerful, and capable of being beneficial for years to come. Tasks that would be painful, like say reading and writing from the registry, are trivial in AutoHotKey. Forget Windows batch files, AutoHotKey, despite its clunky syntax, is going to be way more powerful and simpler to use.

It may also be a handy way to learn programming. That's because the emphasis is on getting stuff done, and the programming concepts become secondary to that.

It's worth learning today.

No comments:

Post a Comment