[solved] How to detect Calculator on Windows 10

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
mviens
Posts: 43
Joined: 08 Jan 2014, 19:04

[solved] How to detect Calculator on Windows 10

09 Feb 2016, 14:57

Microsoft has changed Calculator for Windows 10. For all previous version (including the server versions), this used to work just fine:

Code: Select all

WinActive("ahk_class CalcFrame")
I realized it was no longer working on Windows 10, so I used Window Spy and discovered the following:

Code: Select all

ahk_class ApplicationFrameWindow
ahk_exe ApplicationFrameHost.exe
This is not reliable to determine if the window is indeed Calculator because other application also use ApplicationFrameHost.exe. Nor is it acceptable to search for "Calculator" because of various languages. Does anyone have ideas on how to detect if a window is actually Calculator using Windows 10?
Last edited by mviens on 20 Mar 2016, 00:27, edited 1 time in total.
Mike V.
User avatar
Exaskryz
Posts: 2882
Joined: 17 Oct 2015, 20:28

Re: How to detect Calculator on Windows 10

09 Feb 2016, 16:33

Maybe you can poll the names of the Controls with WinGet? Though if it varies from language to language, I don't know if the names for values would be consistent. And you'll get a different number of controls based on using Standard vs Scientific/Programming/Whatever the fourth one is.

If AHK is the one responsible for launching the Calculator, you might be able to use the PID output that can be collected by the Run command.
just me
Posts: 9407
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: How to detect Calculator on Windows 10

10 Feb 2016, 05:40

As long as the Calculator app is running Process, Exist, Calculator.exe will retrieve a PID. But it will need more to determine, whether it is active.
User avatar
mviens
Posts: 43
Joined: 08 Jan 2014, 19:04

Re: How to detect Calculator on Windows 10

12 Feb 2016, 16:43

Hi "just me",

Thanks for the idea. I have it nearly working, but it doesn't seem to be able to detect when Windows 10 Calculator is actually active. It works for the following:
  • Before Windows 10, calculator is not running
  • Before Windows 10, calculator is running but not active
  • Before Windows 10, calculator is active
  • Windows 10, calculator is not running
  • Windows 10, calculator is running but not active
This is the one for which the script does not work:
  • Windows 10, calculator is active

Code: Select all

#b::
    isCalculatorActive()
    return

isCalculatorActive() {
    Process, Exist, Calc.exe
    cpid := ErrorLevel
    calcFound := false
    if (cpid > 0) {
        calcFound := true
        if (WinActive("ahk_class CalcFrame")) {
            message("Calculator (before Win10) is active...")
        }
        else {
            message("Calculator (before Win10) is running but not active...")
        }
    }
    else {
        Process, Exist, Calculator.exe
        cpid := ErrorLevel
        if (cpid > 0) {
            calcFound := true
            if (WinActive("ahk_pid " cpid)) {
                message("Calculator (Win10+) is active...")
            }
            else {
                message("Calculator (Win10+) is running but not active...")
            }
        }
    }
    if (!calcFound) {
        message("Calculator is not running or active...")
    }
}
Mike V.
User avatar
mviens
Posts: 43
Joined: 08 Jan 2014, 19:04

Re: How to detect Calculator on Windows 10

22 Feb 2016, 15:16

Still working on this issue...

I found a post that seems to solve this, but the answer is written using C. I would like to ask for assistance with converting that into usable AHK code but someone who may see this. Here is the link: http://stackoverflow.com/questions/3236 ... ndows-8-10

Code: Select all

#b::
    isCalculatorActive()
    return
 
isCalculatorActive() {
    Process, Exist, Calc.exe
    calcPid := ErrorLevel
    calcFound := false
    if (calcPid > 0) {
        calcFound := true
        if (WinActive("ahk_class CalcFrame")) {
            message("Calculator (before Win10) is active...")
        }
        else {
            message("Calculator (before Win10) is running but not active...")
        }
    }
    else {
        Process, Exist, Calculator.exe
        calcPid := ErrorLevel
        if (calcPid > 0) {
            calcFound := true
            ; this is returning the PID for ApplicationFrameHost.exe,
            ; but I am expecting the PID for Calculator.exe
            ; see - http://stackoverflow.com/questions/32360149/name-of-process-for-active-window-in-windows-8-10
            WinGet, curPid, pid, A
            message("calcPid: " . calcPid . "`ncurPid: " . curPid)
            if (WinActive("ahk_pid " . calcPid)) {
                message("Calculator (Win10+) is active...")
            }
            else {
                message("Calculator (Win10+) is running but not active...")
            }
        }
    }
    if (!calcFound) {
        message("Calculator is not running or active...")
    }
}
 
