Any chance to have an updated code for Win64 systray icon?

Started by fielder, February 14, 2025, 09:25:33

Previous topic - Next topic

fielder

on blitzcoder there is Win32 code: https://www.blitzbasic.org/forum/topic.php?id=366

anyone more skilled than me can made an updated Win64 code?

very thank you!

' Tray minimize win32 application

Import MaxGUI.drivers

?Win32
' tray icons
'
Const NIM_ADD:Int           = 0
Const NIM_MODIFY:Int        = 1
Const NIM_DELETE:Int        = 2
Const NIM_SETFOCUS:Int      = 3
Const NIM_SETVERSION:Int    = 4

Const NIF_MESSAGE:Int   = $00000001
Const NIF_ICON:Int      = $00000002
Const NIF_TIP:Int       = $00000004
Const NIF_STATE:Int = $00000008
Const NIF_INFO:Int      = $00000010
Const NIF_GUID:Int      = $00000020

Type TNotifyIconData
    Field Size:Int
    Field HWND:Int
    Field id:Int
    Field Flags:Int
    Field CallbackMessage:Int
    Field Icon:Int              ' HICON
    Field Tip:Long              ' array [0..63] of AnsiChar;
    Field Tip2:Long
    Field Tip3:Long
    Field Tip4:Long
    Field Tip5:Long
    Field Tip6:Long
    Field Tip7:Long
    Field Tip8:Long
EndType

Extern "WIN32"
    Function Shell_NotifyIcon:Int( message:Int, notifyicondata:Byte Ptr) = "Shell_NotifyIconA@8"
EndExtern

Function SetNotifyIconDataTip( nid:TNotifyIconData, s:String)
    MemClear( Varptr nid.Tip, 64)
    If s.length > 0 Then
        Local p:Byte Ptr = s.ToCString()
        If s.length < 64 Then
            MemCopy( Varptr nid.Tip, p, s.length)
        Else           
            MemCopy( Varptr nid.Tip, p, 63)        
        EndIf
        MemFree( p)
    EndIf
EndFunction

'
' window messages (allso used by tray icon)
'
Const WM_MOUSEMOVE:Int        = $0200
Const WM_LBUTTONDOWN:Int      = $0201
Const WM_LBUTTONUP:Int        = $0202
Const WM_LBUTTONDBLCLK:Int    = $0203
Const WM_RBUTTONDOWN:Int      = $0204
Const WM_RBUTTONUP:Int        = $0205
Const WM_RBUTTONDBLCLK:Int    = $0206
Const WM_MBUTTONDOWN:Int      = $0207
Const WM_MBUTTONUP:Int        = $0208
Const WM_MBUTTONDBLCLK:Int    = $0209

'
' icon resources
'
Const IMAGE_BITMAP:Int      = 0
Const IMAGE_ICON:Int        = 1
Const IMAGE_CURSOR:Int      = 2
Const IMAGE_ENHMETAFILE:Int = 3

Const LR_DEFAULTSIZE:Int      = 64
Const LR_DEFAULTCOLOR:Int     = 0
Const LR_MONOCHROME:Int       = 1
Const LR_COLOR:Int            = 2
Const LR_COPYRETURNORG:Int    = 4
Const LR_COPYDELETEORG:Int    = 8
Const LR_LOADFROMFILE:Int     = 16
Const LR_LOADTRANSPARENT:Int  = 32
Const LR_LOADREALSIZE:Int     = 128
Const LR_LOADMAP3DCOLORS:Int  = 4096
Const LR_CREATEDIBSECTION:Int = 8192
Const LR_COPYFROMRESOURCE:Int = $4000 ' 0x4000
Const LR_SHARED:Int           = 32768          

Global nid:TNotifyIconData

Extern "WIN32"
    Function LoadImage_:Int( Instance:Int, Name$z, Type_:Int, DesiredX:Int, DesiredY:Int, Load:Int) = "LoadImageA@24"
EndExtern

Function LoadIcon:Int( filename:String, width:Int=16,Height:Int=16, Flags:Int=LR_SHARED)
    Return LoadImage_( 0, filename, IMAGE_ICON, width,Height, LR_LOADFROMFILE| Flags)
EndFunction

?

' Register tray icon
Function RegisterTrayIcon(window:TGadget, toolTip:String, iconFile:String)

