Why Msgbox dosn't use "TraySetIcon"? Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
xMaxrayx
Posts: 168
Joined: 06 Dec 2022, 02:56
Contact:

Why Msgbox dosn't use "TraySetIcon"?

15 Apr 2024, 03:56

Hi , I want to figure out why msgbox dosn't use "custom icon" but GUI objects can use it?
I use it TraySetIcon


this normal msgbox()
image.png
(31.81 KiB) Downloaded 365 times



.
.
.

this small custom GUI()
image.png
(55.81 KiB) Downloaded 365 times
-----------------------ヾ(•ω•`)o------------------------------
https://github.com/xmaxrayx/
User avatar
mikeyww
Posts: 27009
Joined: 09 Sep 2014, 18:38

Re: Why Msgbox dosn't use "TraySetIcon"?

15 Apr 2024, 05:44

I'm guessing that it's just how Windows implements its MsgBox to be an "independent" sort of dialog window.
User avatar
Seven0528
Posts: 347
Joined: 23 Jan 2023, 04:52
Location: South Korea
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?  Topic is solved

15 Apr 2024, 06:09

 I'm not sure if there's a better way, but here's a trick for now.
You could try using WM_SETICON with Msgbox, but I haven't attempted it yet because it seems like Msgbox needs to be created first before it can be changed.

The reason is probably, as @mikeyww mentioned, because MessageBox is part of the Win32 API. Since MessageBox is a commonly used form not only in AHK but also in other places, I don't think it can be fully customized to suit the user's taste.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
download("http://autohotkey.com/favicon.ico", A_Temp "/autohotkey_favicon.ico")
traySetIcon(A_Temp "/autohotkey_favicon.ico")
F5::  {
    msgboxWithTrayIcon("Hello, World!")
}
MsgboxWithTrayIcon(text?, title?, options?)    { ;  ahk2.0
    myGui := gui("+LastFound +OwnDialogs")
    winSetTransparent(0)
    myGui.show()
    result := msgbox(text?, title?, options?)
    myGui.destroy()
    return result
}
20240415_201621.png
(57.05 KiB) Downloaded 337 times
  • English is not my native language. Please forgive any awkward expressions.
  • 영어는 제 모국어가 아닙니다. 어색한 표현이 있어도 양해해 주세요.
User avatar
xMaxrayx
Posts: 168
Joined: 06 Dec 2022, 02:56
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

15 Apr 2024, 11:17

mikeyww wrote:
15 Apr 2024, 05:44
I'm guessing that it's just how Windows implements its MsgBox to be an "independent" sort of dialog window.
Oh I see so that's why , many thanks <3
Seven0528 wrote: I'm not sure if there's a better way, but here's a trick for now.
You could try using WM_SETICON with Msgbox, but I haven't attempted it yet because it seems like Msgbox needs to be created first before it can be changed.

The reason is probably, as @mikeyww mentioned, because MessageBox is part of the Win32 API. Since MessageBox is a commonly used form not only in AHK but also in other places, I don't think it can be fully customized to suit the user's taste.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
download("http://autohotkey.com/favicon.ico", A_Temp "/autohotkey_favicon.ico")
traySetIcon(A_Temp "/autohotkey_favicon.ico")
F5::  {
    msgboxWithTrayIcon("Hello, World!")
}
MsgboxWithTrayIcon(text?, title?, options?)    { ;  ahk2.0
    myGui := gui("+LastFound +OwnDialogs")
    winSetTransparent(0)
    myGui.show()
    result := msgbox(text?, title?, options?)
    myGui.destroy()
    return result
}
20240415_201621.png
oh great function I thought I need to build a whole custom Msgbox function , many thanks <3
-----------------------ヾ(•ω•`)o------------------------------
https://github.com/xmaxrayx/
Panaku
Posts: 22
Joined: 02 Apr 2022, 17:24

Re: Why Msgbox dosn't use "TraySetIcon"?

15 Apr 2024, 13:45

UPDATE: I may have been a little confused on what was the original goal here. The code I've provided below is for adding the script's Icon to the titlebar of the MsgBox and not to the Window's Taskbar, however it will add the icon to BOTH. I also noticed some potential poor image quality when using this with an uncompiled script. If I found out what's causing that I'll update the code here.