message(msg) {
    MsgBox % msg
}
Mike V.
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

23 Feb 2016, 07:26

EDIT: Working code at the bottom of post

Code: Select all

MsgBox % getActiveProcess()

getActiveProcess() {
	handle := DllCall("GetForegroundWindow", "Ptr")
	DllCall("GetWindowThreadProcessId", "Int", handle, "int*", pid)
	WinGet, name, ProcessName, ahk_pid %pid%
	return name
}
EDIT: Actually, this returns ApplicationFrameHost.exe for calculator, sorry, guess I should read the post you linked more carefully...

EDIT:I've done a little more work on it, but I'm still having a problem. Here's what I have right now:

Code: Select all

#s::MsgBox % getActiveProcess()
 
getActiveProcess() {
	handle := DllCall("GetForegroundWindow", "Ptr")
	DllCall("GetWindowThreadProcessId", "Int", handle, "int*", pid)
	global true_pid := pid
	callback := RegisterCallback("enumChildCallback")
	DllCall("EnumChildWindows", "Int", handle, "ptr", callback, "int", pid)
	WinGet, name, ProcessName, ahk_pid %true_pid%
	return name
}

enumChildCallback(hwnd, pid) {
	DllCall("GetWindowThreadProcessId", "Int", hwnd, "int*", child_pid)
	msgbox % child_pid " " pid
	if (child_pid != pid)
		global true_pid := child_pid
	return 1
}
This code seems to work for the most part, except that enumChildCallback only seems to be called for the first child window, where I believe it should be called for each child window. I believe this is the main issue with this script right now.

EDIT: Fixed that issue, just had to return 1 from the callback function. Now the only issue is that WinGet doesn't seem to get a process name from the "true" PID, guess WinApi functions are necessary for that too!

EDIT: And it's sorted!! Got it fully working:

Code: Select all

DetectHiddenWindows, on

;#s::MsgBox % getActiveProcess() ; one example of use
 
;"full" example use
#s::
IfWinExist, ahk_exe calculator.exe
	if (getActiveProcess() = "calculator.exe")
		MsgBox Calc active
	else
		MsgBox Calc open, not active
else
	MsgBox Calc not open
return
 
getActiveProcess() {
	handle := DllCall("GetForegroundWindow", "Ptr")
	DllCall("GetWindowThreadProcessId", "Int", handle, "int*", pid)
	global true_pid := pid
	callback := RegisterCallback("enumChildCallback", "Fast")
	DllCall("EnumChildWindows", "Int", handle, "ptr", callback, "int", pid)
	handle := DllCall("OpenProcess", "Int", 0x0400, "Int", 0, "Int", true_pid)
	length := 259 ;max path length in windows
	VarSetCapacity(name, length)
	DllCall("QueryFullProcessImageName", "Int", handle, "Int", 0, "Ptr", &name, "int*", length)
	SplitPath, name, pname
	return pname
}
 
enumChildCallback(hwnd, pid) {
	DllCall("GetWindowThreadProcessId", "Int", hwnd, "int*", child_pid)
	if (child_pid != pid)
		global true_pid := child_pid
	return 1
}
Returns Calculator.exe for me on Windows 10, when calculator is active! (If you want the full path rather than just the process name, you can take out the SplitPath command at the end of the function and change the return to return name instead of pname). Should work with any program, whether the new Windows store style apps or traditional programs.

EDIT: Created a thread for this function: https://autohotkey.com/boards/viewtopic.php?f=6&t=14300
Last edited by SifJar on 23 Feb 2016, 10:53, edited 2 times in total.
CodeKiller
Posts: 42
Joined: 02 Sep 2014, 04:14

Re: How to detect Calculator on Windows 10

23 Feb 2016, 10:36

I don't really understand this whole post...

Code: Select all

IfWinExist ahk_exe calculator.exe
Msgbox Ok
This always works...

In my task manager calculator is... calculator.exe...
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

23 Feb 2016, 10:40

CodeKiller wrote:I don't really understand this whole post...

Code: Select all

IfWinExist ahk_exe calculator.exe
Msgbox Ok
This always works...

