Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Noitidart/b7cbb80622241c249961 to your computer and use it in GitHub Desktop.
Save Noitidart/b7cbb80622241c249961 to your computer and use it in GitHub Desktop.
_ff-addon-snippet-WINAPI_FocusMostRecentWindowOfPID - Windows. WinAPI. js-ctypes. Give pid and the most recent window will be focused. Even if it's minimized.
Cu.import('resource://gre/modules/ctypes.jsm');
var lib = {
user32: ctypes.open('user32.dll')
}
var NULL = ctypes.cast(ctypes.uint64_t(0x0), ctypes.voidptr_t);
/* http://msdn.microsoft.com/en-us/library/ms633514%28VS.85%29.aspx
* HWND WINAPI GetTopWindow(
* __in_opt_ HWND hWnd
* );
*/
var GetTopWindow = lib.user32.declare('GetTopWindow', ctypes.winapi_abi, ctypes.voidptr_t,
ctypes.voidptr_t
);
/* http://msdn.microsoft.com/en-us/library/ms633515%28v=vs.85%29.aspx
* I was trying to use GetNextWindow however that is not available through DLL, but that just calls GetWindow so am using GetWindow with GW_HWNDNEXT instead
* HWND WINAPI GetWindow(
* __in_ HWND hWnd,
* __in_ UINT wCmd
* );
*/
var GetWindow = lib.user32.declare('GetWindow', ctypes.winapi_abi, ctypes.voidptr_t,
ctypes.voidptr_t,
ctypes.unsigned_int
);
var GW_HWNDNEXT = 2;
/* http://msdn.microsoft.com/en-us/library/ms633515%28v=vs.85%29.aspx
* LONG_PTR WINAPI GetWindowLongPtr(
* __in_ HWND hWnd,
* __in_ int nIndex
* );
*/
if (ctypes.voidptr_t.size == 4 /* 32-bit */) {
var GetWindowLong = lib.user32.declare('GetWindowLongW', ctypes.winapi_abi, ctypes.unsigned_long,
ctypes.voidptr_t,
ctypes.int
);
} else if (ctypes.voidptr_t.size == 8 /* 64-bit */) {
var GetWindowLong = lib.user32.declare('GetWindowLongPtrW', ctypes.winapi_abi, ctypes.unsigned_long.ptr,
ctypes.voidptr_t,
ctypes.int
);
}
var GWL_STYLE = -16;
var WS_VISIBLE = 0x10000000;
var WS_CAPTION = 0x00C00000;
var styleIsInAltTab = WS_VISIBLE | WS_CAPTION;
var GetClassName = lib.user32.declare('GetClassNameW', ctypes.winapi_abi, ctypes.int,
ctypes.voidptr_t, // HWND
ctypes.jschar.ptr, // LPTSTR
ctypes.int
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms633522%28v=vs.85%29.aspx
* DWORD WINAPI GetWindowThreadProcessId(
* __in_ HWND hWnd,
* __out_opt_ LPDWORD lpdwProcessId
* );
*/
var GetWindowThreadProcessId = lib.user32.declare('GetWindowThreadProcessId', ctypes.winapi_abi, ctypes.unsigned_long, //DWORD
ctypes.voidptr_t, //ctypes.uint32_t, //HWND
ctypes.uint32_t.ptr //LPDWORD
);
/* http://msdn.microsoft.com/en-us/library/ms633539%28v=vs.85%29.aspx
* BOOL WINAPI SetForegroundWindow(
* __in HWND hWnd
* );
*/
var SetForegroundWindow = lib.user32.declare('SetForegroundWindow', ctypes.winapi_abi, ctypes.bool,
ctypes.voidptr_t // HWND
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms633507%28v=vs.85%29.aspx
* BOOL WINAPI IsIconic(
* __in HWND hWnd
* );
*/
var IsIconic = lib.user32.declare('IsIconic', ctypes.winapi_abi, ctypes.bool, // BOOL
ctypes.voidptr_t // HWND
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms633507%28v=vs.85%29.aspx
* BOOL WINAPI ShowWindow(
* __in HWND hWnd
* __in INT nCmdShow
* );
*/
var ShowWindow = lib.user32.declare('ShowWindow', ctypes.winapi_abi, ctypes.bool, // BOOL
ctypes.voidptr_t, // HWND
ctypes.int // INT
);
var SW_RESTORE = 9;
function winFocusWindow(hwnd) {
if (IsIconic(hwnd)) {
console.warn('its minimized so un-minimize it');
//its minimized so unminimize it
var rez = ShowWindow(hwnd, SW_RESTORE);
if (!rez) {
throw new Error('Failed to un-minimize window');
}
}
var rez = SetForegroundWindow(hwnd);
if (!rez) {
console.log('could not set to foreground window for a reason other than minimized, maybe process is not foreground, lets try that now');
/*
var cPid = ctypes.cast(ctypes.voidptr_t(0), ctypes.unsigned_long);
var rez = GetWindowThreadProcessId(hwnd, cPid.address());
if (!rez) {
throw new Error('Failed to get PID');
} else {
console.log('trying to set pid to foreground process');
return false;
}
*/
} else {
return rez;
}
}
var hwndC = GetTopWindow(null);
var hwndStyle;
var i = 0;
var buf;
var PID = new ctypes.uint32_t;
var skipThisMany = 3;
var skipped = 0;
while (hwndC != NULL) {
//console.log('i:', i);
hwndC = GetWindow(hwndC, GW_HWNDNEXT);
var rez_GWTPI = GetWindowThreadProcessId(hwndC, PID.address());
//console.log(i, 'rez_GWTPI:', rez_GWTPI.toString(), 'pid:', PID.value);
if (rez_GWTPI > 0 && PID.value == 4764) {
//console.log('pid found:', PID.value);
hwndStyle = GetWindowLong(hwndC, GWL_STYLE);
if ('contents' in hwndStyle) {
hwndStyle = hwndStyle.contents; //handles 64 bit //untested but im pretty sure (99%) it will get the uint64 like it is for 32bit
}
//console.log('hwndStyle', hwndStyle.toString());
if (hwndStyle & WS_VISIBLE) {
var rez_WFW = winFocusWindow(hwndC);
console.log('rez_WFW:', rez_WFW);
if (!rez_WFW) {
throw new Error('failed to focus most recent window');
}
break;
}
}
/*
buf = new new ctypes.ArrayType(ctypes.jschar, 255);
GetClassName(hwndC, buf, 255);
var className = buf.readString();
hwndStyle = GetWindowLong(hwndC, GWL_STYLE);
if (className == 'MozillaWindowClass') {
var rez = GetWindowThreadProcessId(hwndC, PID.address());
console.log(PID.value);
if (PID.value == 4764) {
if (skipped == skipThisMany) {
var rez_SFW = SetForegroundWindow(hwndC);
console.log('rez_SFW:', rez_SFW);
break;
} else {
console.log('SKIPPING ', skipped);
skipped++;
}
}
};
*/
i++;
if (i >= 3000) {
console.warn('breaking because went through too many windows, i:', i);
throw new Error('could not find most recent window of this pid')
break;
}
}
console.log('hwndC:', hwndC);
for (var l in lib) {
lib[l].close();
}
@Noitidart
Copy link
Author

README

Rev1 - Rev1

Forked from _ff-addon-snippet-FocusWindowHwndCTypes.js

Rev2

  • Pasted working code
  • Right now to avoid infinite loop it breaks at 3000 (L#200) - should probably find a better way to avoid infinite loop
  • Tested and works

Rev3

  • Updated file name to match description

Rev4

  • Code clean up also found out that null works in place of NULL

Rev5

  • Included "WINAPI_" into the file and gist name

@Noitidart
Copy link
Author

behavior research:

win81:

  • works as expected, it focuses the most recently used window EVEN IF minmized

win7 and winxp:

learned that when minimized it has WS_ICONIC and WS_MINIMIZED flags while still having the WS_CAPTION and WS_VISIBLE flags
  • focused browser, minimized, so last focus was browser. scratch is currently non-minimized. test result :: clicked focus most rec win profilist : it focuses scratch, and the hwnd of browser does not occur before the hwnd of scratch (i was hoping it would occur before scratch but would have WS_MINIMIZED-or-something and just be missing WS_VISIBLE or something)

    //hwnds
    0x240de4 //scratch
    0x110e30 //browser
    
  • if all windows minimized: it focuses the window that was FIRST minimized

    • this one i can fix by seeing if the first hwnd found with WS_VISIBLE and WS_CAPTION has minimized flags, if it does then keep going till it finds the last WS_VISIBLE and WS_CAPTION one (im pretty sure all the ones after it will have the minimized flags, really very sure as it looks like when minimize it gets thrown to end [right in alt+tab menu] of array (unlike when focus non-minimized it gets moved to top of array [left in alt tab]))

i posted this on stackoverflow:


This is analysis on differetn Windows versions of the code by @CoryNelson (huge thanks to him for the help)

behavior research via physical testing:

NOTE learned that when minimized hwnd has WS_ICONIC and WS_MINIMIZED flags while still having the WS_CAPTION and WS_VISIBLE flags

win81:

  • works as expected, it focuses the most recently used window EVEN IF minmized

win7 and winxp:

  • focused browser, minimized, so last focus was browser. scratch is currently non-minimized. test result :: clicked focus most rec win profilist : it focuses scratch, and the hwnd of browser does not occur before the hwnd of scratch (i was hoping it would occur before scratch but would have WS_MINIMIZED-or-something and just be missing WS_VISIBLE or something)

    //hwnds
    0x240de4 //scratch
    0x110e30 //browser
    
    • FIX: i dont know how to fix this one yet, anyone any ideas other than hooking? ill keep thinking, mainly posting this here for myself, but if anyone wants to apply thoughts more then welcome!!
  • if all windows minimized: it focuses the window that was FIRST minimized

    • FIX: this one i can fix by seeing if the first hwnd found with WS_VISIBLE and WS_CAPTION has minimized flags, if it does then keep going till it finds the last WS_VISIBLE and WS_CAPTION one (im pretty sure all the ones after it will have the minimized flags, really very sure as it looks like when minimize it gets thrown to end [right in alt+tab menu] of array (unlike when focus non-minimized it gets moved to top of array [left in alt tab]))

GUESSES as i cant physically test these

win8

  • im guessing win8 behaves as win81

winvista

  • im guessing winVist behaves as winxp and win7 as its in between

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment