diff --git a/lib/windows.js b/lib/windows.js index 03b6492..d55662d 100644 --- a/lib/windows.js +++ b/lib/windows.js @@ -14,6 +14,7 @@ const Rect = struct({ bottom: 'long' }); const RectPointer = ref.refType(Rect); +const VoidPointer = ref.refType(ref.types.void); // Required by QueryFullProcessImageName // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx @@ -31,7 +32,10 @@ const user32 = new ffi.Library('User32.dll', { GetWindowThreadProcessId: ['uint32', ['pointer', 'uint32 *']], // Get window bounds function // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowrect - GetWindowRect: ['bool', ['pointer', RectPointer]] + GetWindowRect: ['bool', ['pointer', RectPointer]], + // Iterate through child windows + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumchildwindows + EnumChildWindows: ['bool', ['pointer', VoidPointer, 'int32']] }); const SIZE_T = 'uint64'; @@ -68,6 +72,74 @@ const kernel32 = new ffi.Library('kernel32', { QueryFullProcessImageNameW: ['int', ['pointer', 'uint32', 'pointer', 'pointer']] }); +// Check parent window for SubWindows and find the unique process inside of it. +function getSubWindowRealProcessPath(activeWindowHandle, processPath) { + let newProcessPath; + + const windowProcess = ffi.Callback('bool', ['pointer', 'int32'], hwnd => { + // Return the Process ID & Process Handle from the Active Window Handle + const [, processHandle] = getProcessIdAndHandle(hwnd); + + if (ref.isNull(processHandle)) { + return false; // Failed to get process handle + } + + const processPathChild = getProcessPath(processHandle); + + if (processPathChild !== processPath) { + newProcessPath = processPathChild; + return false; + } + + return true; + }); + + user32.EnumChildWindows(activeWindowHandle, windowProcess, 0); + + if (newProcessPath !== undefined && newProcessPath !== processPath) { + return newProcessPath; + } + + return processPath; +} + +function getProcessPath(processHandle) { + if (ref.isNull(processHandle)) { + return undefined; + } + + // Set the path length to more than the Windows extended-length MAX_PATH length + // The maximum path of 32,767 characters is approximate, because the "\\?\" prefix may be expanded to a longer string by the system at run time, and this expansion applies to the total length. + const pathLengthBytes = 66000; + // Path length in "characters" + const pathCharacterCount = Math.floor(pathLengthBytes / 2); + // Allocate a buffer to store the path of the process + const processFileNameBuffer = Buffer.alloc(pathLengthBytes); + // Create a buffer containing the allocated size for the path, as a buffer as it must be writable + const processFileNameSizeBuffer = ref.alloc('uint32', pathCharacterCount); + // Write process file path to buffer + kernel32.QueryFullProcessImageNameW(processHandle, 0, processFileNameBuffer, processFileNameSizeBuffer); + // Remove null characters from buffer + const processFileNameBufferClean = ref.reinterpretUntilZeros(processFileNameBuffer, wchar.size); + // Get process file path as a string + const processPath = wchar.toString(processFileNameBufferClean); + + return processPath; +} + +function getProcessIdAndHandle(windowHandle) { + // Allocate a buffer to store the process ID + const processIdBuffer = ref.alloc('uint32'); + // Write the process ID creating the window to the buffer (it returns the thread ID, but it's not used here) + user32.GetWindowThreadProcessId(windowHandle, processIdBuffer); + // Get the process ID as a number from the buffer + const processId = ref.get(processIdBuffer); + // Get a "handle" of the process + const processHandle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId); + + return [processId, processHandle]; +} + function windows() { // Windows C++ APIs' functions are declared with capitals, so this rule has to be turned off @@ -93,35 +165,23 @@ function windows() { // The text as a JavaScript string const windowTitle = wchar.toString(windowTextBufferClean); - // Allocate a buffer to store the process ID - const processIdBuffer = ref.alloc('uint32'); - // Write the process ID creating the window to the buffer (it returns the thread ID, but it's not used here) - user32.GetWindowThreadProcessId(activeWindowHandle, processIdBuffer); - // Get the process ID as a number from the buffer - const processId = ref.get(processIdBuffer); - // Get a "handle" of the process - const processHandle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId); + // Return the Process ID & Process Handle from the Active Window Handle + const [processId, processHandle] = getProcessIdAndHandle(activeWindowHandle); if (ref.isNull(processHandle)) { return undefined; // Failed to get process handle } - // Set the path length to more than the Windows extended-length MAX_PATH length - const pathLengthBytes = 66000; - // Path length in "characters" - const pathLengthChars = Math.floor(pathLengthBytes / 2); - // Allocate a buffer to store the path of the process - const processFileNameBuffer = Buffer.alloc(pathLengthBytes); - // Create a buffer containing the allocated size for the path, as a buffer as it must be writable - const processFileNameSizeBuffer = ref.alloc('uint32', pathLengthChars); - // Write process file path to buffer - kernel32.QueryFullProcessImageNameW(processHandle, 0, processFileNameBuffer, processFileNameSizeBuffer); - // Remove null characters from buffer - const processFileNameBufferClean = ref.reinterpretUntilZeros(processFileNameBuffer, wchar.size); - // Get process file path as a string - const processPath = wchar.toString(processFileNameBufferClean); + // Return the Process Path & Process Name from a Process Handle + let processPath = getProcessPath(processHandle); // Get process file name from path - const processName = path.basename(processPath); + let processName = path.basename(processPath); + + // ApplicationFrameHost & Universal Windows Platform Support + if (processName === 'ApplicationFrameHost.exe') { + processPath = getSubWindowRealProcessPath(activeWindowHandle, processPath); + processName = path.basename(processPath); + } // Get process memory counters const memoryCounters = new ProcessMemoryCounters();