Can AHK detect when Action Center notifications occur?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Can AHK detect when Action Center notifications occur?

18 May 2020, 14:55

Hello! I've never used AutoHotkey before but have been told it might be helpful for something I've been wanting to do. Is it possible to set up AHK to run a command whenever the Action Center gets a notification from a given app? Thanks!
BNOLI
Posts: 548
Joined: 23 Mar 2020, 03:55

Re: Can AHK detect when Action Center notifications occur?

19 May 2020, 00:50

AFAICS, no. The only thing I found about that - probably offering some details for a conversion - is this: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/notification-listener

Good luck :)

BTW, the majority of articles I've done some research on this topic have suggested to check out EventViewer instead.
Remember to use [code]CODE[/code]-tags for your multi-line scripts. Stay safe, stay inside, and remember washing your hands for 20 sec !
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

19 May 2020, 09:30

BNOLI wrote:
19 May 2020, 00:50
AFAICS, no. The only thing I found about that - probably offering some details for a conversion - is this: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/notification-listener

Good luck :)

BTW, the majority of articles I've done some research on this topic have suggested to check out EventViewer instead.
It looks like the link you gave applies when developing an app. To make sure my question isn't confusing, I'm asking about an already-created app (specifically, TextNow) which sends notifications to the Action Center. Is there a way I could create an event on my computer that can trigger whenever an already-developed app (as opposed to an app I'm programming) sends a notification to the Action Center? Thanks! :)
BNOLI
Posts: 548
Joined: 23 Mar 2020, 03:55

Re: Can AHK detect when Action Center notifications occur?

19 May 2020, 09:56

Yep, that's what the linked content is about, how to become the (listening) man in the middle. There are a few fantastic members at this forum who might be able to translate/convert that code into something that can be used the way you've requested - or coming up with a completely different way :lol:.
So, get your fingers crossed & good luck :)
Remember to use [code]CODE[/code]-tags for your multi-line scripts. Stay safe, stay inside, and remember washing your hands for 20 sec !
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

19 May 2020, 12:18

Kilk1, here is examples how to use Windows Runtime API in ahk:
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72797
https://www.autohotkey.com/boards/viewtopic.php?p=319141#p319141
I am sure that You can get notifications from action center in loop with UserNotificationListener.GetNotificationsAsync method and then parse them without any problem.
https://docs.microsoft.com/en-us/uwp/ap ... ationKinds_
But set event listener - it will be quite difficult.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

20 May 2020, 09:57

Tried to do it but there is memory leaking, therefore I empty working set.
Memory leaking may be because I not correct send enumerations
https://docs.microsoft.com/en-us/uwp/api/windows.ui.notifications.notificationkinds?view=winrt-18362
But despite of that code is working.
Code shows notification send by autohotkey.

Code: Select all

AppName := "Autohotkey"

