簡體   English   中英

使用firefox插件將Firefox窗口置於最前面

[英]bring firefox window to the front using firefox addon

當我在firefox addon上運行特定功能時,我想聚焦firefox。我創建了一個可以聚焦firefox的.vbs文件[帶到頂部],然后我使用nsIProcess執行該exe。

file.initWithPath("C:\\Users\\madhawax\\Documents\\focusFirefox.vbs");
var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
process.init(file);
process.run(false, args, args.length);

它工作正常。但是由於某些原因,我想直接從附加代碼中聚焦Firefox,而無需另一個應用程序的幫助。 我閱讀了firefox windows API,但無論如何我都找不到焦點window。我要問的是如何從插件代碼集中瀏覽器。

編輯..

這是我用來關注firefox的代碼。

focusFirefox.vbs

Set wshShell = CreateObject("WScript.Shell")
wshShell.AppActivate("Firefox")

這些解決方案使用js-ctypes winapi。 這是Windows解決方案。 OSX和Linux沒有這個問題,很容易在那兒搶到重點。

如果我們可以只使用SetForegroundWindow winapi方法,這將很容易,但是調用此方法的過程必須以用戶為中心。 如果沒有,則它什么都不做,因此可以通過以下方法強制執行此操作:

附加到線程方法(推薦方法)

此方法執行以下操作:

  1. 獲取當前在前台的窗口
  2. 如果未找到,則常規SetForegroundWindow將正常工作,然后退出
  3. 如果前景中的窗口找到它,則獲取其線程的ID
  4. 然后將其與當前firefox的線程ID(即目標Windows線程)進行比較。
  5. 如果相同,則使用常規SetForegroundWindow並退出
  6. 如果不相同,則使用AttachThreadInput將Firefox的線程附加到當前在前台窗口中的線程
  7. 如果步驟6中的調用失敗,則放棄並退出該函數,並引發錯誤
  8. 如果第6步中的調用沒有失敗,則它將調用SetForegroundWindow因為它將立即工作
  9. 然后,它撤消在步驟6中完成的附件,因此,如果現在執行SetForegroundWindow調用,它將失敗,如通常情況那樣

我想不出AttachThreadInput失敗的原因,但是如果失敗了,那是此方法唯一AttachThreadInput部分。 如果有人知道失敗的原因,請針對該失敗原因分享解決方案。 但是,我將SetCursorPos方法留在下面作為緊急備用。

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/ctypes.jsm');

if (ctypes.voidptr_t.size == 4 /* 32-bit */ ) {
    var is64bit = false;
} else if (ctypes.voidptr_t.size == 8 /* 64-bit */ ) {
    var is64bit = true;
} else {
    throw new Error('huh??? not 32 or 64 bit?!?!');
}

var user32 = ctypes.open('user32.dll');

var GetForegroundWindow = user32.declare('GetForegroundWindow', ctypes.winapi_abi,
    ctypes.voidptr_t // return
);

var DWORD = ctypes.uint32_t;
var LPDWORD = DWORD.ptr;

var GetWindowThreadProcessId = user32.declare('GetWindowThreadProcessId', ctypes.winapi_abi,
    DWORD, // return
    ctypes.voidptr_t, // hWnd
    LPDWORD // lpdwProcessId
);

var AttachThreadInput = user32.declare('AttachThreadInput', ctypes.winapi_abi,
    ctypes.bool, // return
    DWORD, // idAttach
    DWORD, // idAttachTo
    ctypes.bool // fAttach
);

var SetForegroundWindow = user32.declare('SetForegroundWindow', ctypes.winapi_abi,
    ctypes.bool, // return BOOL
    ctypes.voidptr_t // HWND
);