?Win32
    nid = New TNotifyIconData
    nid.Size = SizeOf(TNotifyIconData)
    nid.hwnd = QueryGadget(window, QUERY_HWND)
    nid.id = 0
    nid.CallbackMessage = WM_MOUSEMOVE
    nid.Icon = LoadIcon( iconFile)
    nid.Flags = NIF_MESSAGE | NIF_TIP | NIF_ICON
    SetNotifyIconDataTip( nid, toolTip )
    Shell_NotifyIcon( NIM_ADD, nid)
?
End Function  

'Remove tray icon
Function RemoveTrayIcon()   
?Win32
    Shell_NotifyIcon(NIM_DELETE, nid)
?
End Function

SuperStrict

Include "trayappmodule.bmx"

'
' EXAMPLE
'
Local window:TGadget = CreateWindow( "Window", 80,80,640,480)
Local panel:TGadget = CreatePanel( 0,0, 0,0, window, PANEL_ACTIVE)

RegisterTrayIcon(panel, "Click to activate app", "beholder.ico")

Repeat
    WaitEvent()

    Select EventID()
        Case EVENT_MOUSEMOVE
            If EventSource() = panel Then
                Select EventX()
'                   Case WM_MOUSEMOVE       Print "mouse move"
'                   Case WM_LBUTTONDOWN Print "left down"
                    Case WM_LBUTTONUP       ShowGadget(window)'; Shell_NotifyIcon( NIM_DELETE, nid)
                    Case WM_LBUTTONDBLCLK   ShowGadget(window)'; Shell_NotifyIcon( NIM_DELETE, nid)
'                   Case WM_RBUTTONDOWN Print "right down"
'                   Case WM_RBUTTONUP       Print "right up"
'                   Case WM_RBUTTONDBLCLK   Print "right doucle click"
'                   Case WM_MBUTTONDOWN Print "middle down"
'                   Case WM_MBUTTONUP       Print "middle up"
'                   Case WM_MBUTTONDBLCLK   Print "middle double click"
                    Default
'                       Print "unknown " + EventX()
                EndSelect
            EndIf
        Case EVENT_WINDOWCLOSE
            'Shell_NotifyIcon( NIM_ADD, nid)       
            HideGadget(window)
    EndSelect
Forever

' unregister tray icon
RemoveTrayIcon()

End

Scaremonger

I haven't written Windows API code in a while because I moved to Linux, but the "Win32 API" is just a name and is still relevant on 64 bit systems. I don't think there is a specific Win64 API.

Apart from adding an import for MaxGUI.drivers, I think that old code should probably still work.

What problem are you having with it?

fielder

Hi Scaremonger!!! sorry for the delay :)

Function RegisterTrayIcon(window:TGadget, toolTip:String, iconFile:String)

?Win32
    nid = New TNotifyIconData
    nid.Size = SizeOf(TNotifyIconData)
   -------------------> nid.hwnd = QueryGadget(window, QUERY_HWND) <--------------------
    nid.id = 0
    nid.CallbackMessage = WM_MOUSEMOVE
    nid.Icon = LoadIcon( iconFile)
    nid.flags = NIF_MESSAGE | NIF_TIP | NIF_ICON
    SetNotifyIconDataTip( nid, toolTip )
    Shell_NotifyIcon( NIM_ADD, nid)
?
End Function 
Unable to convert from byte Ptr to Int (the usual error when building old 32 stuff to 64)

Henri

In 64-bit world windows handles (hwnd) are presented as byte ptr's in Bmax-ng. This applies to pretty much to all memory handles in WinAPI. Other one is the icon handle in TNotifyIconData type.

Another difference is the way external API functions are declared.

Example in legacy:
Extern "WIN32"
    Function Shell_NotifyIcon:Int( message:Int, notifyicondata:Byte Ptr) = "Shell_NotifyIconA@8"
EndExtern

Example in NG
Extern "WIN32"
    Function Shell_NotifyIcon:Int( message:Int, notifyicondata:Byte Ptr) = "INT Shell_NotifyIconA(INT, BYTE*)!"
EndExtern

Note the ! char at the end. This tells the translator to use this declaration when ever there is a conflicting previous declaration somewhere.

If you import Pub.Win32, there is a chance that declaration is already there and you don't need to declare this in your code.

-Henri
- Got 01100011 problems, but the bit ain't 00000001

fielder

Very thnak you!

and what about this:

