Window control (min/max/hide) on start from initiator

Propose new features and changes
User avatar
Synetech
Posts: 16
Joined: 28 Nov 2015, 16:30

Window control (min/max/hide) on start from initiator

Post by Synetech » 07 Oct 2022, 12:56

I'm not sure if this is a bug-report or a feature-request, I suppose it depends on how AHK works and its intentions.

Anyway, normally, when you run a program, it's possible to control its window from the executing program. In the command-line, you can use start /min notepad to run Notepad minimized, or in AHK, you can use Run,notepad,,hide to run it hidden. You can also set the Run field in a Windows shortcut.

This does not seem to work for AHK GUI programs. This simple script creates a GUI with an edit box. It defaults to making the window just big enough for the edit-box:

Code: Select all

gui,add,edit,w100 h100
gui,+resize
gui,show
No matter what I try, running the script will always show the dialog the default size, none of the commands seem to be able to start AHK GUI scripts minimized, maximized, or hidden.

I checked the docs to see if there's some sort of A_ variable to deal with this, but there doesn't seem to be one.

Yes, there are workarounds, but they shouldn't be necessary. Most programs behave correctly without having to jump through hoops.

The easiest way to fix this would be to just expose nShowCmd that the AHK executable receives in its WinMain() from the program that ran it, for example, as the A_ variable A_ShowCmd, then the script can adjust the GUI as necessary.


[Mod action: Moved topic from "Bug Reports"]

User avatar
boiler
Posts: 16918
Joined: 21 Dec 2014, 02:44

Re: Window control (min/max/hide) on start from initiator

Post by boiler » 07 Oct 2022, 16:15

Synetech wrote: I'm not sure if this is a bug-report or a feature-request
It is neither. The features already exist and without issue. Moving this thread to the "Ask For Help" section.

Synetech wrote: none of the commands seem to be able to start AHK GUI scripts minimized, maximized, or hidden.
See the Gui, Show options Minimize, Maximize, and Hide.

Synetech wrote: I checked the docs to see if there's some sort of A_ variable to deal with this, but there doesn't seem to be one.
The the built-in variables A_... generally do not do something by setting their values. They reflect the state of something or provide other information by checking what they contain.
Built-in Variables documentation wrote:With the exception of Clipboard, ErrorLevel, and command line parameters, these variables are read-only; that is, their contents cannot be directly altered by the script.

User avatar
Synetech
Posts: 16
Joined: 28 Nov 2015, 16:30

Re: Window control (min/max/hide) on start from initiator

Post by Synetech » 12 Oct 2022, 19:22

See the Gui, Show options Minimize, Maximize, and Hide.
You seem to have misunderstood the question. I'm not asking how the script can manually control min/max/hide on its own, I'm asking how you can let a user control them on LAUNCH (and I gave several examples to make it clear :eh:).
The the built-in variables A_... generally do not do something by setting their values. They reflect the state of something or provide other information by checking what they contain.
As you can see in the notes of the docs for the Run command, it says some programs like Calculator ignore the specified window state. ALL AHK GUI programs fall into this category because there doesn't seem to be a way to expose the nShowCmd variable that's passed to the WinMain() function when the AHK executable runs, it doesn't pass it to the script. An A_ShowCmd variable would be EXACTLY what you describe, it would be set on run to contain the value of nShowCmd, so I don't know what you are trying to criticize, it wouldn't "do" anything, it would be set by the executable so that the script can see it and adjust the GUI size accordingly. Without it, the script has no way to know what the user tried to set the window size to.