In my task manager calculator is... calculator.exe...
Try detecting if it is active. (Assuming you're on Windows 10).

Actually, that code doesn't even work for me as-is on Windows 10. You need to add DetectHiddenWindows, on before those lines for that to even work.

(It still shows as calculator.exe in task manager, but AHK can't find a window for that process, because of the way UWP apps work on Windows 10 [and 8, I believe]. The window owned by that process is hidden and inactive, therefore can be detected if DetectHiddenWindows is on, but will never be detected as active)
CodeKiller
Posts: 42
Joined: 02 Sep 2014, 04:14

Re: How to detect Calculator on Windows 10

23 Feb 2016, 11:30

Ok sorry, miss the "activate" part in your problem and focus on "detect if the windows exist". :-)
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

23 Feb 2016, 11:46

CodeKiller wrote:Ok sorry, miss the "activate" part in your problem and focus on "detect if the windows exist". :-)
Even for that though, your code doesn't work ;) It needs DetectHiddenWindows, on.
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to detect Calculator on Windows 10

24 Feb 2016, 03:11

Does this do the job?

Code: Select all

WinGetActiveProcessName() {
    WinGet name, ProcessName, A
    if (name = "ApplicationFrameHost.exe") {
        ControlGet hwnd, Hwnd,, Windows.UI.Core.CoreWindow1, A
        if hwnd {
            WinGet name, ProcessName, ahk_id %hwnd%
        }
    }
    return name
}
CodeKiller
Posts: 42
Joined: 02 Sep 2014, 04:14

Re: How to detect Calculator on Windows 10

24 Feb 2016, 05:17

I knew an easy way should exist !

Found by the great lexikos ! :-D
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

24 Feb 2016, 06:44

Well, Lexikos' solution is definitely better than mine ;)

For anyone interested, I did a quick timing benchmark of my version, Lexikos' version, and also a version just me sent me via PM, which was similar to my version except using all built-in commands. Ran each function 5000 times, and divided the difference in A_TickCount before & after by 5000, these were the results(time in milliseconds)

Code: Select all

SifJar: 1.012600
just me: 2.156200
Lexkios: 0.793800
So Lexikos' is slightly quicker than mine, but mine is quicker than just me's version ;)

Note: The above is with Calculator. With Chrome my version and just me's are closer, and Lexikos' is even further ahead:

Code: Select all

SifJar: 0.615600
just me: 0.668800
Lexkios: 0.106200
Benchmark script for anyone interested:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

#s::
start := A_TickCount
loop, 5000
	SifJar()
SifJarTime := (A_TickCount - start)/5000

start := A_TickCount
loop, 5000
	just_me()
justmeTime := (A_TickCount - start)/5000

start := A_TickCount
loop, 5000
	lexikos()
lexikosTime := (A_TickCount - start)/5000

times := "SifJar: " . SifJarTime . "`r`njust me: " . justmeTime . "`r`nLexkios: " . lexikosTime
clipboard := times
MsgBox % times

SifJar() {
	handle := DllCall("GetForegroundWindow", "Ptr")
	DllCall("GetWindowThreadProcessId", "Int", handle, "int*", pid)
	global true_pid := pid
	callback := RegisterCallback("enumChildCallback", "Fast")
	DllCall("EnumChildWindows", "Int", handle, "ptr", callback, "int", pid)
	handle := DllCall("OpenProcess", "Int", 0x0400, "Int", 0, "Int", true_pid)
	length := 259 ;max path length in windows
	VarSetCapacity(name, length)
	DllCall("QueryFullProcessImageName", "Int", handle, "Int", 0, "Ptr", &name, "int*", length)
	SplitPath, name, pname
	return pname
}
 
enumChildCallback(hwnd, pid) {
	DllCall("GetWindowThreadProcessId", "Int", hwnd, "int*", child_pid)
	if (child_pid != pid)
		global true_pid := child_pid
	return 1
}

Lexikos() {
    WinGet name, ProcessName, A
    if (name = "ApplicationFrameHost.exe") {
        ControlGet hwnd, Hwnd,, Windows.UI.Core.CoreWindow1, A
        if hwnd {
            WinGet name, ProcessName, ahk_id %hwnd%
        }
    }
    return name
}

