Lazarus

Programming => General => Topic started by: pcurtis on May 19, 2021, 10:22:29 am

Title: Rewrite a function
Post by: pcurtis on May 19, 2021, 10:22:29 am
Is there a better way to do this?

Code: Pascal  [Select][+][-]
  1. var
  2.   AHandle: HWND;
  3.  
  4. function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): bool stdcall;
  5. var
  6.   Title: array[0..255] of Char;
  7. begin
  8.   GetWindowText(hWnd, Title, 255);
  9.   if (IsWindowVisible(hWnd)) and (pos('JPEGView', Title) <> 0) then
  10.       AHandle := hWnd;
  11.   Result := True;
  12. end;
  13.  
  14. function GetJPGViewHandle : hWnd;
  15. begin
  16.   AHandle := 0;
  17.   EnumWindows(@EnumWindowsProc, 0);
  18.   Result := AHandle;
  19. end;
  20.  
  21. procedure TForm1.FormCreate(Sender: TObject);
  22. begin
  23.   Caption := GetJPGViewHandle.ToString;
  24. end;
  25.  
Title: Re: Rewrite a function
Post by: pcurtis on May 19, 2021, 12:22:09 pm
I thought like this :

Code: Pascal  [Select][+][-]
  1. function GetJPGViewHandle(AText : String) : hWnd;
  2. var
  3.   AHandle : HWND;
  4.   function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): bool stdcall;
  5.     var
  6.       Title: array[0..255] of Char;
  7.     begin
  8.       GetWindowText(hWnd, Title, 255);
  9.       if (IsWindowVisible(hWnd)) and (pos(AText, Title) <> 0) then
  10.         AHandle := hWnd;
  11.       Result := True;
  12.     end;
  13. begin
  14.   AHandle := 0;
  15.   EnumWindows(@EnumWindowsProc, 0);
  16.   Result := AHandle;
  17. end;
  18.  
  19. procedure TForm1.FormCreate(Sender: TObject);
  20. var
  21.   AHandle : hWnd;
  22. begin
  23.   AHandle := GetJPGViewHandle('JPEGView');
  24. end;
  25.  

but I can't reference the callback function

 EnumWindows(@EnumWindowsProc, 0);
Title: Re: Rewrite a function
Post by: Zvoni on May 19, 2021, 12:31:19 pm
In your first Code, are you missing a Begin/end around Line 9/10?
Because currently, you set the Result always to True, which would continue the Enumeration

EDIT: and is there a semicolon missing in your Function-header? Between "bool" and "stdcall"

EDIT2: Found this:
https://forum.lazarus.freepascal.org/index.php?topic=13007.0
Looks different to yours
Title: Re: Rewrite a function
Post by: Mr.Madguy on May 19, 2021, 12:41:11 pm
May be it's better to use FindWindow and search by window class?
Title: Re: Rewrite a function
Post by: pcurtis on May 19, 2021, 01:09:34 pm
@Mr.Madguy - can't use FindWindow because the class is not unique and the caption varies.

@Zvoni semicolon yes, but compiles and works without it %). begin end are not needed. The code works, but I want to simplify it.
Title: Re: Rewrite a function
Post by: PascalDragon on May 19, 2021, 01:23:16 pm
@Zvoni semicolon yes, begin end are not needed. The code works, but I want to simplify it.

The lParam parameter of EnumWindows is a) a type of pointer size and b) passed through to your callback. Thus you can do it like this:

Code: Pascal  [Select][+][-]
  1. function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): bool stdcall;
  2. var
  3.   Title: array[0..255] of Char;
  4.   hdl: PHWND absolute lParam;
  5. begin
  6.   GetWindowText(hWnd, Title, 255);
  7.   if (IsWindowVisible(hWnd)) and (pos('JPEGView', Title) <> 0) then
  8.       hdl^ := hWnd;
  9.   Result := True;
  10. end;
  11.  
  12. function GetJPGViewHandle : hWnd;
  13. begin
  14.   EnumWindows(@EnumWindowsProc, LPARAM(@Result));
  15. end;

This way you don't need to use a global variable (which would be bad if multi threading is involved and no synchronization is used).
Title: Re: Rewrite a function
Post by: pcurtis on May 19, 2021, 01:35:48 pm
Doesn't compile. I get

Quote
Compile Project, Target: project1.exe: Exit code 1, Errors: 3, Hints: 1
unit1.pas(33,8) Error: Identifier not found "PHWND"
unit1.pas(33,14) Error: Error in type definition
unit1.pas(37,12) Error: Illegal qualifier
unit1.pas(43,33) Hint: Conversion between ordinals and pointers is not portable

Title: Re: Rewrite a function
Post by: dseligo on May 19, 2021, 01:37:57 pm
Use PHANDLE instead of PHWND.
Title: Re: Rewrite a function
Post by: 440bx on May 19, 2021, 01:41:14 pm
@Mr.Madguy - can't use FindWindow because the class is not unique and the caption varies.