Extern "WIN32"
    Function LoadImage_:Int( Instance:Int, Name$z, Type_:Int, DesiredX:Int, DesiredY:Int, Load:Int) = "LoadImageA@24"
EndExtern


i replaced it by
Extern "WIN32"
    Function LoadImage_:Int( Instance:Int, Name$z, Type_:Int, DesiredX:Int, DesiredY:Int, Load:Int) = "INT LoadImageA(INT,BBSTRING,INT,INT,INT,INT)!"
EndExtern


it compiles, the programs is tsrating but there is an empty icon on traybar

Henri

Icon file needs to be in .ICO 16 x 16 pixels format.

-Henri
- Got 01100011 problems, but the bit ain't 00000001

fielder

Quote from: Henri on February 20, 2025, 12:36:23Icon file needs to be in .ICO 16 x 16 pixels format.

-Henri
i was using the same that was working on 32bit application without any issue.. but just to try i created a fresh new icon 16x16 bat there is still an empty icon on tray bar

if i remove the "!" i rcevive this error:

C:/BlitzMax/MinGW32x64/x86_64-w64-mingw32/include/winuser.h:4202:28: note: previous declaration of 'LoadImageA' with type 'void *(struct HINSTANCE__ *, const CHAR *, UINT,  int,  int,  UINT)' {aka 'void *(struct HINSTANCE__ *, const char *, unsigned int,  int,  int,  unsigned int)'}
 4202 |  WINUSERAPI HANDLE WINAPI LoadImageA(HINSTANCE hInst,LPCSTR name,UINT type,int cx,int cy,UINT fuLoad);
      |                            ^~~~~~~~~~
Build Error: failed to compile (1) C:/Users/test/Documents/.bmx/tray.bmx.gui.release.win32.x64.c
Process complete

fielder

' Tray minimize win64 test application

Import MaxGUI.drivers

Const NIM_ADD:Int          = 0
Const NIM_MODIFY:Int        = 1
Const NIM_DELETE:Int        = 2
Const NIM_SETFOCUS:Int      = 3
Const NIM_SETVERSION:Int    = 4

Const NIF_MESSAGE:Int  = $00000001
Const NIF_ICON:Int      = $00000002
Const NIF_TIP:Int      = $00000004
Const NIF_STATE:Int = $00000008
Const NIF_INFO:Int      = $00000010
Const NIF_GUID:Int      = $00000020

Type TNotifyIconData
    Field Size:Int
    Field HWND:Byte Ptr
    Field id:Int
    Field flags:Int
    Field CallbackMessage:Int
    Field Icon:Int              ' HICON
    Field Tip:Long              ' array [0..63] of AnsiChar;
    Field Tip2:Long
    Field Tip3:Long
    Field Tip4:Long
    Field Tip5:Long
    Field Tip6:Long
    Field Tip7:Long
    Field Tip8:Long
EndType

'Extern "WIN32"
'  Function Shell_NotifyIcon:Int( message:Int, notifyicondata:Byte Ptr) = "Shell_NotifyIconA@8"
'EndExtern

Extern "WIN32"
    Function Shell_NotifyIcon:Int( message:Int, notifyicondata:Byte Ptr) = "INT Shell_NotifyIconA(INT, BYTE*)!"
EndExtern

Function SetNotifyIconDataTip( nid:TNotifyIconData, s:String)
    MemClear( Varptr nid.Tip, 64)
    If s.length > 0 Then
        Local p:Byte Ptr = s.ToCString()
        If s.length < 64 Then
            'MemCopy( Varptr nid.Tip, p, s.length)
            MemCopy( Varptr nid.Tip, p, Size_T(s.length))

        Else         
            MemCopy( Varptr nid.Tip, p, 63)       
        EndIf
        MemFree( p)
    EndIf
EndFunction

'
' window messages (allso used by tray icon)
'
Const WM_MOUSEMOVE:Int        = $0200
Const WM_LBUTTONDOWN:Int      = $0201
Const WM_LBUTTONUP:Int        = $0202
Const WM_LBUTTONDBLCLK:Int    = $0203
Const WM_RBUTTONDOWN:Int      = $0204
Const WM_RBUTTONUP:Int        = $0205
Const WM_RBUTTONDBLCLK:Int    = $0206
Const WM_MBUTTONDOWN:Int      = $0207
Const WM_MBUTTONUP:Int        = $0208
Const WM_MBUTTONDBLCLK:Int    = $0209

