2

When running a Node.js app in a console, is there a way to bring the console window to the front programmatically, from within the app itself?

I am primarily interested in doing so in Windows, using the latest Node.js (v13 atm)

And if there is a package that can do so, that would be good enough.

Scenario

Running multiple Node.js consoles. When something important happens in one of them, bring the window up front. I haven't been able to find any library or example for it.

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • There's nothing like that built into node.js. You'd have to find a native code module that does that on Windows. I'm not aware of one. – jfriend00 Nov 16 '19 at 22:13
  • 1
    Yes, using batch-scripting, you can look at this answer for inspiration https://stackoverflow.com/questions/557166/bring-to-front-for-windows-xp-command-shell – ege Nov 19 '19 at 13:59

2 Answers2

4

TL;DR; bringToFront


Use Node.js N-API

The basic idea is to use the windows api to controll the window states.

One can use SetForegroundWindow.

But there are some restrictions:

  • The process is the foreground process.
  • The process was started by the foreground process.
  • The process received the last input event.
  • There is no foreground process.
  • The foreground process is being debugged.
  • The foreground is not locked (see LockSetForegroundWindow).
  • The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • No menus are active.

To workaround those restrictions one can programatically simulate userinput:

  • press alt key (think of e.g.: alt+tab):

  • keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);

  • now bring to front

  • SetForegroundWindow(hWnd);

  • release key

  • keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);


copy of main logic for stackoverflow:

    #include "./pidToFront.h"
    #include <windows.h>

    using namespace v8;

    void SetForegroundWindowInternal(HWND hWnd)
    {
        if(!::IsWindow(hWnd)) return;

        BYTE keyState[256] = {0};
        //to unlock SetForegroundWindow we need to imitate Alt pressing
        if(::GetKeyboardState((LPBYTE)&keyState))
        {
            if(!(keyState[VK_MENU] & 0x80))
            {
                ::keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
            }
        }

        ::SetForegroundWindow(hWnd);

        if(::GetKeyboardState((LPBYTE)&keyState))
        if(::GetKeyboardState((LPBYTE)&keyState))
        if(::GetKeyboardState((LPBYTE)&keyState))
        {
            if(!(keyState[VK_MENU] & 0x80))
            {
                ::keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
            }
        }
    }

    HWND g_HWND=NULL;
    BOOL CALLBACK EnumWindowsProcMy(HWND hwnd,LPARAM lParam)
    {
        DWORD lpdwProcessId;
        GetWindowThreadProcessId(hwnd,&lpdwProcessId);
        if(lpdwProcessId==lParam)
        {
            g_HWND=hwnd;
            return FALSE;
        }
        return TRUE;
    }
    void win32js::toFront(const Nan::FunctionCallbackInfo<Value>& info){
        Local<Context> context = info.GetIsolate()->GetCurrentContext();
        if(info.Length() < 1 || !info[0]->IsNumber()){
            Nan::ThrowTypeError("Invalid argument expected pid as number");
        }

        LPARAM pid = info[0]->NumberValue(context).FromJust();
        if(EnumWindows(EnumWindowsProcMy, (double)pid)){
            info.GetReturnValue().Set(-2);
        }
        if(g_HWND > 0){
            SetForegroundWindow(g_HWND);
            if(GetForegroundWindow() == g_HWND){
                info.GetReturnValue().Set((double)(unsigned long)g_HWND);
                g_HWND = NULL;
                return;
            }
            SetForegroundWindowInternal(g_HWND);
            if(GetForegroundWindow() == g_HWND){
                info.GetReturnValue().Set((double)(unsigned long)g_HWND);
                g_HWND = NULL;
                return;
            }
        } else {
            info.GetReturnValue().Set(-1);
        }
    }
Estradiaz
  • 3,483
  • 1
  • 11
  • 22
  • I'm not sure how to use this in Node.js JavaScript, but I will try :) – vitaly-t Nov 19 '19 at 23:59
  • On Windows 10, I can see it only flashing the window icon in the task bar, but without bringing it up. – vitaly-t Nov 20 '19 at 08:48
  • I do the same here, on Windows 10. I run it in the console, then click away. It only flashes the icon, but doesn't bring the window up. And yes, it does find the window. – vitaly-t Nov 20 '19 at 09:17
  • Using the latest, not getting any further than just a blinking icon. – vitaly-t Nov 20 '19 at 12:30
  • @vitaly-t ok this time it works for sure - hacked key press into it, changed repo – Estradiaz Nov 20 '19 at 17:31
  • Yes, I can confirm that example works. Would you like to update the answer, and remove what's no longer relevant? Thank you for the repo! I hope you will decide to maintain it ;) – vitaly-t Nov 20 '19 at 17:37
  • Thanks! I used `robotjs` to get around the restrictions you listed by send a key down before calling `SetForegroundWindow()` and a key up event after. – Benjamin Bojko Feb 02 '22 at 23:47
1

I am not sure this the answer to your question but it looks like this could resolve your problem: Setting focus to a Windows application from Node-JS

Sorry if this is wrong.

Laurent Dhont
  • 1,012
  • 1
  • 9
  • 22
  • Can't use that. That's based on now abandoned `ffi` module. I was able to install it specifically under Node.js v8, and nothing else. Doesn't even install under v6, v10, v12, v13. In all, it is useless. – vitaly-t Nov 20 '19 at 08:39