@Zvoni semicolon yes, but compiles and works without it %). begin end are not needed. The code works, but I want to simplify it.
I have a question for you... do you know beforehand the name of the executable that has the string "JPEGView" in its caption ?

if you know the executable's name then it would likely be simpler to find out if the executable is running and then do whatever it is you want to do with it.

Just a thought, because looking for strings in window captions is not a reliable way to getting to an executable of interest.
Title: Re: Rewrite a function
Post by: pcurtis on May 19, 2021, 01:54:20 pm
OK, how do I find if an executable is running, and get it's top level window handle?
Title: Re: Rewrite a function
Post by: 440bx on May 19, 2021, 03:45:28 pm
OK, how do I find if an executable is running, and get it's top level window handle?
There are a number of APIs that will list which executables are running.  The Toolhelp32 functions Process32First/Next among others would do that.  You get the process id from them, then use the same method used in EnumWindows, that is, with the process id open the process, get the full image name (Process32First returns an abbreviated image name - no path information - which is not enough to uniquely identify an executable.)

That step gives you the process id and the full image name.

Once you have that, you can use EnumWindows to enumerate the top level windows.  Once you hit the window that has the same process id then you've got the process and window you are interested in.

For good measure, it must be noted that it is possible for the process you found in the first step to end, a new process be created by the user or Windows that just happens to use the same process id as the process that ended.  This can happen because Windows re-uses process ids but, because of how Windows goes about re-using process ids, it is extremely unlikely to happen unless you purposely create a contrived case that would cause that situation.  I mention it only for completeness.  If I were you, I wouldn't worry about it.

HTH.
Title: Re: Rewrite a function
Post by: Zvoni on May 19, 2021, 03:48:21 pm
can you use WMI?

If yes, you can query "winmgmts"
select * from win32_process where name='myexename.exe'

If ReturnCount>0 Then ExeIsRunning:=True;

EDIT: Regarding WMI: Jurrasic Pork released something:
https://forum.lazarus.freepascal.org/index.php?topic=24490.0
Title: Re: Rewrite a function
Post by: pcurtis on May 19, 2021, 04:36:03 pm
@Zvoni - Do you have a small sample?
Title: Re: Rewrite a function
Post by: Zvoni on May 19, 2021, 05:08:24 pm
Look at the link
Otherwise, tomorrow, i think i can find the link to the WMI i used in the past
Title: Re: Rewrite a function
Post by: ASerge on May 20, 2021, 03:39:30 am
Is there a better way to do this?
My version :). What's important:
1. In general, GetWindowText does not work across application boundaries. Although Windows makes an exception for top level windows (for compatibility with Win 3.0), but it is always better to do it correctly.
2. Using SendMessageTimeout you can exclude hung applications.
3. The search stops as soon as the window is found.
4. When comparing, you can skip the conversion of PChar to string (implicitly, via the Pos function), and use ready-made functions.

Code: Pascal  [Select][+][-]
  1. uses Windows;
  2.  
  3. function ContinueSearchNextWindow(AWnd: HWND; ALParam: LPARAM): BOOL; stdcall;
  4. const
  5.   CWinTitleContains = 'JPEGView';
  6.   SMTO_ERRORONEXIT = $20; // Not defined in windows.pas
  7. var
  8.   Buffer: array[Byte] of Char;
  9.   Len: LRESULT;
  10. begin
  11.   if SendMessageTimeoutA(AWnd, WM_GETTEXT, SizeOf(Buffer), LPARAM(@Buffer),
  12.     SMTO_ABORTIFHUNG or SMTO_ERRORONEXIT, MSecsPerSec, @Len) <> 0 then
  13.   begin
  14.     if Len >= Length(CWinTitleContains) then
  15.     begin
  16.       if AnsiStrPos(Buffer, CWinTitleContains) <> nil then
  17.       begin
  18.         HWND(Pointer(ALParam)^) := AWnd;
  19.         Exit(False);
  20.       end;
  21.     end;
  22.   end;
  23.   Result := True;
  24. end;
  25.  
  26. function GetJPGViewHandle: HWND;
  27. begin
  28.   Result := 0;
  29.   EnumWindows(@ContinueSearchNextWindow, LPARAM(@Result));
  30. end;
Title: Re: Rewrite a function
Post by: PascalDragon on May 20, 2021, 09:03:36 am
Doesn't compile. I get

Quote
Compile Project, Target: project1.exe: Exit code 1, Errors: 3, Hints: 1
unit1.pas(33,8) Error: Identifier not found "PHWND"
unit1.pas(33,14) Error: Error in type definition
unit1.pas(37,12) Error: Illegal qualifier
unit1.pas(43,33) Hint: Conversion between ordinals and pointers is not portable


Yes, sorry, as dseligo said, use PHandle instead.
TinyPortal © 2005-2018