'
' icon resources
'
Const IMAGE_BITMAP:Int      = 0
Const IMAGE_ICON:Int        = 1
Const IMAGE_CURSOR:Int      = 2
Const IMAGE_ENHMETAFILE:Int = 3

Const LR_DEFAULTSIZE:Int      = 64
Const LR_DEFAULTCOLOR:Int    = 0
Const LR_MONOCHROME:Int      = 1
Const LR_COLOR:Int            = 2
Const LR_COPYRETURNORG:Int    = 4
Const LR_COPYDELETEORG:Int    = 8
Const LR_LOADFROMFILE:Int    = 16
Const LR_LOADTRANSPARENT:Int  = 32
Const LR_LOADREALSIZE:Int    = 128
Const LR_LOADMAP3DCOLORS:Int  = 4096
Const LR_CREATEDIBSECTION:Int = 8192
Const LR_COPYFROMRESOURCE:Int = $4000 ' 0x4000
Const LR_SHARED:Int          = 32768         

Global nid:TNotifyIconData

Extern "WIN32"
    'Function LoadImage_:Int( Instance:Int, Name$z, Type_:Int, DesiredX:Int, DesiredY:Int, Load:Int) = "LoadImageA@24"
    Function LoadImage_:Int( Instance:Int, Name$z, Type_:Int, DesiredX:Int, DesiredY:Int, Load:Int) = "INT LoadImageA(INT,BBSTRING,INT,INT,INT,INT)!"

EndExtern

Function LoadIcon:Int( filename:String, width:Int=16,height:Int=16, flags:Int=LR_SHARED)
    Return LoadImage_( 0, filename, IMAGE_ICON, width,height, LR_LOADFROMFILE| flags)
EndFunction

?

' Register tray icon
Function RegisterTrayIcon(window:TGadget, toolTip:String, iconFile:String)


    nid = New TNotifyIconData
    nid.Size = SizeOf(TNotifyIconData)
    nid.hwnd = QueryGadget(window, QUERY_HWND)
    nid.id = 0
    nid.CallbackMessage = WM_MOUSEMOVE
    nid.Icon = LoadIcon( iconFile)
    nid.flags = NIF_MESSAGE | NIF_TIP | NIF_ICON
    SetNotifyIconDataTip( nid, toolTip )
    Shell_NotifyIcon( NIM_ADD, nid)

End Function 

'Remove tray icon
Function RemoveTrayIcon() 

    Shell_NotifyIcon(NIM_DELETE, nid)

End Function


'
' EXAMPLE
'
Local window:TGadget = CreateWindow( "Window", 80,80,640,480)
Local panel:TGadget = CreatePanel( 0,0, 0,0, window, PANEL_ACTIVE)

RegisterTrayIcon(panel, "this is a test", "1616icon.ico")

Repeat
    WaitEvent()

    Select EventID()
        Case EVENT_MOUSEMOVE
            If EventSource() = panel Then
                Select EventX()

                    Case WM_LBUTTONUP      ShowGadget(window)'; Shell_NotifyIcon( NIM_DELETE, nid)
                    Case WM_LBUTTONDBLCLK  ShowGadget(window)'; Shell_NotifyIcon( NIM_DELETE, nid)

                    Default
'                      Print "unknown " + EventX()
                EndSelect
            EndIf
        Case EVENT_WINDOWCLOSE
            'Shell_NotifyIcon( NIM_ADD, nid)     
            HideGadget(window)
    EndSelect
Forever

' unregister tray icon
RemoveTrayIcon()

End

Henri

You have to change the icon field to byte ptr.

Field Icon:Int -> Byte Ptr

Also, I did notice the icon can be bigger then 16 x 16 (as long as its .ico). Windows seems to re-scale it.

-Henri
- Got 01100011 problems, but the bit ain't 00000001

fielder

Quote from: Henri on February 22, 2025, 22:28:27You have to change the icon field to byte ptr.

Field Icon:Int -> Byte Ptr

Also, I did notice the icon can be bigger then 16 x 16 (as long as its .ico). Windows seems to re-scale it.

-Henri
VERY VERY THANK YOU!!!

fielder

But... there is not feedback from Tray icon the pop up appear but clicking on it or moving mouse on main window doesn't print any comment , tried also the original version for old Blitz 32 but it's the same , can be something related to windows 11? (something changed from Windows 10 ?)