UPDATE #2: Worked out why the image quality was happening and updated the options for LoadPicture, also updated default values for the iconFile and iconNumber parameters so that it will now take into consideration if TraySetIcon() has been used.

We actually just went through this process in the AHK discord last night and this morning. Here is the current solution we have in place, which works very well.

Code: Select all

MsgBoxEx(text?, title?, options?, iconFile := A_IconFile ? A_IconFile : A_AhkPath, iconNumber := A_IconNumber ? A_IconNumber : 1) {
    winTitle := Format("{1} ahk_class #32770", title?)
    OnMessage(0x0044, SetTitleBarIcon)
    result := MsgBox(text?, title?, options?)
    return result

    SetTitleBarIcon(*) {
        DetectHiddenWindows(true)
        DetectHiddenText(true)
        iconHandle := LoadPicture(iconFile, "w24 h24 Icon" iconNumber, &HandleType)
        SendMessage(WM_SETICON := 0x0080, 0, iconHandle, control?, winTitle, text ?? "Press OK to continue.")
        OnMessage(0x0044, SetTitleBarIcon, 0)
        DetectHiddenWindows(false)
        DetectHiddenText(false)
    }
}
Once you include this function into your script, you can call it like a normal MsgBox and you'll have your script's icon added to the titleBar.

Code: Select all

MsgBoxEx("Do you like my new Icon?", "Success Message")
For compiled scripts, if you omit the iconFile and iconNumber parameters, it will use the first icon in the .exe and for uncompiled scripts, you'll need to make sure pass the filepath to the icon you want to use in the iconFile parameter.
User avatar
Seven0528
Posts: 347
Joined: 23 Jan 2023, 04:52
Location: South Korea
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

15 Apr 2024, 17:08

20240416_074213.png
(118.79 KiB) Downloaded 236 times
20240416_074225.png
(120.02 KiB) Downloaded 236 times
20240416_074236.png
(118.48 KiB) Downloaded 236 times

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
suspend(true)
if (!fileExist(iconFileName:=A_Temp "/autohotkey_favicon.ico"))
    download("http://autohotkey.com/favicon.ico", iconFileName)