setbatchlines -1
CreateClass("Windows.UI.Notifications.Management.UserNotificationListener", IUserNotificationListenerStatics := "{FF6123CF-4386-4AA3-B73D-B804E5B63B23}", UserNotificationListenerStatics)
DllCall(NumGet(NumGet(UserNotificationListenerStatics+0)+6*A_PtrSize), "ptr", UserNotificationListenerStatics, "ptr*", listener)   ; get_Current
DllCall(NumGet(NumGet(listener+0)+6*A_PtrSize), "ptr", listener, "int*", accessStatus)   ; RequestAccessAsync
WaitForAsync(accessStatus)
if (accessStatus != 1)
{
   msgbox AccessStatus Denied
   exitapp
}
loop
{
   DllCall(NumGet(NumGet(listener+0)+10*A_PtrSize), "ptr", listener, "int", 1, "ptr*", UserNotificationReadOnlyList)   ; GetNotificationsAsync
   WaitForAsync(UserNotificationReadOnlyList)
   DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+7*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int*", count)   ; count
   loop % count
   {
      DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+6*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int", A_Index-1, "ptr*", UserNotification)   ; get_Item
      DllCall(NumGet(NumGet(UserNotification+0)+8*A_PtrSize), "ptr", UserNotification, "uint*", id)   ; get_Id
      if InStr(idList, "|" id "|")
      {
         ObjRelease(UserNotification)
         Continue
      }
      idList .= "|" id "|"
      if !DllCall(NumGet(NumGet(UserNotification+0)+7*A_PtrSize), "ptr", UserNotification, "ptr*", AppInfo)   ; get_AppInfo
      {
         DllCall(NumGet(NumGet(AppInfo+0)+8*A_PtrSize), "ptr", AppInfo, "ptr*", AppDisplayInfo)   ; get_DisplayInfo
         DllCall(NumGet(NumGet(AppDisplayInfo+0)+6*A_PtrSize), "ptr", AppDisplayInfo, "ptr*", hText)   ; get_DisplayName
         buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
         text := StrGet(buffer, "UTF-16")
         DeleteHString(hText)
         ObjRelease(AppDisplayInfo)
         ObjRelease(AppInfo)
         if (text != AppName)
         {
            ObjRelease(UserNotification)
            Continue
         }
      }
      else
      {
         ObjRelease(UserNotification)
         Continue
      }
      DllCall(NumGet(NumGet(UserNotification+0)+6*A_PtrSize), "ptr", UserNotification, "ptr*", Notification)   ; get_Notification
      DllCall(NumGet(NumGet(Notification+0)+8*A_PtrSize), "ptr", Notification, "ptr*", NotificationVisual)   ; get_Visual
      DllCall(NumGet(NumGet(NotificationVisual+0)+8*A_PtrSize), "ptr", NotificationVisual, "ptr*", NotificationBindingList)   ; get_Bindings
      DllCall(NumGet(NumGet(NotificationBindingList+0)+7*A_PtrSize), "ptr", NotificationBindingList, "int*", count)   ; count
      loop % count
      {
         DllCall(NumGet(NumGet(NotificationBindingList+0)+6*A_PtrSize), "ptr", NotificationBindingList, "int", A_Index-1, "ptr*", NotificationBinding)   ; get_Item
         DllCall(NumGet(NumGet(NotificationBinding+0)+11*A_PtrSize), "ptr", NotificationBinding, "ptr*", AdaptiveNotificationTextReadOnlyList)   ; GetTextElements
         DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+7*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int*", count)   ; count
         loop % count
         {
            DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+6*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int", A_Index-1, "ptr*", AdaptiveNotificationText)   ; get_Item
            DllCall(NumGet(NumGet(AdaptiveNotificationText+0)+6*A_PtrSize), "ptr", AdaptiveNotificationText, "ptr*", hText)   ; get_Text
            buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
            if (A_Index = 1)
               text := StrGet(buffer, "UTF-16")
            else
               text .= "`n" StrGet(buffer, "UTF-16")
            DeleteHString(hText)
            ObjRelease(AdaptiveNotificationText)
         }
         ObjRelease(AdaptiveNotificationTextReadOnlyList)
         ObjRelease(NotificationBinding)
         msgbox % text
      }
      ObjRelease(NotificationBindingList)
      ObjRelease(NotificationVisual)
      ObjRelease(Notification)
      ObjRelease(UserNotification)
   }
   ObjRelease(UserNotificationReadOnlyList)
   DllCall("psapi.dll\EmptyWorkingSet", "ptr", -1)
   sleep 50
}


CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
   DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjRelease(Object)
   Object := ObjectResult
}
Notification send with this code:

Code: Select all

; Unlike TrayTip, this API does not require a tray icon:
#NoTrayIcon

; Toast notifications from desktop apps can only use local image files.
if !FileExist("sample.png")
    URLDownloadToFile https://autohotkey.com/boards/styles/simplicity/theme/images/announce_unread.png
        , % A_ScriptDir "\sample.png"
; The templates are described here:
;  http://msdn.com/library/windows/apps/windows.ui.notifications.toasttemplatetype.aspx
toast_template := "toastImageAndText02"
; Image path/URL must be absolute, not relative.
toast_image := A_ScriptDir "\sample.png"
; Text is an array because some templates have multiple text elements.
toast_text := ["Hello, world!", "This is the sub-text."]

; For Windows 10.0.16299 (and possibly earlier or later versions), the AppID
; must identify an app which has a shortcut on the Start screen, otherwise
; the notification won't display.  AppIDs for desktop apps seem to be the
; path of the executable, with system/known folders replaced with GUIDs.
; If this doesn't work, the Get-StartApps powershell command can be used to
; get a list of AppIDs on the system.
; This assumes AutoHotkey is installed in the default location:
toast_appid := (A_Is64bitOS ? "{6D809377-6AF0-444b-8957-A3773F02200E}"
                            : "{905e63b6-c1bf-494e-b29c-65b732d3d21a}")
    . "\AutoHotkey\AutoHotkey.exe"

; Only the Edge version of JsRT supports WinRT.
js := new JsRT.Edge
js.AddObject("yesno", Func("yesno"))
yesno(s) {

        return
}

; Enable use of WinRT.  "Windows.UI" or "Windows" would also work.
js.ProjectWinRTNamespace("Windows.UI.Notifications")
code =
(
    function toast(template, image, text, app) {
        // Alias for convenience.
        var N = Windows.UI.Notifications;
        // Get the template XML as an XmlDocument.
        var toastXml = N.ToastNotificationManager
            .getTemplateContent(N.ToastTemplateType[template]);
        // Insert our content.
        var i = 0;
        for (let el of toastXml.getElementsByTagName("text")) {
            if (typeof text == 'string') {
                el.innerText = text;
                break;
            }
            el.innerText = text[++i];
        }
        toastXml.getElementsByTagName("image")[0]
            .setAttribute("src", image);
        // Show the notification.
        var toastNotifier = N.ToastNotificationManager
            .createToastNotifier(app || "AutoHotkey");
        var notification = new N.ToastNotification(toastXml);
        toastNotifier.show(notification);
        // Unlike TrayTip, this API lets us hide the notification:
        if (yesno("Hide the notification?")) {
            toastNotifier.hide(notification);
        }
    }
)
try {
    ; Define the toast function.
    js.Exec(code)
    ; Show a toast notification.
    js.toast(toast_template, toast_image, toast_text, toast_appid)
}
catch ex {
    try errmsg := ex.stack
    if !errmsg
        errmsg := "Error: " ex.message
    MsgBox % errmsg
}
; Note: If the notification wasn't hidden, it will remain after we exit.
ExitApp