function forceFocus() {

    var hToDOMWindow = Services.wm.getMostRecentWindow('navigator:browser');
    if (!hToDOMWindow) {
        throw new Error('No browser window found');
    }

    var hToBaseWindow = hToDOMWindow.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsIDocShellTreeItem)
        .treeOwner
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIBaseWindow);

    var hToString = hToBaseWindow.nativeHandle;
    var hTo = ctypes.voidptr_t(ctypes.UInt64(hToString));


    var hFrom = GetForegroundWindow();
    if (hFrom.isNull()) {
        var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
        console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);
        return true;
    }

    if (hTo.toString() == hFrom.toString()) {
        console.log('window is already focused');
        return true;
    }

    var pid = GetWindowThreadProcessId(hFrom, null);
    console.info('pid:', pid);

    var _threadid = GetWindowThreadProcessId(hTo, null); // _threadid is thread of my firefox id, and hTo is that of my firefox id so this is possible to do
    console.info('_threadid:', _threadid);

    if (pid == _threadid) {
        var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
        console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);
        return true;
    }

    var rez_AttachThreadInput = AttachThreadInput(_threadid, pid, true)
    console.info('rez_AttachThreadInput:', rez_AttachThreadInput);
    if (!rez_AttachThreadInput) {
        throw new Error('failed to attach thread input');
    }
    var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
    console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);

    var rez_AttachThreadInput = AttachThreadInput(_threadid, pid, false)
    console.info('rez_AttachThreadInput:', rez_AttachThreadInput);
}

setTimeout(function() {
    forceFocus();
    user32.close();
}, 5000);

然后竊取,然后恢復光標位置方法(不推薦使用的方法-並非傻瓜,並且由於它模擬點擊-如果用戶全屏顯示窗口並且鏈接位於窗口的0,0位置,則它可能會點擊鏈接之類的內容)

這是一個使用js-ctypes且僅在Windows操作系統上有效的解決方案,該解決方案執行以下操作:

  1. 將目標窗口設置為“始終在頂部”(但焦點不在窗口中)
  2. 獲取當前光標位置
  3. 將光標位置設置在窗口的左上方
  4. 單擊以使其具有焦點
  5. 將光標位置恢復到原來的位置
  6. 將窗口設置回正常(不是“總是在頂部”)

它有一個問題,即不尊重SendInput中鼠標單擊的x和y坐標,這就是為什么我必須使用SetCursorPos的原因,在以后的版本中,我想對此進行修復,以便在移動鼠標時不使用SetCursorPos用戶鼠標對用戶來說很煩人,但是好處遠遠超過了輕微的煩人,因為它對賦予窗口焦點非常重要,我暫時接受此SetCursorPos方法,因為我在“竊取前將光標位置恢復到”,因此在這里是:

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/ctypes.jsm');

