Lazarus

Programming => General => Topic started by: pcurtis on May 18, 2021, 04:23:55 pm

Title: [SOLVED] Get unique constant identifier
Post by: pcurtis on May 18, 2021, 04:23:55 pm
I can get the handle of the foreground app with

Code: Pascal  [Select][+][-]
  1.   AHandle: HWND;
  2.  
  3.   AHandle := GetForegroundWindow;
  4.  

How can I get a unique constant identifier (maybe exe name, or anything else but must be unique) for this handle?
Title: Re: Get unique constant identifier
Post by: Thaddy on May 18, 2021, 04:30:19 pm
https://www.freepascal.org/docs-html/rtl/sysutils/createguid.html

Note that handles are per session, so not permanent.
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 04:38:40 pm
The identifier must be constant across restarts, hence application EXE name.
Title: Re: Get unique constant identifier
Post by: Handoko on May 18, 2021, 04:41:41 pm
Maybe by pressing Ctr+Shift+G in the Lazarus' Source Code Editor.
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 04:48:58 pm
What does that do. On my system it opens the AMD Radeon control panel  %)
Title: Re: Get unique constant identifier
Post by: Handoko on May 18, 2021, 04:53:35 pm
It will generate something like this:
['{97AEE240-25CB-4A98-AC64-626413E51E6A}']

They call it UUID/GUID:
https://en.wikipedia.org/wiki/Universally_unique_identifier (https://en.wikipedia.org/wiki/Universally_unique_identifier)

It promises to be unique (by who?). Everytime you press the shortcut, you will get different result.

What wrong with your computer?

The ctrl-shift-g (insert guid) shortcut is documented here:
https://wiki.lazarus.freepascal.org/Lazarus_IDE_Shortcuts#macOS.2C_Lazarus_style (https://wiki.lazarus.freepascal.org/Lazarus_IDE_Shortcuts#macOS.2C_Lazarus_style)
Title: Re: Get unique constant identifier
Post by: lucamar on May 18, 2021, 05:02:15 pm
What does that do. On my system it opens the AMD Radeon control panel  %)

It inserts a GUID in the source such as used for interface definitions, e.g..:
Code: Pascal  [Select][+][-]
  1. ['{E93B22EC-6270-4D83-9021-2045DDFCFC08}']
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 05:19:23 pm
That will not help. I have no chance to modify the 'target app'. Really I need to get the EXE name from a handle.
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 05:25:27 pm
That will not help. I have no chance to modify the 'target app'. Really I need to get the EXE name from a handle.
I know a Windows only solution.  Would that suffice ?
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 05:27:29 pm
Yes, I'm a windoze man.
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 05:35:18 pm
Yes, I'm a windoze man.
It's really simple then.  Here is a code snippet that should get you going
Code: Pascal  [Select][+][-]
  1.     WindowThreadId := GetWindowThreadProcessId(Wnd, @WindowProcessId);
  2.  
  3.     ProcessHandle := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
  4.                                  FALSE,
  5.                                  WindowProcessId);
  6.  
  7.     if ProcessHandle <> 0 then
  8.     begin
  9.       ProcessExecutableSize := sizeof(WindowProcessExecutable);  // a buffer you declare anywhere you want.
  10.  
  11.       QueryFullProcessImageName(ProcessHandle,
  12.                                 0,
  13.                                 WindowProcessExecutable,
  14.                                 ProcessExecutableSize);
  15.  
with the window handle, get the process id, with the process id open the process to get a process handle, with the process handle get the executable name using QueryFullProcessImageName.  Go to the balcony, wave the executable's name and let the fat ladies know it's time for them to sing.

HTH.
Title: Re: Get unique constant identifier
Post by: Handoko on May 18, 2021, 05:42:41 pm
Interesting. I bookmarked it, maybe I can use it someday.

Thank you for sharing it.
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 05:45:57 pm
Thanks. Do you have a little more complete version? My WinAPI knowledge sucks.
Title: Re: Get unique constant identifier
Post by: Remy Lebeau on May 18, 2021, 06:00:53 pm
Code: Pascal  [Select][+][-]
  1. ProcessExecutableSize := sizeof(WindowProcessExecutable);  // a buffer you declare anywhere you want.

You should use Length() instead of Sizeof(). QueryFullProcessImageName() wants a character count, not a byte count.  If you compile with a Unicode buffer, Sizeof() will report twice the size, thus risking a buffer overflow by telling QueryFullProcessImageName() there is room for more characters than there really is.
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 06:09:40 pm
Code: Pascal  [Select][+][-]
  1. ProcessExecutableSize := sizeof(WindowProcessExecutable);  // a buffer you declare anywhere you want.
