Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting focus to a Windows application from Node-JS

I have a NodeJS application running on Windows that needs to display and switch the focus to a running Windows application when a user does a certain action. I have been using the node-ffi package to make windows API calls but have not been able to make it switch focus consistently. Here is the code I am using. It successfully gets the HWND of a running Calculator app, but then tries to switch focus to that HWND and it only works sometimes:

    var ffi = require('ffi');   
    var intPtr = ref.refType('long');
    var user32 = new ffi.Library('user32', {
        'FindWindowA': ['long', [ 'string', 'string']],
        'SetForegroundWindow': ['bool', ['long']],
        'BringWindowToTop': ['bool', ['long']],
    });

    var winToSetOnTop = user32.FindWindowA(null,"calculator")
    var res = user32.ShowWindow(winToSetOnTop, 9);
    res = user32.SetForegroundWindow(winToSetOnTop);
    res = user32.BringWindowToTop(winToSetOnTop);   

This combination of commands seems to work most consistently of the ones I have tried, but it does not work all the time. If the window I want to switch focus to is minimized it will always pop to the top. If the window is not minimized, but just behind another window, it will only be shown intermittently. I am not sure how to consistently to get a running windows application to always move to the top of the order, even if it is currently minimized.

like image 770
KeithTheBiped Avatar asked Sep 09 '16 23:09

KeithTheBiped


1 Answers

I've worked out the following solution which works well in all circumstances to bring a window to the top. First it will get the window handle to a running instance of the Calculator application, then it will bring it topmost and focus it.

var ffi = require('ffi-napi')

var user32 = new ffi.Library('user32', {
    'GetTopWindow': ['long', ['long']],
    'FindWindowA': ['long', ['string', 'string']],
    'SetActiveWindow': ['long', ['long']],
    'SetForegroundWindow': ['bool', ['long']],
    'BringWindowToTop': ['bool', ['long']],
    'ShowWindow': ['bool', ['long', 'int']],
    'SwitchToThisWindow': ['void', ['long', 'bool']],
    'GetForegroundWindow': ['long', []],
    'AttachThreadInput': ['bool', ['int', 'long', 'bool']],
    'GetWindowThreadProcessId': ['int', ['long', 'int']],
    'SetWindowPos': ['bool', ['long', 'long', 'int', 'int', 'int', 'int', 'uint']],
    'SetFocus': ['long', ['long']]
});

var kernel32 = new ffi.Library('Kernel32.dll', {
    'GetCurrentThreadId': ['int', []]
});

var winToSetOnTop = user32.FindWindowA(null, "calculator")
var foregroundHWnd = user32.GetForegroundWindow()
var currentThreadId = kernel32.GetCurrentThreadId()
var windowThreadProcessId = user32.GetWindowThreadProcessId(foregroundHWnd, null)
var showWindow = user32.ShowWindow(winToSetOnTop, 9)
var setWindowPos1 = user32.SetWindowPos(winToSetOnTop, -1, 0, 0, 0, 0, 3)
var setWindowPos2 = user32.SetWindowPos(winToSetOnTop, -2, 0, 0, 0, 0, 3)
var setForegroundWindow = user32.SetForegroundWindow(winToSetOnTop)
var attachThreadInput = user32.AttachThreadInput(windowThreadProcessId, currentThreadId, 0)
var setFocus = user32.SetFocus(winToSetOnTop)
var setActiveWindow = user32.SetActiveWindow(winToSetOnTop)
like image 139
KeithTheBiped Avatar answered Oct 15 '22 12:10

KeithTheBiped