function myFocus() {

    //////// START Ctypes DECLARES

    if (ctypes.voidptr_t.size == 4 /* 32-bit */ ) {
        var is64bit = false;
    } else if (ctypes.voidptr_t.size == 8 /* 64-bit */ ) {
        var is64bit = true;
    } else {
        throw new Error('huh??? not 32 or 64 bit?!?!');
    }

    var user32 = ctypes.open('user32.dll');

    var SetWindowPos = user32.declare('SetWindowPos', ctypes.winapi_abi,
        ctypes.bool, //return
        ctypes.voidptr_t, //hwnd
        ctypes.voidptr_t, //hWndInsertAfter
        ctypes.int, //X
        ctypes.int, //Y
        ctypes.int, //cx
        ctypes.int, //cy
        ctypes.unsigned_int //uFlags
    );

    var RECT = ctypes.StructType('_RECT', [
        {left: ctypes.long},
        {top: ctypes.long},
        {right: ctypes.long},
        {bottom: ctypes.long}
    ]);
    var LPRECT = RECT.ptr;
    var GetWindowRect = user32.declare('GetWindowRect', ctypes.winapi_abi,
        ctypes.bool, // return
        ctypes.voidptr_t, // hwnd
        LPRECT // lpRect
    );

    var SetCursorPos = user32.declare('SetCursorPos', ctypes.winapi_abi,
        ctypes.bool, // return
        ctypes.int, // x
        ctypes.int // y
    );

    var POINT = ctypes.StructType('_tagPoint', [
        {x: ctypes.long},
        {y: ctypes.long}
    ]);
    var LPPOINT = POINT.ptr;

    var GetCursorPos = user32.declare('GetCursorPos', ctypes.winapi_abi,
        ctypes.bool, // return
        LPPOINT // lpPoint
    );

    // send mouse stuff
    var ULONG_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_long;
    var DWORD = ctypes.uint32_t;
    var MOUSEINPUT = ctypes.StructType('tagMOUSEINPUT', [
        {'dx': ctypes.long},
        {'dy': ctypes.long},
        {'mouseData': DWORD},
        {'dwFlags': DWORD},
        {'time': ULONG_PTR},
        {'dwExtraInfo': DWORD}
    ]);

    var INPUT = ctypes.StructType('tagINPUT', [
        {'type': DWORD},
        {'mi': MOUSEINPUT} // union, pick which one you want, we want keyboard input
    ]);
    var LPINPUT = INPUT.ptr;

    var SendInput = user32.declare('SendInput', ctypes.winapi_abi, ctypes.unsigned_int, ctypes.unsigned_int, LPINPUT, ctypes.int);

    var INPUT_MOUSE = 0;
    var MOUSEEVENTF_LEFTDOWN = 2;
    var MOUSEEVENTF_LEFTUP = 4;
    var MOUSEEVENTF_ABSOLUTE = 0x8000;
    // end send mouse stuff

    var HWND_TOP = ctypes.voidptr_t(-1); //ctypes.cast(ctypes.int(-1), ctypes.voidptr_t);
    var HWND_NOTOPMOST = ctypes.voidptr_t(-2);
    var SWP_NOMOVE = 2;
    var SWP_NOSIZE = 1;

    //////// END Ctypes DECLARES


    // pick a one of our navigator:browser firefox windows to focus
    var browserWindow = Services.wm.getMostRecentWindow('navigator:browser');
    if (!browserWindow) {
        throw new Error('No browser window found');
    }

    // convert our DOMWindow to a HWND
    var baseWindow = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsIDocShellTreeItem)
        .treeOwner
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIBaseWindow);
    var hwndString = baseWindow.nativeHandle;
    var hwnd = ctypes.voidptr_t(ctypes.UInt64(hwndString));


    browserWindow.focus(); // this is important, withou this, i dont know why, but the window will not become "always on top" from the SetWindowPos code on the next line
    var rez_SetWindowPos = SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var myRect = RECT();
    var rez_GetWindowRect = GetWindowRect(hwnd, myRect.address());
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var myRectLeft = parseInt(myRect.left.toString());
    var myRectTop = parseInt(myRect.top.toString());
    console.log('myRect.left', myRectLeft);
    console.log('myRect.top', myRectTop);

    var myPoint = POINT();
    var rez_GetCursorPos = GetCursorPos(myPoint.address());
    console.log('rez_GetCursorPos:', rez_GetCursorPos);
    var myPointX = parseInt(myPoint.x.toString());
    var myPointY = parseInt(myPoint.y.toString());
    console.log('myPoint.x', myPointX);
    console.log('myPoint.y', myPointY);

    var rez_SetCursorPos = SetCursorPos(myRect.left, myRect.top);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    // may need to wait for the window to come to top
    // send click - i dont know why but the x and y coords of these send clicks is not being respected, it is just clicking where the mouse is, so im having to use SetCursorPos as temp hack
    var js_pInputs = [
        INPUT(INPUT_MOUSE, MOUSEINPUT(myRectLeft, myRectTop, 0, MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE, 0, 0)),
        INPUT(INPUT_MOUSE, MOUSEINPUT(myRectLeft, myRectTop, 0, MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE, 0, 0))
    ];

    var pInputs = INPUT.array()(js_pInputs);

    var rez_SI = SendInput(pInputs.length, pInputs, INPUT.size);
    console.log('rez_SI:', rez_SI.toString());
    // end send click

    var rez_SetCursorPos = SetCursorPos(myPoint.x, myPoint.y);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var rez_SetWindowPos = SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    user32.close();

}
setTimeout(function() {
    myFocus();
}, 5000);

這是需要的方法,因為如果調用它的進程不是當前關注的進程,則SetForegroundWindow不會執行任何操作。

經過測試,即使將另一個窗口設置為“始終在頂部”,此方法仍然有效,但是由於另一個窗口始終位於頂部,因此將焦點放在此窗口之后將是最頂部。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM