The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2,352 other followers

Automatically closing ABBY Finereader 5.0 windows after scanning is completed

Posted by jpluimers on 2021/02/23

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:

these types:

and these constants:

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:

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

program AbbyyFineReader_CreativeCloud_AutoCloseConsoleProject;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Winapi.Messages,
Winapi.Windows,
System.SysUtils;
const
CreativeCloud = 'Creative Cloud';
AbbyyFineReaderForScanSnap50 = 'ABBYY FineReader for ScanSnap 5.0';
type
TWindowDump = record
public
HWnd: HWnd;
IsAppWindow: Boolean;
IsOwned: Boolean;
IsVisible: Boolean;
WindowText: string;
WindowTextLength: Integer;
ParentHWnd: lParam;
Prefix: string;
constructor Create(const APrefix: string; const AHWnd: HWnd; const AParentHWnd: lParam);
function ToString: string;
end;
constructor TWindowDump.Create(const APrefix: string; const AHWnd: HWnd; const AParentHWnd: lParam);
begin
Prefix := APrefix;
HWnd := AHWnd;
ParentHWnd := AParentHWnd;
IsAppWindow := GetWindowLongPtr(HWnd, GWL_STYLE) and WS_EX_APPWINDOW <> 0;
IsOwned := GetWindow(HWnd, GW_OWNER) <> 0;
IsVisible := IsWindowVisible(HWnd);
WindowTextLength := GetWindowTextLength(HWnd);
if WindowTextLength <> 0 then
begin // without Length check you can get an access violation
SetLength(WindowText, WindowTextLength);
GetWindowText(HWnd, PChar(WindowText), Length(WindowText) + 1);
end;
end;
function TWindowDump.ToString: string;
begin
Result := Format('%sParentHWnd=$%8.8x;HWnd=$%8.8x;IsVisible=%s;IsOwned=%s;IsAppWindow=%s;WindowTextLength=%d;WindowText="%s"', [ //
Prefix, ParentHWnd, HWnd, BoolToStr(IsVisible), BoolToStr(IsOwned), BoolToStr(IsAppWindow), WindowTextLength, WindowText]);
end;
function DumpWindow(const Prefix: string; const HWnd: HWnd; const ParentHWnd: lParam): TWindowDump;
begin
Result := TWindowDump.Create(Prefix, HWnd, ParentHWnd);
Writeln(Result.ToString());
end;
procedure DumpWindowStack(const Prefix: string; const HWnd: HWnd; const ParentHWnd: lParam);
var
OwnerHWnd: lParam;
begin
if ParentHWnd <> 0 then
begin
OwnerHWnd := GetWindow(ParentHWnd, GW_OWNER);
DumpWindow(Prefix, ParentHWnd, OwnerHWnd);
DumpWindowStack(' ' + Prefix, ParentHWnd, OwnerHWnd);
end;
end;
function EnumChildWindowsProc(HWnd: HWnd; ParentHWnd: lParam): Bool; stdcall;
var
IsVisible: Boolean;
ParentWindowText: string;
ParentWindowTextLength: Integer;
WindowDump: TWindowDump;
begin
Result := True; // carry on enumerating
IsVisible := IsWindowVisible(HWnd);
if IsVisible then
begin
WindowDump := DumpWindow(' ', HWnd, ParentHWnd);
ParentWindowTextLength := GetWindowTextLength(ParentHWnd);
if ParentWindowTextLength <> 0 then
begin
SetLength(ParentWindowText, ParentWindowTextLength);
GetWindowText(ParentHWnd, PChar(ParentWindowText), Length(ParentWindowText) + 1);
end;
if ParentWindowText = AbbyyFineReaderForScanSnap50 then
begin
if WindowDump.WindowText = '&Close' then
begin
Writeln(' > Child is Close button: clicking.');
DumpWindowStack(' < ', HWnd, ParentHWnd);
SendMessage(HWnd, BM_CLICK, 0, 0);
end;
end;
if ParentWindowText = CreativeCloud then
begin
if WindowDump.WindowText = 'Sign in – Adobe ID' then
begin
Writeln(' > Child is Signin button: closing parent.');
DumpWindowStack(' < ', HWnd, ParentHWnd);
SendMessage(ParentHWnd, WM_CLOSE, 0, 0);
end;
end;
end;
end;
function EnumWindowsProc(HWnd: HWnd; lParam: lParam): Bool; stdcall;
var
WindowDump: TWindowDump;
begin
Result := True; // carry on enumerating
WindowDump := TWindowDump.Create('', HWnd, lParam);
if WindowDump.IsVisible then
begin
Writeln(WindowDump.ToString());
(*
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"
*)
if WindowDump.IsVisible and WindowDump.IsAppWindow then
begin
if WindowDump.WindowText = AbbyyFineReaderForScanSnap50 then
begin
Writeln('> Recursive child windows for ABBYY');
EnumChildWindows(HWnd, @EnumChildWindowsProc, HWnd);
end;
end;
if WindowDump.IsVisible and WindowDump.IsOwned then
if WindowDump.WindowText = CreativeCloud then
begin
Writeln('> Recursive child windows for Creative Cloud');
EnumChildWindows(HWnd, @EnumChildWindowsProc, HWnd);
end;
end;
end;
begin
try
repeat
EnumWindows(@EnumWindowsProc, 0);
Writeln('.');
Sleep(10 * 1000);
until False;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: