Make MSGBOX child of a third-party application

Get help with using AutoHotkey and its commands and hotkeys
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Make MSGBOX child of a third-party application

31 Mar 2016, 14:42

Can I make a MSGBOX written in AutoHotkey a child of another third party application?

For example, let's say I'm using Microsoft Word and some condition occurs and I want to raise a MSGBOX as a result of that condition occurring, but I want that MSGBOX to be modal for Microsoft word - meaning the user can't do anything in MS Word until they click on one of the MSGBOX buttons or close it.

Is that possible? If so, could someone provide an example of that or point me to one?
lexikos
Posts: 7088
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Make MSGBOX child of a third-party application

31 Mar 2016, 17:46

Try MessageBox.

Code: Select all

SetTitleMatchMode 2
MessageBox(WinExist("Microsoft Word"), "Text", "Caption", 0)

MessageBox(OwnerHwnd, Text, Caption, Options) {
    DllCall("MessageBox", "ptr", OwnerHwnd, "str", Text, "str", Caption, "uint", Options)
}
(The window title may be incorrect.)
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: Make MSGBOX child of a third-party application

31 Mar 2016, 17:57

Code: Select all

hwnd := WinExist("ahk_exe WINWORD.exe") ; could use window title, but I used exe name instead
DllCall("MessageBox", Ptr, hwnd, Str, "this is a test", Str, "test", UInt, 0)
Seems to work here! (Although Lexikos' function is a nice wrapper to this DllCall)
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Re: Make MSGBOX child of a third-party application

31 Mar 2016, 20:17

That's pretty sweet guys! the solution raises another question for me. How would I do this with a custom Gui dialog instead of a MSGBOX? I was just using MSGBOX as an example of a Gui element so that I could get the gist of the process. Wasn't expecting the dllcall there.
lexikos
Posts: 7088
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Make MSGBOX child of a third-party application

31 Mar 2016, 21:36

MsgBox is totally distinct from GUI. It is implemented with the MessageBox function which we are calling via DllCall.

For a GUI, you set the owner the same way regardless of whether it is a script GUI or something else.
[v1.1.03+]: +Owner can be immediately followed by the name or number of an existing Gui or the HWND of any top-level window.
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Re: Make MSGBOX child of a third-party application

01 Apr 2016, 08:52

Thanks Lexikos! That helps a ton.
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Re: Make MSGBOX child of a third-party application

01 Apr 2016, 13:58

I tried both of these approaches but could not get them to work. I'm trying to figure out how to make messageboxes and GUIs modal in a third party application. That is, I want to be able to create these GUIs in AutoHotkey and raise them when a condition occurs in the third-party app and not allow the user to interact with the third-party app until they've closed my GUI or responded to my MSGBOX.

The third-party application I'm dealing with is ArcMap, from Esri.
I tried both approaches above for the MSGBOX, neither worked for me. The messagebox never appeared. Wondering what was going on, I assigned the return value from the dllcall to a variable and then displayed that return value in a MSGBOX after the dllcall executed and found that I would always get a return value of 0.

I then tried the gui approach and made a simple gui window with various controls. I assigned its owner as the active window because I'm launching the script from within the ArcMap application by calling it from a Python shell.

I used a simple cludge script to verify that I was getting the correct handle id and I am. In the case of the GUI, it shows up on top of Arcmap but its not modal. The user can click in ArcMap and then the AutoHotkey GUI loses focus and drops behind it. I don't know what I'm doing wrong and wonder if perhaps ArcMap is refusing to own the GUI.

Here's the custom GUI code.

Code: Select all

hwnd := WinExist("A")
GUI +Owner %hwnd%
Gui, Add, DropDownList, x12 y30 w450 h40 , DropDownList
Gui, Add, DropDownList, x12 y80 w450 h21 , DropDownList
Gui, Add, ListView, x12 y130 w450 h70 , ListView
Gui, Add, CheckBox, x22 y220 w80 h20 , CheckBox
Gui, Add, Radio, x382 y220 w80 h20 , Radio
Gui, Add, Radio, x292 y220 w80 h20 , Radio
Gui, Add, Button, x362 y290 w100 h30 , Button
Gui, Add, Button, x252 y290 w100 h30 , Button
Gui, Add, CheckBox, x22 y240 w80 h20 , CheckBox
Gui, Show, x446 y211 h341 w479, New GUI Window
Return

GuiClose:
ExitApp
And here's the cludge script I used to verify that I was getting the right hwnd id (credit: Leef_me @ https://autohotkey.com/board/topic/4742 ... t-checker/)

Code: Select all

;MouseGetPos, xpos, ypos 
;Msgbox, The cursor is at X%xpos% Y%ypos%. 

; This example allows you to move the mouse around to see
; the title of the window currently under the cursor:
#Persistent
SetTimer, WatchCursor, 100
return

WatchCursor:
MouseGetPos,x ,y , id, control
WinGetTitle, title, ahk_id %id%
WinGetClass, class, ahk_id %id%
ToolTip, x%x% y%y% `nahk_id %id%`nahk_class %class%`n%title%`nControl: %control%
if flag
{
  clipboard= x%x% y%y% ahk_id %id%`nahk_class %class%`n%title%`nControl: %control%
  flag=0
}
return

f1::
flag=1
return

esc:: ; in case of emergency hit ESCAPE to exit
exitapp
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Re: Make MSGBOX child of a third-party application

01 Apr 2016, 16:10

So I was finally able to get the window to be owned by the third party application, ArcMap using the following code:

Code: Select all

; Get the handle of the currently active window that launched the app 
parentHWND := WinExist("A")

; Declare as a Tool Window
GUI +ToolWindow
; Add Controls
Gui, Add, DropDownList, x12 y30 w450 h40 , DropDownList
Gui, Add, DropDownList, x12 y80 w450 h21 , DropDownList
Gui, Add, ListView, x12 y130 w450 h70 , ListView
Gui, Add, CheckBox, x22 y220 w80 h20 , CheckBox
Gui, Add, Radio, x382 y220 w80 h20 , Radio
Gui, Add, Radio, x292 y220 w80 h20 , Radio
Gui, Add, Button, x362 y290 w100 h30 , Button
Gui, Add, Button, x252 y290 w100 h30 , Button
Gui, Add, CheckBox, x22 y240 w80 h20 , CheckBox
; Show the Window - It's now the Active Window
Gui, Show, h341 w479, Awesome tool
; Get the handle of the currently active window - which is going to be the child
childHWND := WinExist("A")
;WinGet, selfHWND, ID, Awesome tool
;MSGBOX 4096, Self, %selfHWND%

; Set the parent application as parent of the child
succeeded := DllCall("SetParent", "uint", childHWND, "uint", parentHWND)
IF NOT succeeded
{
	MSGBOX 4096, Error, An error while attempting to own the window. Error Code: %A_LastError%
}
Return

GuiClose:
ExitApp
The only problem I'm facing is now is that it's not modal. It shows up on top of the third party application and remains there, but you can still interact with the third party application if you want to. How can I block input to the third party application? I think I want to to be Task Modal. How would I do that?
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: Make MSGBOX child of a third-party application

01 Apr 2016, 17:56

You could try disabling the parent window like so:

Code: Select all

DllCall("EnableWindow", "UInt", parentHWND, "Int", 0) ; disable parent window
It can be renabled (e.g. in GuiClose label) by

Code: Select all

DllCall("EnableWindow", "UInt", parentHWND, "Int", 1) ; enable parent window
MrBubbles
Posts: 87
Joined: 25 Nov 2015, 15:27

Re: Make MSGBOX child of a third-party application

02 Apr 2016, 08:42

Great thought SifJar!
I gave it a shot but it looks like that had the adverse effect of also disabling the custom GUI as well, so I ended up with a completely unresponsive application and custom GUI =(
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: Make MSGBOX child of a third-party application

02 Apr 2016, 10:02

Hmm, I suppose that makes sense. Possibly you can use CreateWindow (or CreateWindowEx, but I think CreateWindow should work) with the WS_POPUP or WS_POPUPWINDOW style.

Or it may be possible to apply one of those styles to the AHK GUI, which might be easier. It might just be a case of doing this: GUI +Owner %hwnd% +0x8000000 (untested)
lexikos
Posts: 7088
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Make MSGBOX child of a third-party application

02 Apr 2016, 17:45

You are applying the option incorrectly. Options are delimited by spaces, so there must not be a space between +Owner and the HWND. If you have a space, the HWND will be misinterpreted as a window style, which I suppose could have the effect of disabling the window, depending on the value of the HWND.

There's no way EnableWindow with the owner HWND will disable the GUI, especially if you are not actually setting the owner. (Note: Top-level windows have owners. Only child windows/controls have parents.) When I tested it worked as expected, and disabled only the parent window (SciTE) and its child windows (controls). Also note that there's no need for DllCall in this case.

Code: Select all

hwnd := WinExist("A")
GUI +Owner%hwnd%
WinSet Disable,, ahk_id %hwnd%
Gui, Add, DropDownList, x12 y30 w450 h40 , DropDownList
Gui, Add, DropDownList, x12 y80 w450 h21 , DropDownList
Gui, Add, ListView, x12 y130 w450 h70 , ListView
Gui, Add, CheckBox, x22 y220 w80 h20 , CheckBox
Gui, Add, Radio, x382 y220 w80 h20 , Radio
Gui, Add, Radio, x292 y220 w80 h20 , Radio
Gui, Add, Button, x362 y290 w100 h30 , Button
Gui, Add, Button, x252 y290 w100 h30 , Button
Gui, Add, CheckBox, x22 y240 w80 h20 , CheckBox
Gui, Show, x446 y211 h341 w479, New GUI Window
Return
 
GuiClose:
WinSet Enable,, ahk_id %hwnd%
ExitApp
+Owner already applies WS_POPUP and removes WS_CHILD.

MessageBox automatically disables its owner window, and this works as expected for me with SciTE.

Code: Select all

MessageBox(WinExist("A"), "Text", "Caption", 0)
 
MessageBox(OwnerHwnd, Text, Caption, Options) {
    DllCall("MessageBox", "ptr", OwnerHwnd, "str", Text, "str", Caption, "uint", Options)
}
jamez
Posts: 3
Joined: 18 Nov 2016, 04:29

Re: Make MSGBOX child of a third-party application

06 Oct 2020, 07:27

Lexikos wrote that MessageBox automatically disables its owner window.
I can reenable the parent window as SifJar wrote, but is it possible to do it in one step with DllCall MessageBox options?

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
From there I found MB_SYSTEMMODAL, but I haven't figured out where and how to enter it in the DllCall

I tried something like this:

Code: Select all

DllCall("MessageBox", "ptr", OwnerHwnd, "str", Text, "str", Caption, "uint", "0 | MB_SYSTEMMODAL")
or
DllCall("MessageBox", "ptr", OwnerHwnd, "str", Text, "str", Caption, "uint", "0 | 0x00001000L")
User avatar
SKAN
Posts: 853
Joined: 29 Sep 2013, 16:58

Re: Make MSGBOX child of a third-party application

06 Oct 2020, 13:53

jamez wrote:
06 Oct 2020, 07:27
From there I found MB_SYSTEMMODAL, but I haven't figured out where and how to enter it in the DllCall
Working example:

Code: Select all

#SingleInstance Force

ListVars
DllCall("MessageBox", "Ptr",A_ScriptHwnd, "Str",A_Now, "Str",A_ScriptName, "Int",0x00001000)
jamez
Posts: 3
Joined: 18 Nov 2016, 04:29

Re: Make MSGBOX child of a third-party application

14 Oct 2020, 11:28

Thank you!
It appears that I misunderstood what MB_SYSTEMMODAL does. I read it again and it does not allow user to interact with parent window. My mistake. I was trying to get a MsgBox that would allow user to interact with parent window with one line of code.

Return to “Ask For Help”

Who is online

Users browsing this forum: Frosti, mikeyww and 53 guests