/*
 *  JsRT for AutoHotkey v1.1
 *
 *  Utilizes the JavaScript engine that comes with IE11.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class JsRT extends ActiveScript._base
{
    __New()
    {
        throw Exception("This class is abstract. Use JsRT.IE or JSRT.Edge instead.", -1)
    }
    
    class IE extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "jscript9", "ptr")
                throw Exception("Failed to load jscript9.dll", -1)
            if DllCall("jscript9\JsCreateRuntime", "int", 0, "int", -1
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("jscript9\JsCreateContext", "ptr", runtime, "ptr", 0, "ptr*", context)
            this._Initialize("jscript9", runtime, context)
        }
    }
    
    class Edge extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "chakra", "ptr")
                throw Exception("Failed to load chakra.dll", -1)
            if DllCall("chakra\JsCreateRuntime", "int", 0
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("chakra\JsCreateContext", "ptr", runtime, "ptr*", context)
            this._Initialize("chakra", runtime, context)
        }
        
        ProjectWinRTNamespace(namespace)
        {
            return DllCall("chakra\JsProjectWinRTNamespace", "wstr", namespace)
        }
    }
    
    _Initialize(dll, runtime, context)
    {
        this._dll := dll
        this._runtime := runtime
        this._context := context
        DllCall(dll "\JsSetCurrentContext", "ptr", context)
        DllCall(dll "\JsGetGlobalObject", "ptr*", globalObject)
        this._dsp := this._JsToVt(globalObject)
    }
    
    __Delete()
    {
        this._dsp := ""
        if dll := this._dll
        {
            DllCall(dll "\JsSetCurrentContext", "ptr", 0)
            DllCall(dll "\JsDisposeRuntime", "ptr", this._runtime)
        }
        DllCall("FreeLibrary", "ptr", this._hmod)
    }
    
    _JsToVt(valref)
    {
        VarSetCapacity(variant, 24, 0)
        DllCall(this._dll "\JsValueToVariant", "ptr", valref, "ptr", &variant)
        ref := ComObject(0x400C, &variant), val := ref[], ref[] := 0
        return val
    }
    
    _ToJs(val)
    {
        VarSetCapacity(variant, 24, 0)
        ref := ComObject(0x400C, &variant) ; VT_BYREF|VT_VARIANT
        ref[] := val
        DllCall(this._dll "\JsVariantToValue", "ptr", &variant, "ptr*", valref)
        ref[] := 0
        return valref
    }
    
    _JsEval(code)
    {
        e := DllCall(this._dll "\JsRunScript", "wstr", code, "uptr", 0, "wstr", "source.js"
            , "ptr*", result)
        if e
        {
            if DllCall(this._dll "\JsGetAndClearException", "ptr*", excp) = 0
                throw this._JsToVt(excp)
            throw Exception("JsRT error", -2, format("0x{:X}", e))
        }
        return result
    }
    
    Exec(code)
    {
        this._JsEval(code)
    }
    
    Eval(code)
    {
        return this._JsToVt(this._JsEval(code))
    }
    
    AddObject(name, obj, addMembers := false)
    {
        if addMembers
            throw Exception("AddMembers=true is not supported", -1)
        this._dsp[name] := obj
    }
}




/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }

    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}

class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}

_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}

_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}
Last edited by malcev on 27 May 2020, 15:51, edited 2 times in total.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

20 May 2020, 10:03

Thanks, @malcev! Since I never used AHK before, I could be confused. To make sure I understand, does this code trigger when a specific app of your choice sends an Action Center notification, or does it trigger whenever any app sends an Action Center notification? Thanks!
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

20 May 2020, 10:07

Code should trigger only appplication with display name "Autohotkey".

Code: Select all

AppName := "Autohotkey"
But some applications does not have AppInfo, You have to check it by Yourself.
PS I added sleep in loop.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

22 May 2020, 12:27

malcev wrote:
20 May 2020, 10:07
Code should trigger only appplication with display name "Autohotkey".

Code: Select all

AppName := "Autohotkey"
But some applications does not have AppInfo, You have to check it by Yourself.
PS I added sleep in loop.
Thanks for doing all this! To make sure I understand, if I 1) get AutoHotkey, 2) use the code you provided, and 3) change "Autohotkey" to the name of the app I want, then it should work? :)
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

22 May 2020, 16:02

If Your application has AppInfo then yes.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

22 May 2020, 16:07

malcev wrote:
22 May 2020, 16:02
If Your application has AppInfo then yes.
Thanks so much! I'll attempt it in the next few days. :D
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

25 May 2020, 14:03

@malcev Hello again! I copied and pasted the code to an AHK script, but it didn't work. I changed the AppName from "Autohotkey" to "TextNow - Unlimited Texts + Calls." Since that didn't work, I changed the AppName to just "TextNow" in case this would count as it's true name, but it still didn't work.

In case what I mean is confusing, I want AHK to work with an app known as TextNow. The Windows version that I use can be downloaded here: https://www.microsoft.com/en-us/p/textnow-unlimited-texts-calls/9n43spjlcxcv?activetab=pivot:overviewtab. This app can be opened in the background. Therefore, even when TextNow is closed, it can still send notifications to the Action Center.

What I want is for TextNow to open its app whenever it sends a notification to the Action Center. How can I do this? Thanks!
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

25 May 2020, 14:20

This code will show You appName if it exists.

Code: Select all

AppName := "Autohotkey"

setbatchlines -1
CreateClass("Windows.UI.Notifications.Management.UserNotificationListener", IUserNotificationListenerStatics := "{FF6123CF-4386-4AA3-B73D-B804E5B63B23}", UserNotificationListenerStatics)
DllCall(NumGet(NumGet(UserNotificationListenerStatics+0)+6*A_PtrSize), "ptr", UserNotificationListenerStatics, "ptr*", listener)   ; get_Current
DllCall(NumGet(NumGet(listener+0)+6*A_PtrSize), "ptr", listener, "int*", accessStatus)   ; RequestAccessAsync
WaitForAsync(accessStatus)
if (accessStatus != 1)
{
   msgbox AccessStatus Denied
   exitapp
}
loop
{
   DllCall(NumGet(NumGet(listener+0)+10*A_PtrSize), "ptr", listener, "int", 1, "ptr*", UserNotificationReadOnlyList)   ; GetNotificationsAsync
   WaitForAsync(UserNotificationReadOnlyList)
   DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+7*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int*", count)   ; count
   loop % count
   {
      DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+6*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int", A_Index-1, "ptr*", UserNotification)   ; get_Item
      DllCall(NumGet(NumGet(UserNotification+0)+8*A_PtrSize), "ptr", UserNotification, "uint*", id)   ; get_Id
      if InStr(idList, "|" id "|")
      {
         ObjRelease(UserNotification)
         Continue
      }
      idList .= "|" id "|"
      if !DllCall(NumGet(NumGet(UserNotification+0)+7*A_PtrSize), "ptr", UserNotification, "ptr*", AppInfo)   ; get_AppInfo
      {
         DllCall(NumGet(NumGet(AppInfo+0)+8*A_PtrSize), "ptr", AppInfo, "ptr*", AppDisplayInfo)   ; get_DisplayInfo
         DllCall(NumGet(NumGet(AppDisplayInfo+0)+6*A_PtrSize), "ptr", AppDisplayInfo, "ptr*", hText)   ; get_DisplayName
         buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
         text := StrGet(buffer, "UTF-16")
         DeleteHString(hText)
         ObjRelease(AppDisplayInfo)
         ObjRelease(AppInfo)
         msgbox % text
         if (text != AppName)
         {
            ObjRelease(UserNotification)
            Continue
         }
      }
      else
      {
         msgbox no AppInfo
         ObjRelease(UserNotification)
         Continue
      }
      DllCall(NumGet(NumGet(UserNotification+0)+6*A_PtrSize), "ptr", UserNotification, "ptr*", Notification)   ; get_Notification
      DllCall(NumGet(NumGet(Notification+0)+8*A_PtrSize), "ptr", Notification, "ptr*", NotificationVisual)   ; get_Visual
      DllCall(NumGet(NumGet(NotificationVisual+0)+8*A_PtrSize), "ptr", NotificationVisual, "ptr*", NotificationBindingList)   ; get_Bindings
      DllCall(NumGet(NumGet(NotificationBindingList+0)+7*A_PtrSize), "ptr", NotificationBindingList, "int*", count)   ; count
      loop % count
      {
         DllCall(NumGet(NumGet(NotificationBindingList+0)+6*A_PtrSize), "ptr", NotificationBindingList, "int", A_Index-1, "ptr*", NotificationBinding)   ; get_Item
         DllCall(NumGet(NumGet(NotificationBinding+0)+11*A_PtrSize), "ptr", NotificationBinding, "ptr*", AdaptiveNotificationTextReadOnlyList)   ; GetTextElements
         DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+7*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int*", count)   ; count
         loop % count
         {
            DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+6*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int", A_Index-1, "ptr*", AdaptiveNotificationText)   ; get_Item
            DllCall(NumGet(NumGet(AdaptiveNotificationText+0)+6*A_PtrSize), "ptr", AdaptiveNotificationText, "ptr*", hText)   ; get_Text
            buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
            if (A_Index = 1)
               text := StrGet(buffer, "UTF-16")
            else
               text .= "`n" StrGet(buffer, "UTF-16")
            DeleteHString(hText)
            ObjRelease(AdaptiveNotificationText)
         }
         ObjRelease(AdaptiveNotificationTextReadOnlyList)
         ObjRelease(NotificationBinding)
         msgbox % text
      }
      ObjRelease(NotificationBindingList)
      ObjRelease(NotificationVisual)
      ObjRelease(Notification)
      ObjRelease(UserNotification)
   }
   ObjRelease(UserNotificationReadOnlyList)
   DllCall("psapi.dll\EmptyWorkingSet", "ptr", -1)
   sleep 50
}


CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
   DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjRelease(Object)
   Object := ObjectResult
}
Last edited by malcev on 27 May 2020, 15:53, edited 1 time in total.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

25 May 2020, 14:39

malcev wrote:
25 May 2020, 14:20
This code will show You appName if it exists.

Code: Select all

AppName := "Autohotkey"

setbatchlines -1
CreateClass("Windows.UI.Notifications.Management.UserNotificationListener", IUserNotificationListenerStatics := "{FF6123CF-4386-4AA3-B73D-B804E5B63B23}", UserNotificationListenerStatics)
DllCall(NumGet(NumGet(UserNotificationListenerStatics+0)+6*A_PtrSize), "ptr", UserNotificationListenerStatics, "ptr*", listener)   ; get_Current
DllCall(NumGet(NumGet(listener+0)+6*A_PtrSize), "ptr", listener, "int*", accessStatus)   ; RequestAccessAsync
WaitForAsync(accessStatus)
if (accessStatus != 1)
{
   msgbox AccessStatus Denied
   exitapp
}
loop
{
   DllCall(NumGet(NumGet(listener+0)+10*A_PtrSize), "ptr", listener, "int", 1, "ptr*", UserNotificationReadOnlyList)   ; GetNotificationsAsync
   WaitForAsync(UserNotificationReadOnlyList)
   DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+7*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int*", count)   ; count
   loop % count
   {
      DllCall(NumGet(NumGet(UserNotificationReadOnlyList+0)+6*A_PtrSize), "ptr", UserNotificationReadOnlyList, "int", A_Index-1, "ptr*", UserNotification)   ; get_Item
      DllCall(NumGet(NumGet(UserNotification+0)+8*A_PtrSize), "ptr", UserNotification, "uint*", id)   ; get_Id
      if InStr(idList, "|" id "|")
      {
         ObjRelease(UserNotification)
         Continue
      }
      idList .= "|" id "|"
      if !DllCall(NumGet(NumGet(UserNotification+0)+7*A_PtrSize), "ptr", UserNotification, "ptr*", AppInfo)   ; get_AppInfo
      {
         DllCall(NumGet(NumGet(AppInfo+0)+8*A_PtrSize), "ptr", AppInfo, "ptr*", AppDisplayInfo)   ; get_DisplayInfo
         DllCall(NumGet(NumGet(AppDisplayInfo+0)+6*A_PtrSize), "ptr", AppDisplayInfo, "ptr*", hText)   ; get_DisplayName
         buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
         text := StrGet(buffer, "UTF-16")
         DeleteHString(hText)
         ObjRelease(AppDisplayInfo)
         ObjRelease(AppInfo)
         msgbox % text
         if (text != AppName)
            Continue
      }
      else
      {
         msgbox no AppInfo
         Continue
      }
      DllCall(NumGet(NumGet(UserNotification+0)+6*A_PtrSize), "ptr", UserNotification, "ptr*", Notification)   ; get_Notification
      DllCall(NumGet(NumGet(Notification+0)+8*A_PtrSize), "ptr", Notification, "ptr*", NotificationVisual)   ; get_Visual
      DllCall(NumGet(NumGet(NotificationVisual+0)+8*A_PtrSize), "ptr", NotificationVisual, "ptr*", NotificationBindingList)   ; get_Bindings
      DllCall(NumGet(NumGet(NotificationBindingList+0)+7*A_PtrSize), "ptr", NotificationBindingList, "int*", count)   ; count
      loop % count
      {
         DllCall(NumGet(NumGet(NotificationBindingList+0)+6*A_PtrSize), "ptr", NotificationBindingList, "int", A_Index-1, "ptr*", NotificationBinding)   ; get_Item
         DllCall(NumGet(NumGet(NotificationBinding+0)+11*A_PtrSize), "ptr", NotificationBinding, "ptr*", AdaptiveNotificationTextReadOnlyList)   ; GetTextElements
         DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+7*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int*", count)   ; count
         loop % count
         {
            DllCall(NumGet(NumGet(AdaptiveNotificationTextReadOnlyList+0)+6*A_PtrSize), "ptr", AdaptiveNotificationTextReadOnlyList, "int", A_Index-1, "ptr*", AdaptiveNotificationText)   ; get_Item
            DllCall(NumGet(NumGet(AdaptiveNotificationText+0)+6*A_PtrSize), "ptr", AdaptiveNotificationText, "ptr*", hText)   ; get_Text
            buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
            if (A_Index = 1)
               text := StrGet(buffer, "UTF-16")
            else
               text .= "`n" StrGet(buffer, "UTF-16")
            DeleteHString(hText)
            ObjRelease(AdaptiveNotificationText)
         }
         ObjRelease(AdaptiveNotificationTextReadOnlyList)
         ObjRelease(NotificationBinding)
         msgbox % text
      }
      ObjRelease(NotificationBindingList)
      ObjRelease(NotificationVisual)
      ObjRelease(Notification)
      ObjRelease(UserNotification)
   }
   ObjRelease(UserNotificationReadOnlyList)
   DllCall("psapi.dll\EmptyWorkingSet", "ptr", -1)
   sleep 50
}


CreateClass(string, interface, ByRef Class)
{
   CreateHString(string, hString)
   VarSetCapacity(GUID, 16)
   DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
   result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
   DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjRelease(Object)
   Object := ObjectResult
}
When you say, "This code will show You appName if it exists," do you mean it will show me what TextNow's name is? That's not necessarily what I'm wanting to do. What I'm wanting is something that means this:

If TextNow (which is in the background) sends a notification to the Action Center, then open Textnow.

Is there code that accomplishes this goal? If so, thanks! :)
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

25 May 2020, 14:56

I think You dont understand how this script works.
Script sends requests in loop for all notifications that are in Action Center panel.
Therefore to detect that current notification is from Your application You have to put its name instead of "Autohotkey".
To detect its name use the last script.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

26 May 2020, 16:34

malcev wrote:
25 May 2020, 14:56
I think You dont understand how this script works.
Script sends requests in loop for all notifications that are in Action Center panel.
Therefore to detect that current notification is from Your application You have to put its name instead of "Autohotkey".
To detect its name use the last script.
Okay, so the last script will give me the name, and the script before is intended for what I'm hoping for. I'll give it a try now. :)
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

26 May 2020, 16:44

Okay, I ran the most recently posted script. Messages appeared saying "Outlook," "SupportAssist," and "Skype." While I do have these apps, I don't know why these three were mentioned but not other apps. What do I need to do from here? Thanks!
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Can AHK detect when Action Center notifications occur?

27 May 2020, 02:42

You have to remove all notifications from action center.
Then send notification from Your application.
Then run script and get its name.
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

27 May 2020, 10:27

malcev wrote:
27 May 2020, 02:42
You have to remove all notifications from action center.
Then send notification from Your application.
Then run script and get its name.
Aha, it worked! The app's name is "TextNow - Unlimited Text + Calls".
Kilk1
Posts: 19
Joined: 18 May 2020, 14:45

Re: Can AHK detect when Action Center notifications occur?

27 May 2020, 10:48

And now I put the app's name into the original script, and now it works! Whenever "TextNow - Unlimited Text + Calls" sends a notification to the Action Center, the same notification appears as a popup! I've been wanting something like this for a long time, and now I do. Thank you! :dance:

I have one more question: Is there code that could open "TextNow - Unlimited Text + Calls" whenever a notification appears, instead of making a popup? Again, thank you!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: OrangeCat and 189 guests