traySetIcon(iconFileName)
suspend(false)
;-----------------------
F2::msgbox("Hello, World!")
F3::msgbox("Hello, World!",,0x1000)
F6::msgboxWithIcon("Hello, World!")
F7::msgboxWithIcon("Hello, World!",,0x1000)
F12::  {
    hIconSmall:=loadPicture("Shell32.dll", "Icon55", &_)
    hIconBig:=loadPicture("explorer.exe", "Icon22", &_)
    msgboxWithIcon("Hello, World!",,0x1000,hIconSmall,hIconBig)
    for hIcon in [&hIconSmall, &hIconBig]
        dllCall("User32.dll\DestroyIcon", "Ptr",%hIcon%)
}
;-----------------------
MsgboxWithIcon(text?, title?, options?, hIconSmall?, hIconBig?)    { ;  ahk2.0
    WM_COMMNOTIFY(*)    {
        static WM_SETICON:=0x0080, ICON_BIG:=1, ICON_SMALL:=0
            ,WTA_NONCLIENT:=1, WTNCA_NODRAWCAPTION:=0x00000001, WTNCA_NODRAWICON:=0x00000002, WTNCA_NOSYSMENU:=0x00000004, WTNCA_NOMIRRORHELP:=0x00000008            
        winCaption:=(title??A_ScriptName) " ahk_class #32770"
        if (!hWnd:=format("{2}", prevDHW:=detectHiddenWindows(true), winExist(winCaption), detectHiddenWindows(prevDHW)))
            return
        if (hIconSmall)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_SMALL, "Ptr",hIconSmall, "Ptr")
        if (hIconBig)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_BIG, "Ptr",hIconBig, "Ptr")
        if (hIconSmall||hIconBig) && (!draySystemIcon)    {
            eAttribute:=WTA_NONCLIENT
            pvAttribute:=buffer(cbAttribute:=8,0), dwFlags:= dwMask:= 0
            dwFlags|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU       ,numPut("UInt",dwFlags,pvAttribute,0)
            dwMask|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU        ,numPut("UInt",dwMask,pvAttribute,4)
            dllCall("UxTheme.dll\SetWindowThemeAttribute", "Ptr",hwnd, "Int",eAttribute, "Ptr",pvAttribute.Ptr, "UInt",cbAttribute, "Int")
        }
    }
    onApplicationExit(*)    {
        for hIcon in destroyIconList
            dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    }
    static IMAGE_ICON:=1, MB_SYSTEMMODAL:=0x00001000
    destroyIconList:=[], onExit(onApplicationExit)
    ,fileName:=(A_IconFile?A_IconFile:A_AhkPath), iconIndex:=(A_IconNumber?A_IconNumber:1)
    if (!isSet(hIconSmall))    {
        for cIcon in [256,128,64,48,32,16]    {
            if (!dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconSmall:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                continue
            destroyIconList.push(hIconSmall)
            if (!isSet(hIconBig))
                hIconBig:=hIconSmall
            break
        }
    }
    if (!isSet(hIconBig))    {
        for cIcon in [256,128,64,48,32,16]    {
            if (!dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconBig:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                continue
            destroyIconList.push(hIconBig)
            break
        }
    }
    if (!hIconSmall && !hIconBig)
        return msgBox(text?, title?, options?)
    draySystemIcon:=false
    loop Parse, (options??""), A_Space A_Tab    {
        if (A_LoopField~="D)^(?:[[:digit:]]+|(0[Xx][[:xdigit:]]+))$" && A_LoopField&MB_SYSTEMMODAL)
            draySystemIcon:=true    
    }  until (draySystemIcon)
    result:=format("{2}"
        ,onMessage(0x0044,WM_COMMNOTIFY,-1)
        ,msgBox(text?, title?, options?)
        ,onMessage(0x0044,WM_COMMNOTIFY,0))
    onExit(onApplicationExit,false)
    for hIcon in destroyIconList
        dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    return result
}
 You should delete all the unused hIcons by using DestroyIcon.
  • English is not my native language. Please forgive any awkward expressions.
  • 영어는 제 모국어가 아닙니다. 어색한 표현이 있어도 양해해 주세요.
User avatar
xMaxrayx
Posts: 168
Joined: 06 Dec 2022, 02:56
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 05:18

Panaku wrote:
15 Apr 2024, 13:45
UPDATE: I may have been a little confused on what was the original goal here. The code I've provided below is for adding the script's Icon to the titlebar of the MsgBox and not to the Window's Taskbar, however it will add the icon to BOTH. I also noticed some potential poor image quality when using this with an uncompiled script. If I found out what's causing that I'll update the code here.

UPDATE #2: Worked out why the image quality was happening and updated the options for LoadPicture, also updated default values for the iconFile and iconNumber parameters so that it will now take into consideration if TraySetIcon() has been used.

We actually just went through this process in the AHK discord last night and this morning. Here is the current solution we have in place, which works very well.

Code: Select all

MsgBoxEx(text?, title?, options?, iconFile := A_IconFile ? A_IconFile : A_AhkPath, iconNumber := A_IconNumber ? A_IconNumber : 1) {
    winTitle := Format("{1} ahk_class #32770", title?)
    OnMessage(0x0044, SetTitleBarIcon)
    result := MsgBox(text?, title?, options?)
    return result

    SetTitleBarIcon(*) {
        DetectHiddenWindows(true)
        DetectHiddenText(true)
        iconHandle := LoadPicture(iconFile, "w24 h24 Icon" iconNumber, &HandleType)
        SendMessage(WM_SETICON := 0x0080, 0, iconHandle, control?, winTitle, text ?? "Press OK to continue.")
        OnMessage(0x0044, SetTitleBarIcon, 0)
        DetectHiddenWindows(false)
        DetectHiddenText(false)
    }
}
Once you include this function into your script, you can call it like a normal MsgBox and you'll have your script's icon added to the titleBar.

Code: Select all

MsgBoxEx("Do you like my new Icon?", "Success Message")
For compiled scripts, if you omit the iconFile and iconNumber parameters, it will use the first icon in the .exe and for uncompiled scripts, you'll need to make sure pass the filepath to the icon you want to use in the iconFile parameter.
Thanks to much for you effort, I have noticed it will pick the 32*32 icon version instead of 256*256?

this is my icon file
https://github.com/xmaxrayx/Steam-games-wallpapers-finder/blob/main/Asissts/steam%20wallpapers%20finders.ico
image.png
(138.24 KiB) Downloaded 207 times
Update: I have notice if I used icon file that has only one image it wont change but if there multi-image in one Icon it will pick the 32*32.
idk if my icon file wasn't done right.
Last edited by xMaxrayx on 16 Apr 2024, 05:40, edited 1 time in total.
-----------------------ヾ(•ω•`)o------------------------------
https://github.com/xmaxrayx/
User avatar
xMaxrayx
Posts: 168
Joined: 06 Dec 2022, 02:56
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 05:26

Seven0528 wrote:
15 Apr 2024, 17:08
20240416_074213.png
20240416_074225.png
20240416_074236.png

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
suspend(true)
if (!fileExist(iconFileName:=A_Temp "/autohotkey_favicon.ico"))
    download("http://autohotkey.com/favicon.ico", iconFileName)
traySetIcon(iconFileName)
suspend(false)
;-----------------------
F2::msgbox("Hello, World!")
F3::msgbox("Hello, World!",,0x1000)
F6::msgboxWithIcon("Hello, World!")
F7::msgboxWithIcon("Hello, World!",,0x1000)
F12::  {
    hIconSmall:=loadPicture("Shell32.dll", "Icon55", &_)
    hIconBig:=loadPicture("explorer.exe", "Icon22", &_)
    msgboxWithIcon("Hello, World!",,0x1000,hIconSmall,hIconBig)
    for hIcon in [&hIconSmall, &hIconBig]
        dllCall("User32.dll\DestroyIcon", "Ptr",%hIcon%)
}
;-----------------------
MsgboxWithIcon(text?, title?, options?, hIconSmall?, hIconBig?)    { ;  ahk2.0
    WM_COMMNOTIFY(*)    {
        static WM_SETICON:=0x0080, ICON_BIG:=1, ICON_SMALL:=0
            ,WTA_NONCLIENT:=1, WTNCA_NODRAWCAPTION:=0x00000001, WTNCA_NODRAWICON:=0x00000002, WTNCA_NOSYSMENU:=0x00000004, WTNCA_NOMIRRORHELP:=0x00000008            
        winCaption:=(title??A_ScriptName) " ahk_class #32770"
        if (!hWnd:=format("{2}", prevDHW:=detectHiddenWindows(true), winExist(winCaption), detectHiddenWindows(prevDHW)))
            return
        if (hIconSmall)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_SMALL, "Ptr",hIconSmall, "Ptr")
        if (hIconBig)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_BIG, "Ptr",hIconBig, "Ptr")
        if (hIconSmall||hIconBig) && (!draySystemIcon)    {
            eAttribute:=WTA_NONCLIENT
            pvAttribute:=buffer(cbAttribute:=8,0), dwFlags:= dwMask:= 0
            dwFlags|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU       ,numPut("UInt",dwFlags,pvAttribute,0)
            dwMask|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU        ,numPut("UInt",dwMask,pvAttribute,4)
            dllCall("UxTheme.dll\SetWindowThemeAttribute", "Ptr",hwnd, "Int",eAttribute, "Ptr",pvAttribute.Ptr, "UInt",cbAttribute, "Int")
        }
    }
    onApplicationExit(*)    {
        for hIcon in destroyIconList
            dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    }
    static IMAGE_ICON:=1, MB_SYSTEMMODAL:=0x00001000
    destroyIconList:=[], onExit(onApplicationExit)
    ,fileName:=(A_IconFile?A_IconFile:A_AhkPath), iconIndex:=(A_IconNumber?A_IconNumber:1)
    if (!isSet(hIconSmall))    {
        for cIcon in [256,128,64,48,32,16]    {
            if (!dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconSmall:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                continue
            destroyIconList.push(hIconSmall)
            if (!isSet(hIconBig))
                hIconBig:=hIconSmall
            break
        }
    }
    if (!isSet(hIconBig))    {
        for cIcon in [256,128,64,48,32,16]    {
            if (!dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconBig:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                continue
            destroyIconList.push(hIconBig)
            break
        }
    }
    if (!hIconSmall && !hIconBig)
        return msgBox(text?, title?, options?)
    draySystemIcon:=false
    loop Parse, (options??""), A_Space A_Tab    {
        if (A_LoopField~="D)^(?:[[:digit:]]+|(0[Xx][[:xdigit:]]+))$" && A_LoopField&MB_SYSTEMMODAL)
            draySystemIcon:=true    
    }  until (draySystemIcon)
    result:=format("{2}"
        ,onMessage(0x0044,WM_COMMNOTIFY,-1)
        ,msgBox(text?, title?, options?)
        ,onMessage(0x0044,WM_COMMNOTIFY,0))
    onExit(onApplicationExit,false)
    for hIcon in destroyIconList
        dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    return result
}
 You should delete all the unused hIcons by using DestroyIcon.

Thank you so much
win11 64bit
  • *F3 doesn't display the Icon
    *F7 show on Taskbar only (lower than 256
    *F6 shows on both msgbox and tashbar (lower than 256)
    *F12 looks interesting xd
thanks again <3
-----------------------ヾ(•ω•`)o------------------------------
https://github.com/xmaxrayx/
User avatar
xMaxrayx
Posts: 168
Joined: 06 Dec 2022, 02:56
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 05:49

so seems if you want higher res ICON you need to use custom GUI object? because windows msgbox doesn't support it.
-----------------------ヾ(•ω•`)o------------------------------
https://github.com/xmaxrayx/
User avatar
Seven0528
Posts: 347
Joined: 23 Jan 2023, 04:52
Location: South Korea
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 08:20

 Personally, I believe that directly FileInstalling the ICO file and then obtaining the hIcon corresponding to size 256 would be a better approach. Referring to the PrivateExtractIcons part in the code I provided earlier might be helpful.
I don't have much experience using LoadPicture, so I find it difficult to advise on that function.
To be honest, I don't prefer this method either, so I use the method of converting a Base64-encoded string hardcoded using CryptStringToBinary and CreateIconFromResourceEx to obtain hIcon. However, I only have code corresponding to AHK v1, so it's difficult to share.
There's a thread here titled "Convert Base64PNG_to_HICON to v2" that someone else has written,
so if you're interested, you might want to take a look at it.
  • English is not my native language. Please forgive any awkward expressions.
  • 영어는 제 모국어가 아닙니다. 어색한 표현이 있어도 양해해 주세요.
Panaku
Posts: 22
Joined: 02 Apr 2022, 17:24

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 11:31

So, I'm not sure there is a need for doing all of the DllCalls to load the different resolutions of the icons. The SendMessage() call lets us control which resolution is used by defining the width and height when we use LoadPicture. We've made some adjustments to the function that I posted yesterday, including working out a way to override the base MsgBox function to use our custom function instead.

The following code has a few new things compared to the code from yesterday.
1. It now using your system's settings for determining what the icon size should be for titlebar and the taskbar. This can result in different resolution versions being used for each icon.
2. It overrides the default MsgBox() function. Now anytime you use that function, it will add the icons to it automatically.

Code: Select all

#Requires AutoHotkey v2+
TraySetIcon("MyIcon.ico")
MsgBox.Native := MsgBox.Call
MsgBox.DefineProp("Call", {Call: MsgBoxEx})
MsgBoxEx(this, Text?, Title?, Options?, IconFile := A_IconFile ? A_IconFile : A_AhkPath, IconNumber := A_IconNumber ? A_IconNumber : 1) {
    WinTitle := (title ?? A_ScriptName) " ahk_class #32770"
    OnMessage(0x0044, WM_NOTIFY_MSGBOXEX)
    result := this.Native(text?, title?, options?)
    return result

    WM_NOTIFY_MSGBOXEX(*) {
        static ICON_BIG := 1, ICON_SMALL := 0, SM_CXICON := SysGet(11), SM_CYICON := SysGet(12), SM_CXSMICON := SysGet(49), SM_CYSMICON := SysGet(50), WM_SETICON := 0x0080
        hICON_BIG := LoadPicture(IconFile, "w" SM_CXICON " h" SM_CYICON " Icon" IconNumber, &HandleType)
        hICON_SMALL := LoadPicture(IconFile, "w" SM_CXSMICON " h" SM_CYSMICON " Icon" IconNumber, &HandleType)
        DetectHiddenWindows(True)
        SendMessage(WM_SETICON, ICON_BIG, hICON_BIG, Control?, WinTitle, Text ?? "Press OK to continue.")
        SendMessage(WM_SETICON, ICON_SMALL, hICON_SMALL, Control?, WinTitle, Text ?? "Press Ok to continue.")
        DetectHiddenWindows(False)
        OnMessage(0x0044, WM_NOTIFY_MSGBOXEX, 0)
    }
}
MsgBox("Test")
Now, if you want to force the MsgBox to use a different resolution icon, then you can force that change by changing the width and height values in the LoadPicture() calls.
Note: Forcing a larger resolution icon may affect quality, as you're still squishing the larger icon down into a smaller space.

Code: Select all

hICON_BIG := LoadPicture(IconFile, "w256 h256 Icon" IconNumber, &HandleType)
hICON_SMALL := LoadPicture(IconFile, "w256 h256 Icon" IconNumber, &HandleType)
The benefit to the above code is that now all your MsgBoxes in this script will have icons added based off of either the icon used when you compiled the script or the icon used during TraySetIcon(), but you can also use a different icon for specific MsgBoxes by calling MsgBoxEx directly.

Code: Select all

MsgBoxEx(MsgBox, "test",,, "shell32.dll", 54)
The only thing different about calling MsgBoxEx directly, is that now we have to pass the MsgBox function as the first parameter. I found this setup to be ideal because now I don't have to call a custom function everytime I want to add an Icon to my MsgBoxes. If you don't want to override the base MsgBox function and would prefer to call the custom function manually, then you can just use the following code.

Code: Select all

#Requires AutoHotkey v2+
TraySetIcon("MyIcon.ico")
MsgBoxEx(Text?, Title?, Options?, IconFile := A_IconFile ? A_IconFile : A_AhkPath, IconNumber := A_IconNumber ? A_IconNumber : 1) {
    WinTitle := (title ?? A_ScriptName) " ahk_class #32770"
    OnMessage(0x0044, WM_NOTIFY_MSGBOXEX)
    result := MsgBox(text?, title?, options?)
    return result

    WM_NOTIFY_MSGBOXEX(*) {
        static ICON_BIG := 1, ICON_SMALL := 0, SM_CXICON := SysGet(11), SM_CYICON := SysGet(12), SM_CXSMICON := SysGet(49), SM_CYSMICON := SysGet(50), WM_SETICON := 0x0080
        hICON_BIG := LoadPicture(IconFile, "w" SM_CXICON " h" SM_CYICON " Icon" IconNumber, &HandleType)
        hICON_SMALL := LoadPicture(IconFile, "w" SM_CXSMICON " h" SM_CYSMICON " Icon" IconNumber, &HandleType)
        DetectHiddenWindows(True)
        SendMessage(WM_SETICON, ICON_BIG, hICON_BIG, Control?, WinTitle, Text ?? "Press OK to continue.")
        SendMessage(WM_SETICON, ICON_SMALL, hICON_SMALL, Control?, WinTitle, Text ?? "Press Ok to continue.")
        DetectHiddenWindows(False)
        OnMessage(0x0044, WM_NOTIFY_MSGBOXEX, 0)
    }
}
MsgBoxEx("Test")
User avatar
Seven0528
Posts: 347
Joined: 23 Jan 2023, 04:52
Location: South Korea
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 13:40

 @Panaku
Certainly, to clarify, my preference for using PrivateExtractIcons instead of LoadPicture is simply because it's more familiar to me personally. I'm not asserting that the former is better. More than anything, our codes do slightly different things. Your code is more user-friendly, while mine is more friendly to me. (I tend to prefer handling hIcons directly.)
It was meant to demonstrate that code can be written in this way, not to claim superiority over your code.

It is only necessary to call DestroyIcon for icons and cursors created with the following functions: CreateIconFromResourceEx (if called without the LR_SHARED flag), CreateIconIndirect, and CopyIcon. Do not use this function to destroy a shared icon. A shared icon is valid as long as the module from which it was loaded remains in memory. The following functions obtain a shared icon.

LoadIcon
LoadImage (if you use the LR_SHARED flag)
CopyImage (if you use the LR_COPYRETURNORG flag and the hImage parameter is a shared icon)
CreateIconFromResource
CreateIconFromResourceEx (if you use the LR_SHARED flag)
However, one thing is clear, as I mentioned earlier, there is still a slight issue with the code you provided. If hIcon is made a local variable, it must be destroyed using DestroyIcon. That's the point I wanted to address.

Actually, I don't know how the built-in LoadPicture is implemented in practice. However, from the information in the official reference, it seems that if an hIcon is created through LoadPicture, it should also be destroyed using DestroyIcon.
https://www.autohotkey.com/docs/v2/lib/LoadPicture.htm#Remarks

Code: Select all

if (not OutImageType)  ; IMAGE_BITMAP (0) or the OutImageType parameter was omitted.
    DllCall("DeleteObject", "ptr", Handle)
else if (OutImageType = 1)  ; IMAGE_ICON
    DllCall("DestroyIcon", "ptr", Handle)
else if (OutImageType = 2)  ; IMAGE_CURSOR
    DllCall("DestroyCursor", "ptr", Handle)
Last edited by Seven0528 on 16 Apr 2024, 19:40, edited 2 times in total.
  • English is not my native language. Please forgive any awkward expressions.
  • 영어는 제 모국어가 아닙니다. 어색한 표현이 있어도 양해해 주세요.
User avatar
Seven0528
Posts: 347
Joined: 23 Jan 2023, 04:52
Location: South Korea
Contact:

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 13:46

 Anyway, using SysGet to retrieve the icon size seems like a really good idea. It's another thing I'm learning along the way! Thank you.

Code: Select all

MsgboxWithIcon(text?, title?, options?, hIconSmall?, hIconBig?)    { ;  ahk2.0
    WM_COMMNOTIFY(*)    {
        static WM_SETICON:=0x0080, ICON_BIG:=1, ICON_SMALL:=0
            ,WTA_NONCLIENT:=1, WTNCA_NODRAWCAPTION:=0x00000001, WTNCA_NODRAWICON:=0x00000002, WTNCA_NOSYSMENU:=0x00000004, WTNCA_NOMIRRORHELP:=0x00000008            
        winCaption:=(title??A_ScriptName) " ahk_class #32770"
        if (!hWnd:=format("{2}", prevDHW:=detectHiddenWindows(true), winExist(winCaption), detectHiddenWindows(prevDHW)))
            return
        if (hIconSmall)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_SMALL, "Ptr",hIconSmall, "Ptr")
        if (hIconBig)
            dllCall("User32.dll\SendMessage", "Ptr",hWnd, "UInt",WM_SETICON, "UPtr",ICON_BIG, "Ptr",hIconBig, "Ptr")
        if (hIconSmall||hIconBig) && (!drawSystemIcon)    {
            eAttribute:=WTA_NONCLIENT
            pvAttribute:=buffer(cbAttribute:=8,0), dwFlags:= dwMask:= 0
            dwFlags|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU       ,numPut("UInt",dwFlags,pvAttribute,0)
            dwMask|=WTNCA_NODRAWICON|WTNCA_NOSYSMENU        ,numPut("UInt",dwMask,pvAttribute,4)
            dllCall("UxTheme.dll\SetWindowThemeAttribute", "Ptr",hwnd, "Int",eAttribute, "Ptr",pvAttribute.Ptr, "UInt",cbAttribute, "Int")
        }
    }
    onApplicationExit(*)    {
        for hIcon in destroyIconList
            dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    }
    static IMAGE_ICON:=1, MB_SYSTEMMODAL:=0x00001000
    destroyIconList:=[], onExit(onApplicationExit)
    ,fileName:=(A_IconFile?A_IconFile:A_AhkPath), iconIndex:=(A_IconNumber?A_IconNumber:1)
    if (!isSet(hIconSmall))    {
        if (dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",SM_CXSMICON:=sysGet(49), "Int",SM_CYSMICON:=sysGet(50), "Ptr*",&hIconSmall:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))    {
            for cIcon in [256,128,64,48,32,16]    {
                if (dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconSmall:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                    break
            }
        }
        if (hIconSmall)
            destroyIconList.push(hIconSmall)
    }
    if (!isSet(hIconBig))    {
        if (dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",SM_CXICON:=sysGet(11), "Int",SM_CYICON:=sysGet(12), "Ptr*",&hIconBig:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))    {
            for cIcon in [256,128,64,48,32,16]    {
                if (dllCall("User32.dll\PrivateExtractIcons", "Str",fileName, "Int",iconIndex-1, "Int",cIcon, "Int",cIcon, "Ptr*",&hIconBig:=0, "UPtr*",0, "UInt",1, "UInt",IMAGE_ICON, "UInt"))
                    break
            }
        }
        if (hIconBig)
            destroyIconList.push(hIconBig)
    }
    if (!hIconSmall && !hIconBig)
        return msgBox(text?, title?, options?)
    drawSystemIcon:=false
    loop Parse, (options??""), A_Space A_Tab    {
        if (A_LoopField~="D)^(?:[[:digit:]]+|(0[Xx][[:xdigit:]]+))$" && A_LoopField&MB_SYSTEMMODAL)
            drawSystemIcon:=true    
    }  until (drawSystemIcon)
    result:=format("{2}"
        ,onMessage(0x0044,WM_COMMNOTIFY,-1)
        ,msgBox(text?, title?, options?)
        ,onMessage(0x0044,WM_COMMNOTIFY,0))
    onExit(onApplicationExit,false)
    for hIcon in destroyIconList
        dllCall("User32.dll\DestroyIcon", "Ptr",hIcon)
    return result
}
Last edited by Seven0528 on 17 Apr 2024, 06:40, edited 1 time in total.
  • English is not my native language. Please forgive any awkward expressions.
  • 영어는 제 모국어가 아닙니다. 어색한 표현이 있어도 양해해 주세요.
Panaku
Posts: 22
Joined: 02 Apr 2022, 17:24

Re: Why Msgbox dosn't use "TraySetIcon"?

16 Apr 2024, 19:18

@Seven0528, thanks for pointing out that bit in the documentation for LoadPicture(). Ran some leak tests and was noticing some performance issues after about 1600 MsgBox() calls, so we've tweaked the function yet again, now deleting the handles right after they've been applied and are now no longer needed.

Code: Select all

MsgBox.Native := MsgBox.Call
MsgBox.DefineProp("Call", {Call: MsgBoxEx})
MsgBoxEx(this, Text?, Title?, Options?, IconFile := A_IconFile ? A_IconFile : A_AhkPath, IconNumber := A_IconNumber ? A_IconNumber : 1) {
    WinTitle := (title ?? A_ScriptName) " ahk_class #32770"
    OnMessage(0x0044, WM_NOTIFY_MSGBOXEX)
    result := this.Native(text?, title?, options?)
    return result

    WM_NOTIFY_MSGBOXEX(*) {
        static ICON_BIG := 1, ICON_SMALL := 0, SM_CXICON := SysGet(11), SM_CYICON := SysGet(12), SM_CXSMICON := SysGet(49), SM_CYSMICON := SysGet(50), WM_SETICON := 0x0080
        hICON_BIG := LoadPicture(IconFile, "w" SM_CXICON " h" SM_CYICON " Icon" IconNumber, &HandleType)
        hICON_SMALL := LoadPicture(IconFile, "w" SM_CXSMICON " h" SM_CYSMICON " Icon" IconNumber, &HandleType)
        DetectHiddenWindows(True)
        SendMessage(WM_SETICON, ICON_BIG, hICON_BIG, Control?, WinTitle, Text ?? "Press OK to continue.")
        SendMessage(WM_SETICON, ICON_SMALL, hICON_SMALL, Control?, WinTitle, Text ?? "Press Ok to continue.")
        DetectHiddenWindows(False)
        OnMessage(0x0044, WM_NOTIFY_MSGBOXEX, 0)
        SetTimer((*) => DllCall("DestroyIcon", "ptr", hICON_BIG), -100)
        SetTimer((*) => DllCall("DestroyIcon", "ptr", hICON_SMALL), -100)
    }
}

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: teadrinker, XMCQCX and 43 guests