You should use Length() instead of Sizeof(). QueryFullProcessImageName() wants a character count, not a byte count.  If you compile with a Unicode buffer, Sizeof() will report twice the size, thus risking a buffer overflow by telling QueryFullProcessImageName() there is room for more characters than there really is.
You're right.  I'm used to using sizeof because I do everything in ansi  (extended) ascii(single byte characters), with ansi ascii, that works but, it wouldn't work with non-sbcs.  When I use unicode I define the buffers a different way that spares me from using sizeof.



Interesting. I bookmarked it, maybe I can use it someday.

Thank you for sharing it.
You're welcome Handoko.



Thanks. Do you have a little more complete version? My WinAPI knowledge sucks.
You're welcome.  I attached a complete example to this post (including executable).  It might be a little more complete than you were asking for.  It's an example about how to use qsort that uses the steps I outlined in the previous post. 

Suggestion: make a directory where you'll expand the archive, there is about a half dozen files.

HTH.
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 06:18:27 pm
May be I'm stupid, but I can't find any code.
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 06:25:18 pm
May be I'm stupid, but I can't find any code.
you need to expand the archive.  The .7z file is a compressed archive, you need 7zip to decompress it.  7zip is a free download, I recommend it, very nice utility.

Once expanded, you'll find there is a .lpr file (and the .lps, lpi).  The .lpr is the entire program.
Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 06:50:58 pm
OK, got it. Compiles and runs.

It's way out of my competence  :o
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 07:03:44 pm
OK, got it. Compiles and runs.

