go – Add icon tray on Windows using native Golang API-ThrowExceptions

Exception or error:

I’m developing a daemon with no UI apart from a simple icon in the Windows systray.

I would like to have no dependencies on any other package(s), so I’m trying to use the syscall package and implement the necessary call(s) by myself.

Documentation

Libraries

Implementation

Structures

Built with xilp/systray documentation.

type HANDLE uintptr

type HICON HANDLE
    type HWND HANDLE

type GUID struct {
    Data1 uint32
    Data2 uint16
    Data3 uint16
    Data4 [8]byte
}

type NOTIFYICONDATA struct {
    CbSize           uint32
    HWnd             HWND
    UID              uint32
    UFlags           uint32
    UCallbackMessage uint32
    HIcon            HICON
    SzTip            [128]uint16
    DwState          uint32
    DwStateMask      uint32
    SzInfo           [256]uint16
    UVersion         uint32
    SzInfoTitle      [64]uint16
    DwInfoFlags      uint32
    GuidItem         GUID
}

Variables

const (
    NIM_ADD = 0x00000000
    NIM_MODIFY = 0x00000001
    NIM_DELETE = 0x00000002
    NIM_SETVERSION = 0x00000004

    NIF_MESSAGE = 0x00000001
    NIF_ICON = 0x00000002
    NIF_TIP = 0x00000004
    NIF_STATE = 0x00000008

    NIF_HIDDEN = 0x00000001
)

Source

package main

import (
    "log"
    "syscall"
    "unsafe"
)

func main() {
    shell32 := syscall.MustLoadDLL("shell32.dll")
    Shell_NotifyIcon := shell32.MustFindProc("Shell_NotifyIconW")

    iconData := NOTIFYICONDATA{
        HWnd: 0,
        UFlags: NIF_MESSAGE | NIF_STATE,
        DwState: NIF_HIDDEN,
        DwStateMask: NIS_HIDDEN,
    }
    iconData.CbSize = uint32(unsafe.Sizeof(iconData))

    ret, _, _ := Shell_NotifyIcon.Call(
        NIM_ADD,
        uintptr(unsafe.Pointer(&iconData)),
    )

    if ret == 0 {
        log.Println("Failed")
        return
    }

    // Do anything, like open a HTTP server to keep the program running
    http.ListenAndServe(":8080", nil)
}

Details

  • I have no idea what information to give in HWnd, but without it, the executable crashes.
  • UFlags, DwState and DwStateMask have values that I have found in different projects.

I know that it is possible; the Golang WIKI gives an implementation to call a message box.

How to solve:

Fields of NOTIFYICONDATA

hWnd

hWnd field of NOTIFYICONDATA holds a window handle that is associated with notifyicon itself, as mentioned in MSDN:

hWnd

A handle to the window that receives notifications associated with an icon in the notification area.

I found that it’s necessary to associate a window handle, even if the window is not visible.

uFlags tells which fields of NOTIFYICONDATA are valid in single command.
As you see there are lots of fields in NOTIFYICONDATA, and if you are going to change just the icon of the notifyicon, you can leave other fields unchanged and set only hIcon field then pass the whole NOTIFYICONDATA to Shell_NotifyIcon.
If you want to change both icon and message, just set it to NIF_MESSAGE|NIF_ICON.

dwState

dwState can be used to control icon’s visibility. If you specify NIF_STATE for uFlags, and NIS_HIDDEN for dwState and dwStateMask, it’ll make notifyicon hidden.

dwStateMask

And in most case, just set dwStateMask as same as dwState. It just tells which bit of dwState is valid for the command:

The possible values are the same as those for dwState.

Example

You can find full example I’ve wrote at here: https://github.com/hallazzang/go-windows-programming/tree/master/example/gui/notifyicon

Leave a Reply

Your email address will not be published. Required fields are marked *