Both my Fujitsu ScanSnap ix500 and ix100 scanners can be used from Windows to automatically scan to PDF.
PDF conversion is done through the included ABBYY FineReader 5.0 software.
However, on each scan, it keeps a dialog open with the scan results, even if scanning went fine.
When scanning lots of documents, lots of dialogs are open, causing two problems:
- a lot of memory and window handle resource usage
- this can be ~100 megabytes per instance
- a lot of disk usage:
- it keeps both the non-OCR and OCR PDF files active (only when closing, the non-OCR PDF file is deleted)
I wanted to close that dialog automatically, but none of the configuration settings allow it.
So I wrote a quick and dirty solution, that could have been in any tool supporting the Windows API and call backs. The solution below should easily translate to tools other than Delphi.
These are the only Windows API functions used:
- [WayBack] EnumWindows
- [WayBack] EnumChildWindows
- [WayBack] GetWindowText
- [WayBack] GetWindowTextLength
- [WayBack] IsWindowVisible
- [WayBack] GetWindow
- [WayBack] GetWindowLongPtr
- [WayBack] SendMessage
these types:
and these constants:
- [WayBack] BM_CLICK
- Simulates the user clicking a button. This message causes the button to receive the WM_LBUTTONDOWN and WM_LBUTTONUP messages, and the button’s parent window to receive a BN_CLICKED notification code.
- [WayBack] GW_OWNER
- [WayBack] GWL_STYLE
- [WayBack] WM_CLOSE
- [WayBack] WS_EX_APPWINDOW
The basic structure is an EumWindows call passing a callback that gets called for all top level Windows, then in the callback, for matching captions: call EnumChildWindows with another callback. In that callback, for matching captions and child captions, perform a click or close.
Related posts:
- [WayBack] EnumChildWindows already enumerates recursively – The Old New Thing
- [WayBack] How to iterate child controls in your Dialog? | Visual C++ Tips
- [WayBack] c++ – How to get handles to all windows of another application – Stack Overflow
- [WayBack] delphi – How to get captions of actual windows currently running? – Stack Overflow
- [WayBack] windows – Is the order in which handles are returned by EnumWindows meaningful? – Stack Overflow
- [WayBack] c++ – How can I simulate a button click given the handle to the button’s window? – Stack Overflow
Send a BM_CLICK message to the HWND of the button:SendMessage(hButton, BM_CLICK, 0, 0);That causes the button to receive WM_LBUTTONDOWN and WM_LBUTTONUP messages
- [WayBack] Delphi inspect external TLabels inside a TGroupBox via EnumWindows, Enumchildwindows or Spy++ – Stack Overflow
- [WayBack] winapi – How do I find all windows using C#? – Stack Overflow (which is of great help with the Windows API wrappers in C#)
Log of Windows related to both programs:
ParentHWnd=$00000000;HWnd=$00030602;IsVisible=-1;IsOwned=0;IsAppWindow=-1;WindowTextLength=33;WindowText="ABBYY FineReader for ScanSnap 5.0" > Recursive child windows for ABBYY ParentHWnd=$00030602;HWnd=$000205E2;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205E0;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205EC;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205EA;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=74;WindowText="Register your copy of ABBYY FineReader and receive the following benefits:" ParentHWnd=$00030602;HWnd=$000205E8;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=25;WindowText="- Free technical support;" ParentHWnd=$00030602;HWnd=$000205E6;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=51;WindowText="- Information about new versions of ABBYY products." ParentHWnd=$00030602;HWnd=$000205E4;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=12;WindowText="Registration" ParentHWnd=$00030602;HWnd=$000205FC;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205FA;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=6;WindowText="&Close" > Child is Close button: clicking. < ParentHWnd=$00000000;HWnd=$00030602;IsVisible=-1;IsOwned=0;IsAppWindow=-1;WindowTextLength=33;WindowText="ABBYY FineReader for ScanSnap 5.0" ParentHWnd=$00030602;HWnd=$000205F6;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=34;WindowText="Processing finished (warnings: 1)." ParentHWnd=$00030602;HWnd=$000205F4;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=31;WindowText="Converting to searchable PDF..." ParentHWnd=$00030602;HWnd=$000205F0;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205EE;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00030602;HWnd=$000205D2;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=63;WindowText="Page 1. Make sure the correct recognition language is selected." ParentHWnd=$00000000;HWnd=$00010248;IsVisible=-1;IsOwned=-1;IsAppWindow=0;WindowTextLength=14;WindowText="Creative Cloud" > Recursive child windows for Creative Cloud ParentHWnd=$00010248;HWnd=$0001024A;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=28;WindowText="Main Container Client Dialog" ParentHWnd=$00010248;HWnd=$0002034A;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=3;WindowText="IMS" ParentHWnd=$00010248;HWnd=$0001035A;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=0;WindowText="" ParentHWnd=$00010248;HWnd=$00020350;IsVisible=-1;IsOwned=0;IsAppWindow=0;WindowTextLength=18;WindowText="Sign in - Adobe ID" > Child is Signin button: closing parent. < ParentHWnd=$0003011A;HWnd=$00010248;IsVisible=-1;IsOwned=-1;IsAppWindow=0;WindowTextLength=14;WindowText="Creative Cloud" < ParentHWnd=$00000000;HWnd=$0003011A;IsVisible=0;IsOwned=0;IsAppWindow=0;WindowTextLength=4;WindowText="Core"
It appears that ABBYY has a different set of booleans than Creative Cloud.
This is kind of odd, as delphi – How to get captions of actual windows currently running? – Stack Overflow points to Window Features – Windows applications | Microsoft Docs: Owned Windows stating:
The Shell creates a button on the taskbar whenever an application creates a window that isn’t owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.
Apparently, ABBYY fully plays by the rules, but Creatheive Cloud cheats a bit: none of the Windows are WS_EX_APPWINDOW, but the hidden unowned “Core” owner of the “Creative Cloud” still makes it appear on the taskbar.
–jeroen