just_me() {
   If !WinExist("A")
      Return ""
   WinGet, ActivePID, PID
   WinGet, CtrlList, ControlListHwnd
   Loop, Parse, CtrlList, `n
   {
      WinGet, ChildPID, PID, ahk_id %A_LoopField%
      If (ChildPID <> ActivePID) {
         ActivePID := ChildPID
         Break
      }
 
   }
   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, On
   WinGet, ProcName, ProcessName, ahk_pid %ActivePID%
   DetectHiddenWindows, %DHW%
   Return ProcName
}
(cue Lexikos pointing out that this script is wrong and those numbers aren't accurate :P )
just me
Posts: 9407
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: How to detect Calculator on Windows 10

24 Feb 2016, 10:50

Seemingly you run the benchmark on a relative slow machine. You should add SetBatchLines, -1 at least to run with maximum speed. Also, you should add a DllCall("CloseHandle", "Ptr", handle) to your function to free the process handle.

Interestingly, I can confirm that the DllCalls perform better than the AHK commands. But the advantage seems to diminish with the number of child controls which have to be iterated.
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

24 Feb 2016, 13:56

just me wrote:Seemingly you run the benchmark on a relative slow machine.
Yup, mid range laptop from ~5 years ago ;)
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to detect Calculator on Windows 10

24 Feb 2016, 17:16

The two scripts are not doing the same things. ControlGet has to retrieve the class name and track the number of controls of each class that it has encountered to find the one I specified, whereas SifJar's code just looks for a control with a different PID. In general, I suppose that some of the built-in commands do more work than strictly necessary to make them easier to use.
SifJar
Posts: 398
Joined: 11 Jan 2016, 17:52

Re: How to detect Calculator on Windows 10

24 Feb 2016, 19:20

lexikos wrote:The two scripts are not doing the same things. ControlGet has to retrieve the class name and track the number of controls of each class that it has encountered to find the one I specified, whereas SifJar's code just looks for a control with a different PID. In general, I suppose that some of the built-in commands do more work than strictly necessary to make them easier to use.
Which makes sense ;) A little bit more overhead in a command is worth making the commands easier than DllCalls IMO ;)
User avatar
mviens
Posts: 43
Joined: 08 Jan 2014, 19:04

Re: [solved] How to detect Calculator on Windows 10

20 Mar 2016, 00:28

Thank you lexicos. Your code was exactly what I needed to solve this.
Mike V.
User avatar
GollyJer
Posts: 60
Joined: 19 Sep 2015, 19:33
Contact:

Re: [solved] How to detect Calculator on Windows 10

27 Feb 2018, 13:24

Sorry to bump an old post but I was messing around with this today and came up with a simpler solution.
Maybe multiple criteria in WinExist is new since this post was created?

This works without the WinGetActiveProcessName function.

Code: Select all

DetectCalculator() {
	calc_id := WinExist("Calculator ahk_class ApplicationFrameWindow")
	if !(calc_id)	{
		MsgBox, % "Calculator is not running."
	}	else {
		if WinActive(ahk_id %calc_id%) {
			MsgBox, % "Calculator is active."
		} else {
			MsgBox, % "Calculator is running but not active."
		}
	}
}
My quest was to override the calculator key on my keyboard so only one copy of calculator is running at all times.

Code: Select all

;Launch_App2 is Calculator Key
Launch_App2:: ActivateCalculator()

ActivateCalculator() {
	if WinExist("Calculator ahk_class ApplicationFrameWindow") {
		WinActivate
	}	else {
		Run, calc
	}
}
EDIT!!!!
I just tried the old tried and true and it works also. Not sure why I made this so hard on myself! :lol:

Code: Select all

ActivateCalculator() {
	IfWinExist Calculator
		WinActivate
	else
		Run, calc
}
Hopefully this helps some future searcher. :)
User avatar
FanaticGuru
Posts: 1905
Joined: 30 Sep 2013, 22:25

Re: [solved] How to detect Calculator on Windows 10

01 Mar 2018, 14:04

I know this has been "solved" but here is a calculator script that I use many times each day.

Code: Select all

#numpad0::	; <-- Open/Activate/Minimize Windows Calculator
{
	if WinExist("Calculator ahk_class CalcFrame") or WinExist("Calculator ahk_class ApplicationFrameWindow")
		if WinActive()
			WinMinimize
		else
			WinActivate
	else
		Run calc.exe
	return
}
The WinExist is made to work with Windows 10 or earlier Windows.

The script just uses Win+NumPad0 to make the calculator popup or minimize in a toggle like way. Simple but very useful and intuitive.

This script eliminated my physical desktop calculator.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], inseption86 and 153 guests