Search code examples
windowsluanotifications

How can I send a Windows Notification in Lua?


I am trying to send a Windows notification through Lua, I found a few methods to do it, but in Python, I couldn't find anything for Lua. Is there any method I can do this? And if it is, could someone tell me how? Thanks!

I tried searching for answers, but I couldn't find anything about it.


Solution

  • Lua does not have such standard function, you need to use some external library.
    For example, you can use FFI library to invoke Win32API functions directly.
    The following script uses LuaJIT FFI:

    local ffi = require'ffi'
    local shell32 = ffi.load'shell32'
    
    ffi.cdef[[
    typedef int BOOL;
    typedef unsigned int DWORD;
    typedef unsigned int UINT;
    typedef intptr_t HANDLE;
    typedef intptr_t HWND;
    typedef intptr_t HICON;
    typedef intptr_t HINSTANCE;
    typedef struct {int Data[4];} GUID;
    
    typedef struct {
      DWORD cbSize;
      HWND  hWnd;
      UINT  uID;
      UINT  uFlags;
      UINT  uCallbackMessage;
      HICON hIcon;
      char  szTip[128];
      DWORD dwState;
      DWORD dwStateMask;
      char  szInfo[256];
      union {
        UINT uTimeout;
        UINT uVersion;
      };
      char  szInfoTitle[64];
      DWORD dwInfoFlags;
      GUID  guidItem;
      HICON hBalloonIcon;
    } NOTIFYICONDATAA;
    
    BOOL Shell_NotifyIconA(
      int dwMessage,
      NOTIFYICONDATAA * lpData
    );
    HICON LoadIconA(
      HINSTANCE hInstance,
      intptr_t IconCode
    );
    BOOL DestroyIcon(
      HICON hIcon
    );
    HWND GetConsoleWindow();
    ]]
    
    
    
    -- icon codes:
    --    32512   Your application's icon
    --    32513   Error       ("X" inside red circle)
    --    32514   Question    ("?" inside blue circle)
    --    32515   Warning     ("!" inside yellow triangle)
    --    32516   Information ("i" inside blue circle)
    --    32518   Security Shield
    
    local tray_icon_code    = 32516   -- Information
    local balloon_icon_code = 32518   -- Shield
    
    -- create tray icon
    local tray_icon_handle = ffi.C.LoadIconA(0, tray_icon_code)
    local balloon_icon_handle = ffi.C.LoadIconA(0, balloon_icon_code)
    local notify_icon_data = ffi.new"NOTIFYICONDATAA"
    notify_icon_data.cbSize = ffi.sizeof(notify_icon_data)
    notify_icon_data.hWnd = ffi.C.GetConsoleWindow()  -- HWND of your application window
    notify_icon_data.uFlags = 1 + 2  -- NIF_MESSAGE | NIF_ICON
    notify_icon_data.hIcon = tray_icon_handle
    notify_icon_data.uVersion = 4
    notify_icon_data.hBalloonIcon = balloon_icon_handle
    shell32.Shell_NotifyIconA(0, notify_icon_data)  -- NIM_ADD
    shell32.Shell_NotifyIconA(4, notify_icon_data)  -- NIM_SETVERSION
    print("Tray icon added.  Press Enter to continue..."); io.read()
    
    -- show notifications
    local function copy_string(dest_array_ptr, str)
        ffi.copy(dest_array_ptr, (str or ""):sub(1, ffi.sizeof(dest_array_ptr) - 1))
    end
    
    local function show_notification(text, title)
        notify_icon_data.uFlags = 1 + 2 + 16   -- NIF_MESSAGE | NIF_ICON | NIF_INFO
        notify_icon_data.dwInfoFlags = 4 + 32  -- NIIF_USER | NIIF_LARGE_ICON
        copy_string(notify_icon_data.szInfoTitle, title)
        copy_string(notify_icon_data.szInfo, text)
        shell32.Shell_NotifyIconA(1, notify_icon_data) -- NIM_MODIFY
    end
    
    show_notification("some text", "some title")
    print("Notification displayed.  Press Enter to continue..."); io.read()
    
    show_notification("first line\nsecond line", "another title")
    print("Another notification displayed.  Press Enter to continue..."); io.read()
    
    -- notification requests generated by show_notification() are queued,
    -- each notification is kept on the screen for several seconds before next one is displayed
    -- to immediately remove the notification - remove the tray icon
    
    -- remove tray icon
    shell32.Shell_NotifyIconA(2, notify_icon_data)  -- NIM_DELETE
    ffi.C.DestroyIcon(balloon_icon_handle)
    ffi.C.DestroyIcon(tray_icon_handle)
    print("Tray icon removed.  Press Enter to exit..."); io.read()