Active window title of other applications


#1

I’d like to get the window title of whatever application is currently active or focused, even if it’s not my electron app. I am trying to replicate some of the functionality of RescueTime to track where the users time is spent.

Does anyone know of an API in electron or node that would help with this?


#2

Did you find anything? I’m looking for the same to do exactly what you’re doing.


#3

You’ll probably need to write a native Node module to do this sort of thing, on Windows you can hook into window activation events with the CBTProc callback.


#4

It’s been ages since I’ve done Windows (C#) coding. Have you seen a good tutorial on writing a cross-platform native Node module? I want to cover Windows + Mac.

Do you suggest using the projects below to make it accessible with Win + Mac?


https://github.com/TooTallNate/NodObjC (This is with Obj-C, I’m not sure if there’s a Swift based alternative)


#5

This may interest you. but mac only


#6

On Windows this is a pretty low-level API, so you’d write it in C++ not C#. The hook callback has to be in a separate native library, so you can’t write that part using node-ffi.


#7

Thank you @jeancroy. I did finally workout that Apple Script was the answer—which is how active-window does it. Not sure about Windows, perhaps someone has written a c++ app we can include. Right now, I’m only really concerned with OS X so the active-window package will do nicely.


#8

I know it’s a bit late but you do not need to use C++ for something like this, C# or even VB will do just as nicely:

'Public Declare Function GetActiveWindow Lib "user32" () As System.IntPtr
Public Declare Function GetForegroundWindow Lib "user32" () As System.IntPtr
Public Declare Auto Function GetWindowText Lib "user32" _
        (ByVal hWnd As System.IntPtr, _
        ByVal lpString As System.Text.StringBuilder, _
        ByVal cch As Integer) As Integer
Dim makel As String

Function GetCaption() As String
    ' Create a buffer of 256 characters
    Dim Caption As New System.Text.StringBuilder(256)
    Dim hWnd As IntPtr = GetForegroundWindow()
    GetWindowText(hWnd, Caption, Caption.Capacity)
    Return Caption.ToString()
End Function

Wrap that in a module of a console application and simply use Console.WriteLine(GetCaption()). When calling the console application from nodejs you can get the stdout of the application and manipulate it how you please :slight_smile:

Now if there is a way to use the user32.dll directly in node.js this would be an even better solution!

Note:

Some beautiful person wrapped these Win32 APIs in a VB Class.


#9

Looking further into this for my own purposes, I believe you can do this entirely within NodeJS by now…

npm instal ffi

UNTESTED:

var FFI = require('ffi');
function TEXT(text){
   return new Buffer(text, 'ucs2').toString('binary');
}

var user32 = new FFI.Library('user32', {
   'GetForegroundWindow': [
      'int32', []
   ],
   'GetWindowText': [
      'int32', ['int32','string','int32']
   ]
});

function getCaption(){
    //Create a buffer of 256 characters
    var caption = ""
    var hWnd = user32. GetForegroundWindow()
    getWindowText(hWnd,var caption,256)
    return TEXT(caption)
}

#10

active-window is quite broken.

But there’s active-win now :tada:. With Mac, Linux and Windows support, better behaviour, made to work with Electron, etc.


About active-window

I needed this, but the package active-window (linked above) from NPM and GitHub is quite broken and unmantained (there are several unmerged PRs, including some mine).

Some of the problems in the official repo / npm package:

  • It is broken for Mac
  • After fixing it for Mac, it needs to ask for “assistive technologies permissions”
  • It is broken for Linux
  • It always creates a subprocess that keeps running and there’s no way to close if after finishing
  • That subprocess is a shell process in each platform (PowerShell in Windows), so it’s easy for “Antiviruses” to detect it as “suspicious”
  • It doesn’t work with Asar packages, so you have to compile the electron app with raw directories for it to work

About active-win

But there is active-win by sindresorhus (if you are building something with Electron you probably already use one of his popular packages).

  • It supports Mac
  • I recently fixed Linux support
  • I just added Windows support (32 and 64 bits).
  • It works out of the box if you use it with tools like the awesome electron-builder (it doesn’t break asar packaging, electron-builder can compile it automatically for each platform, etc)
  • It doesn’t leave processes dangling
  • It uses the internal OS APIs as much as possible (no “suspicious” shell scripts)

#11

This saved my days.


#13

@sancarn Yeah, using node-ffi works, though I had to make some changes:

import ffi from "ffi";
import ref from "ref";

declare var Buffer;

var user32 = new ffi.Library("user32", {
   "GetForegroundWindow": ["int32", []],
   "GetWindowTextA": ["int32", ["int32", "string", "int32"]],
});

export function GetForegroundWindowText() {
	var buffer = new Buffer(256);
	buffer.type = ref.types.CString;
    var handle = user32.GetForegroundWindow();
    let length = user32.GetWindowTextA(handle, buffer, 256);
    return buffer.toString().substr(0, length);
}

I like this approach the best because it’s the most direct access from JavaScript to the native APIs. If you were going for multiple platforms, a package like active-win above might be best, but for just a few small accesses, using node-ffi to hook into native functions seems the cleanest to me. (and more future proof, since there’s no library you need to understand in order to update it if it has an issue)