can't have a feeedback of any kinf clicking on the tray icon

Henri

Example is missing the callback function that is responsible for relaying messages from tray icon.

Add this to your code:

'Windows callback function to get/post events
Function SysTrayWndProc:Byte Ptr(hwnd:Byte Ptr, msg:UInt, wp:WParam, lp:LParam) "win32"

     Select msg

     Case WM_MOUSEMOVE
          Local owner:TGadget = TWindowsGUIDriver.GadgetFromHwnd(hwnd)
          If owner Then
               Select lp
               Case WM_MOUSELEAVE
                    PostGuiEvent( EVENT_MOUSELEAVE, owner )
               Case WM_MOUSEMOVE
                    PostGuiEvent( EVENT_MOUSEMOVE, owner )
               Case WM_RBUTTONDOWN
                    PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_RIGHT )
               Case WM_LBUTTONDOWN
                    PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_LEFT )
               Case WM_RBUTTONDOWN     
                    PostGuiEvent( EVENT_MOUSEDOWN, owner, MOUSE_RIGHT )
               Case WM_LBUTTONDBLCLK
                    PostGuiEvent( EVENT_MOUSEUP, owner )
               EndSelect
          EndIf
     EndSelect

     Return TWindowsGUIDriver.ClassWndProc(hwnd, msg, wp, lp)
EndFunction


Then you need to register the callback function by adding this line in the RegisterTrayIcon function (somewhere at the end).

SetWindowLongW(nid.hwnd, GWL_WNDPROC, Int(Byte Ptr(SysTrayWndProc)) )


Here is the updated example:
' Tray minimize win64 test application

Import MaxGUI.drivers

'
' EXAMPLE
'
Local window:TGadget = CreateWindow( "Window", 80, 80, 640, 480)
Local panel:TGadget = CreatePanel( 0, 0, 0, 0, window, PANEL_ACTIVE)

RegisterTrayIcon(panel, "this is a test", "tray_icon.ico")

Repeat
     WaitEvent()

     'Print CurrentEvent.ToString()

     Select EventID()

     Case EVENT_MOUSEMOVE
          Print "Mouse moved!"

     Case EVENT_MOUSEDOWN
          Select EventData()

          Case MOUSE_LEFT
               Print "Left mousebutton down"

          Case MOUSE_RIGHT
               Print "Right mousebutton down"
          EndSelect

     Case EVENT_MOUSEUP     'There is no doubleclick event so we are using mouse_up
          Print "Double clicked!"
          ShowGadget(window)

     Case EVENT_WINDOWCLOSE
          HideGadget(window)
     EndSelect

Forever

' unregister tray icon
RemoveTrayIcon()

End

-Henri
- Got 01100011 problems, but the bit ain't 00000001

fielder

The mouse "interactions" are correctly identified on the main window (enlarging the panel to 50x50 for example) but not on the tray bar (again: can be W11?)


' Register tray icon
Function RegisterTrayIcon(window:TGadget, toolTip:String, iconFile:String)

    nid = New TNotifyIconData
    nid.Size = SizeOf(TNotifyIconData)
    nid.hwnd = QueryGadget(window, QUERY_HWND)
    nid.id = 0
    nid.CallbackMessage = WM_MOUSEMOVE
    nid.Icon = LoadIcon( iconFile)
    nid.flags = NIF_MESSAGE | NIF_TIP | NIF_ICON
    SetNotifyIconDataTip( nid, toolTip )
    Shell_NotifyIcon( NIM_ADD, nid)

    SetWindowLongW(nid.hwnd, GWL_WNDPROC, Int(Byte Ptr(SysTrayWndProc)) )  '<------------------------------

End Function 
seems not working this "hook"

Henri

Does the icon show for you ? I just tried it in Windows 11 and icon was hidden, but when I enabled it in system tray settings, I was able to see the icon and messages.

-Henri
- Got 01100011 problems, but the bit ain't 00000001

fielder

Quote from: Henri on February 26, 2025, 10:33:33Does the icon show for you ? I just tried it in Windows 11 and icon was hidden, but when I enabled it in system tray settings, I was able to see the icon and messages.

-Henri
the icon is displayed correctly.
but double clicking (or clicking)  on the ICON is not doing anything.

the only thing i need is to open the app or start a process when the icon is clicked.
i don't need any visible gui app, only the trayicon .. that can launch a software (like updaters, settings, old quickstart of openoffice)