(Again, yes, there are workarounds, but those are not the same as just doing it the way almost every other Windows program already does by default and would unnecessarily complicate the script. For example using config-files or command-line switches would be make the script more complex and error-prone when it could simply just use the command you referenced on startup, which currently, it can't do because again, the script has no idea what, if anything TO set it to.)

If you know of a way to make this work with the current version of AHK, sample code would have been more useful than a link to the docs because I tried several search-terms and none of them found anything of relevance. :problem:

Here are some screenshots to make it clear-er:

Controlling window state with Run command:
Image

Controlling window state with Start command:
Image

Controlling window state with Windows shortcut:
Image

User avatar
boiler
Posts: 16918
Joined: 21 Dec 2014, 02:44

Re: Window control (min/max/hide) on start from initiator

Post by boiler » 12 Oct 2022, 19:28

It's definitely not a bug. Nothing is failing to meet what has been documented. I'll move this to the Wish List area.

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Window control (min/max/hide) on start from initiator

Post by lexikos » 13 Oct 2022, 22:51

There appear to be at least two solutions available at present:
  • Use GetStartupInfo to retrieve the wShowWindow value specified by the parent process.
  • Use ShowWindow with the SW_DEFAULT flag to show (or not) the GUI.
For GUI processes, the first time ShowWindow is called, its nCmdShow parameter is ignored [and] wShowWindow specifies the default value. In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is set to SW_SHOWDEFAULT.
Source: STARTUPINFOA (processthreadsapi.h) - Win32 apps | Microsoft Learn
AutoHotkey is designed to prevent the "first time" case, for reasons described in the source code:
// Some of the MSDN docs mention that an app's very first call to ShowWindow() makes that
// function operate in a special mode. Therefore, it seems best to get that first call out
// of the way to avoid the possibility that the first-call behavior will cause problems with
// our normal use of ShowWindow() below and other places. Also, decided to ignore nCmdShow,
// to avoid any momentary visual effects on startup.
// Update: It's done a second time because the main window might now be visible if the process
// that launched ours specified that. It seems best to override the requested state because
// some calling processes might specify "maximize" or "shownormal" as generic launch method.
// The script can display it's own main window with ListLines, etc.
// MSDN: "the nCmdShow value is ignored in the first call to ShowWindow if the program that
// launched the application specifies startup information in the structure. In this case,
// ShowWindow uses the information specified in the STARTUPINFO structure to show the window.
// On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT
// to use the startup information provided by the program that launched the application."
ShowWindow(g_hWnd, SW_HIDE);
ShowWindow(g_hWnd, SW_HIDE);

The first quote above clearly indicates that ShowWindow(hwnd, SW_DEFAULT) should use the value indicated in the STARTUPINFO, but in my testing it behaved the same as SW_SHOWNORMAL regardless of what GetStartupInfo reported. Fortunately it is sufficient to pass the value reported by GetStartupInfo to ShowWindow:

Code: Select all

gui,add,edit,w100 h100
gui,+resize +hwndhgui
gui,show, hide

VarSetCapacity(StartupInfo, StartupInfo_size := 9*A_PtrSize+32, 0)
NumPut(StartupInfo_size, StartupInfo, "uint")
DllCall("GetStartupInfo", "ptr", &StartupInfo)
wShowWindow := 1 ; Default to SW_SHOWNORMAL
if (NumGet(StartupInfo, A_PtrSize*4+28, "uint") & 1) ; STARTF_USESHOWWINDOW
    wShowWindow := NumGet(StartupInfo, A_PtrSize*4+32, "ushort")
DllCall("ShowWindow", "uptr", hgui, "int", wShowWindow)
Last edited by lexikos on 14 Oct 2022, 17:59, edited 1 time in total.
Reason: ptr -> uptr

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Window control (min/max/hide) on start from initiator

Post by malcev » 14 Oct 2022, 14:40

lexikos, You have error in Your code.
Hwnds should be send as "uptr".

Code: Select all

DllCall("ShowWindow", "uptr", hgui, "int", wShowWindow)

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Window control (min/max/hide) on start from initiator

Post by lexikos » 14 Oct 2022, 17:34

No, it is not an error. I almost never use "uptr", because it is rarely meaningful.
UPtr is also valid, but is only unsigned in 32-bit builds as AutoHotkey does not support unsigned 64-bit integers.
... when passing pure 64-bit integers to the function, signed vs. unsigned makes no difference due to the use of two's complement to represent signed integers.
The distinction of positive vs. negative or signed vs. unsigned is generally irrelevant for opaque handle values, although if you want to display a HWND, you probably want to do it as an unsigned hexadecimal integer.

The documentation also says
For parameters of type UInt64, large unsigned values may be passed as strings.
but I think that's not actually applicable to v2, because changes to general string to number conversions make this apply to all integer types. There is no difference between UInt64 and Int64.

That being said, having a HWND returned as "ptr" might cause it to be sign-extended on 32-bit builds, which won't be an issue for the Win32 API but might confuse the script. It is safe to use "uint" for HWND values, because they are 32-bit even on x64, for interoperability with 32-bit processes.

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Window control (min/max/hide) on start from initiator

Post by malcev » 14 Oct 2022, 17:44

If I run Your code on ahk_l 64bit, then after + 32000 iteration I get error

Code: Select all

setbatchlines -1
loop
{
   gui,add,edit,w100 h100
   gui,+resize +hwndhgui
   gui,show, hide

   VarSetCapacity(StartupInfo, StartupInfo_size := 9*A_PtrSize+32, 0)
   NumPut(StartupInfo_size, StartupInfo, "uint")
   DllCall("GetStartupInfo", "ptr", &StartupInfo)
   wShowWindow := 1 ; Default to SW_SHOWNORMAL
   if (NumGet(StartupInfo, A_PtrSize*4+28, "uint") & 1) ; STARTF_USESHOWWINDOW
      wShowWindow := NumGet(StartupInfo, A_PtrSize*4+32, "ushort")
   DllCall("ShowWindow", "ptr", hgui, "int", wShowWindow)
   if (A_lasterror = 1400)
      msgbox ERROR_INVALID_WINDOW_HANDLE
   tooltip % a_index
   gui, destroy
}
The same bug will be if we use uint

Code: Select all

setbatchlines -1
loop
{
   gui, +hwndhwnd
   if instr(hwnd, "0xffffffff")
   {
      msgbox % hwnd
      if (dllcall("IsWindow", "uint", hwnd) = 1)
         msgbox dllcall ok
      else
         msgbox dllcall failed
   }
   tooltip % a_index
   gui, destroy
}
And only uptr works ok.

Code: Select all

setbatchlines -1
loop
{
   gui, +hwndhwnd
   if instr(hwnd, "0xffffffff")
   {
      msgbox % hwnd
      if (dllcall("IsWindow", "uptr", hwnd) = 1)
         msgbox dllcall ok
      else
         msgbox dllcall failed
   }
   tooltip % a_index
   gui, destroy
}

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Window control (min/max/hide) on start from initiator

Post by lexikos » 14 Oct 2022, 17:58

You are right. I was forgetting that this is v1, and the HWND value is a string, not an integer. The string to number conversion is what breaks it.

For v2, input values for all of the integer types are handled by a single line of code, which clearly doesn't differentiate between signed and unsigned. String to integer conversion does not have the same issue as v1, either.

User avatar
Synetech
Posts: 16
Joined: 28 Nov 2015, 16:30

Re: Window control (min/max/hide) on start from initiator

Post by Synetech » 14 Oct 2022, 21:10

lexikos wrote:
13 Oct 2022, 22:51
Fortunately it is sufficient to pass the value reported by GetStartupInfo to ShowWindow:

Code: Select all

gui,add,edit,w100 h100
gui,+resize +hwndhgui
gui,show, hide

VarSetCapacity(StartupInfo, StartupInfo_size := 9*A_PtrSize+32, 0)
NumPut(StartupInfo_size, StartupInfo, "uint")
DllCall("GetStartupInfo", "ptr", &StartupInfo)
wShowWindow := 1 ; Default to SW_SHOWNORMAL
if (NumGet(StartupInfo, A_PtrSize*4+28, "uint") & 1) ; STARTF_USESHOWWINDOW
    wShowWindow := NumGet(StartupInfo, A_PtrSize*4+32, "ushort")
DllCall("ShowWindow", "uptr", hgui, "int", wShowWindow)
It's best when programs behave consistently; having AHK scripts do things differently than other programs only leads to confusion and frustration. (Old versions of the Windows Calculator ignoring it makes sense since they didn't maximize, though ignoring a request to run it minimized was still annoying. Newer versions that can maximize respect the user's choices.) It would be a good compromise to at least have the A_ variable to let the script handle it, but I guess the suggested code is an acceptable compromise (having to work around stuff and compromise seems to be part of most programs).

Whether or not this is handled in future versions, at least this is now available for anyone who tries to find a solution.

Thanks Lexikos.

Post Reply

Return to “Wish List”