Just in case anyone is still interested in this, I have found a decent workaround.
We used the Window classname in Delphi to find windows in other apps, and to enumerate windows in the current app from a thread, when it isn't safe to use Screen.Forms or Application.Forms.
It is quite inconvenient that Lazarus uses the same classname of 'Window' for all forms, but it is possible to intercept the window handle creation and add a custom property to the window handle as a global atom:
// add these overrides into your base form ancestor, for use in all your forms
procedure TBaseForm.CreateWnd; override;
begin
inherited CreateWnd;
Windows.SetProp(Handle, 'My_Window_Prop_ClassName', GlobalAddAtom(pchar(string(ClassName))));
end;
and then destroy the atom (atoms have built-in reference counting) and remove the property when the window handle is destroyed
procedure TBaseForm.DestroyWnd; override;
var
atom: TAtom;
begin
atom := Windows.RemoveProp(Handle, 'My_Window_Prop_ClassName');
if (atom<>0) then GlobalDeleteAtom(atom);
inherited DestroyWnd;
end;
Then you can enumerate all windows to find the one you are looking for with something like this:
type
TEnumInfo = record
ClassName: string;
Found: boolean;
end;
var
info: TEnumInfo;
info.ClassName := 'class name to look for';
info.Found := false;
EnumThreadWindows(MainThreadId, @EnumThreadCallback, LPARAM(@info));
where EnumThreadCallback is defined as:
function EnumThreadCallback(hwnd: THandle; var info: TEnumInfo): bool; stdcall;
var
atom: TAtom;
cls: array[0..MAX_PATH] of char;
begin
atom := Windows.GetProp('My_Window_Prop_ClassName');
if (atom<>0) then begin
if GlobalGetAtomName(atom, cls, MAX_PATH)>0 then begin
if (StrComp(cls, pchar(info.ClassName)=0 then begin
info.Found := true;
break;
end;
end;
end;
Result := not info.Found; // return TRUE to continue enum, FALSE to stop