Recent

Author Topic: Rewrite a function  (Read 4722 times)

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Rewrite a function
« 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.  
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Rewrite a function
« Reply #1 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);
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Zvoni

  • Hero Member
  • *****
  • Posts: 2316
Re: Rewrite a function
« Reply #2 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
« Last Edit: May 19, 2021, 12:38:36 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Rewrite a function
« Reply #3 on: May 19, 2021, 12:41:11 pm »
May be it's better to use FindWindow and search by window class?
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Rewrite a function
« Reply #4 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.
« Last Edit: May 19, 2021, 01:22:36 pm by pcurtis »
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Rewrite a function
« Reply #5 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).

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Rewrite a function
« Reply #6 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

Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

dseligo

  • Hero Member
  • *****
  • Posts: 1194
Re: Rewrite a function
« Reply #7 on: May 19, 2021, 01:37:57 pm »
Use PHANDLE instead of PHWND.

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: Rewrite a function
« Reply #8 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.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Rewrite a function
« Reply #9 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?
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: Rewrite a function
« Reply #10 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.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Zvoni

  • Hero Member
  • *****
  • Posts: 2316
Re: Rewrite a function
« Reply #11 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
« Last Edit: May 19, 2021, 04:04:46 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Rewrite a function
« Reply #12 on: May 19, 2021, 04:36:03 pm »
@Zvoni - Do you have a small sample?
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Zvoni

  • Hero Member
  • *****
  • Posts: 2316
Re: Rewrite a function
« Reply #13 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
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Rewrite a function
« Reply #14 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;

 

TinyPortal © 2005-2018