It's way out of my competence  :o
Good on both counts.  That it's out of your competence is a good thing.  Use the debugger to step through the program.  This might help
Code: Pascal  [Select][+][-]
  1. function EnumWindowsProc(Wnd : HWND; WindowListInt : ptruint) : BOOL; stdcall;
  2.   { this function is called by the EnumWindows call. It fills the a window    }
  3.   { list with the window information we are interested in.                    }
  4.  
  5. var
  6.   WindowList            : PWindowList absolute WindowListInt;
  7.  
  8.   ProcessHandle         : THANDLE;
  9.  
  10.   TimeOfNoInterest      : TFILETIME;
  11.   ProcessExecutableSize : DWORD;
  12.  
  13. begin
  14.   EnumWindowsProc := TRUE;       { continue enumerating until no more windows }
  15.  
  16.   with WindowList^.WindowInfo[WindowList^.WindowCount] do
  17.   begin
  18.     WindowHandle  := Wnd;
  19.  
  20.     WindowThreadId := GetWindowThreadProcessId(Wnd, @WindowProcessId);
  21.     WindowVisible  := IsWindowVisible(Wnd);
  22.     GetWindowText(Wnd, WindowCaption, sizeof(WindowCaption));
  23.  
  24.     { get the process times for this process.  NOTE: the method used here is  }
  25.     { very inefficient since a handle for the same process is obtained for    }
  26.     { every window the process has created.                                   }
  27.  
  28.     ProcessHandle := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
  29.                                  FALSE,
  30.                                  WindowProcessId);
  31.  
  32.     if ProcessHandle <> 0 then
  33.     begin
  34.       { IMPORTANT: the size must be reset every time because QueryFullProcess }
  35.       {            ImageName sets it to the length of the returned string     }
  36.       {            every time it's called.                                    }
  37.  
  38.       ProcessExecutableSize := sizeof(WindowProcessExecutable);
  39.  
  40.       QueryFullProcessImageName(ProcessHandle,
  41.                                 0,
  42.                                 WindowProcessExecutable,
  43.                                 ProcessExecutableSize);
  44.  
  45.       GetProcessTimes(ProcessHandle,
  46.                       WindowProcessCreationTime,
  47.                       TimeOfNoInterest,
  48.                       TimeOfNoInterest,
  49.                       TimeOfNoInterest);
  50.  
  51.       CloseHandle(ProcessHandle);
  52.     end;
  53.   end;
  54.  
  55.   with WindowList^ do
  56.   begin
  57.     WindowInfoPointers[WindowCount] := @WindowInfo[WindowCount];
  58.     inc(WindowCount);
  59.  
  60.     if WindowCount > high(WindowInfo) then
  61.     begin
  62.       EnumWindowsProc := FALSE;   { no more room, stop enumerating            }
  63.     end;
  64.   end;
  65. end;
  66.  


and

Code: Pascal  [Select][+][-]
  1.     WM_COMMAND:
  2.     begin
  3.       case LOWORD(wParam) of
  4.         IDM_UPDATE_WINDOW_LIST:
  5.         begin
  6.           ZeroMemory(WindowList, sizeof(WindowList^));
  7.  
  8.           { fill it with the window handles and their caption text            }
  9.  
  10.           EnumWindows(@EnumWindowsProc,
  11.                       ptruint(WindowList));           { pass the WindowList   }
  12.  
  13.           { if the "sort by process" menu item is checked then the new window }
  14.           { list must be sorted by process before the listbox is updated.     }
  15.  
  16.           MenuItemState := GetMenuState(GetMenu(Wnd),
  17.                                         IDM_PROCESS_SORT,
  18.                                         MF_BYCOMMAND);
  19.  
  20.           if MenuItemState and MF_CHECKED <> 0 then
  21.           begin
  22.             { sort the window list                                            }
  23.  
  24.             with WindowList^ do
  25.             begin
  26.               qsort(@WindowInfoPointers,
  27.                      WindowCount,
  28.                      sizeof(WindowInfoPointers[low(WindowInfoPointers)]),
  29.                      TCompareFunction(@CompareWindowInfoProcessId));
  30.             end;
  31.  
  32.             PostMessage(Wnd, WM_COMMAND, IDM_UPDATE_LISTBOX, 0);
  33.             exit;
  34.           end;
  35.  
  36.           { if the "sort by process creation time" is checked then the new    }
  37.           { window list must be sorted accordingly.                           }
  38.  
  39.           MenuItemState := GetMenuState(GetMenu(Wnd),
  40.                                         IDM_PROCESS_CREATION_SORT,
  41.                                         MF_BYCOMMAND);
  42.  
  43.           if MenuItemState and MF_CHECKED <> 0 then
  44.           begin
  45.             { sort the window list                                            }
  46.  
  47.             with WindowList^ do
  48.             begin
  49.               qsort(@WindowInfoPointers,
  50.                      WindowCount,
  51.                      sizeof(WindowInfoPointers[low(WindowInfoPointers)]),
  52.                      TCompareFunction(@CompareWindowInfoProcessTimes));
  53.             end;
  54.  
  55.             PostMessage(Wnd, WM_COMMAND, IDM_UPDATE_LISTBOX, 0);
  56.             exit;
  57.           end;
  58.  
  59.           { unnecessary but safe                                              }
  60.  
  61.           PostMessage(Wnd, WM_COMMAND, IDM_UPDATE_LISTBOX, 0);
  62.  
  63.           exit;
  64.         end;
  65.  

Put a breakpoint at each of the highlighted lines, debug the program and step through the code when the breakpoints are hit.  Hover over the variables and you'll see how it all works.

You'll figure it out.


Title: Re: Get unique constant identifier
Post by: pcurtis on May 18, 2021, 07:12:26 pm
I think I've got it  :)

Code: Pascal  [Select][+][-]
  1. function GetCurrentActiveProcessPath: String;
  2. var
  3.   ProcessHandle : DWORD;
  4.   ProcessExecutableSize     : DWORD;
  5.   WindowHandle              : HWND;
  6.   WindowProcessId           : DWORD;
  7.   WindowThreadId            : DWORD;
  8.   WindowProcessCreationTime : TFILETIME;                     { inefficient  }
  9.   WindowProcessExecutable   : packed array[0..511] of char;  { inefficient  }
  10.   WindowVisible             : BOOL;
  11.   WindowCaption             : packed array[0..127] of char;
  12. begin
  13.   WindowHandle := GetForegroundWindow;
  14.  
  15.   WindowThreadId := GetWindowThreadProcessId(WindowHandle, @WindowProcessId);
  16.  
  17.   ProcessHandle := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
  18.                                  FALSE,
  19.                                  WindowProcessId);
  20.  
  21.   if ProcessHandle <> 0 then
  22.     begin
  23.       ProcessExecutableSize := Length(WindowProcessExecutable);  // a buffer you declare anywhere you want.
  24.  
  25.       QueryFullProcessImageName(ProcessHandle,
  26.                                0,
  27.                                WindowProcessExecutable,
  28.                                @ProcessExecutableSize);
  29.  
  30.     end;
  31.   Result := WindowProcessExecutable;
  32. end;
  33.  
Title: Re: Get unique constant identifier
Post by: 440bx on May 18, 2021, 07:24:04 pm
That looks like it should work.
Title: Re: [SOLVED] Get unique constant identifier
Post by: pcurtis on May 19, 2021, 10:25:02 am
Indeed it does. Thanks.
TinyPortal © 2005-2018