Problem (and Fix!)
- Posted by Pete Stoner <peter_stoner at hotmail.com> May 13, 2005
- 563 views
This was an interesting one (for me anyway!). I have a program that uses a hidden window (running XP)and just displays a popup menu at the cursor position. I had a problem with it not reliably closing after a selection was made from the menu and I also wanted to reduce the startup time. I first started using the IPC library to find and close any other running instance(s) but then I thought why not just reopen the popup menu in the existing instance. This kind of worked but had the problem where when the popup was reopened if the mouse was clicked elsewhere on the screen the popup didn't close itself as normal - this is due to the owning (hidden) window not being in the foreground. In XP setForeground just makes a window flash on the taskbar as this is deemed more polite now than bringing up the window. So I searched the web and found this... Under Windows 95 and NT4, if an inactive application calls SetActiveWindow() or SetForegroundWindow() using the HWND of one of its windows, this makes the application active. The same applies if it calls SetWindowPos() with HWND_TOP and SWP_SHOWWINDOW (no need for HWND_TOPMOST windows). These 3 APIs are basically equivalent. Under ME and XP, this does not happen. Instead the inactive application has its button highlighted on the windows taskbar but is not made active. Also if the inactive application calls SetForegroundWindow(), the same thing happens. If SetForegroundWindow() is called from an active application (an app with the active window), and is passed the HWND of a window in an inactive application, it *can* properly activate the other application and the other window. But if an inactive application calls SetForegroundWindow(), it can't make itself active. This applies under all 32 bit versions of Windows. Therefore the only reliable system of inter-application activations that works under all versions of Windows is for the currently active application to call SetForegroundWindow() using the HWND of a window in the other application. So now I am using the IPC lib to check if the program is already running, and if so then it requests its HWND, uses this to bring the existing instance to the foreground, then tells the existing one to reopen its popup and finally closes down. example code below.
without warning include win32lib.ew include ipc.ew without trace atom ExistingHwnd if not ipc_RegisterProcessName("Test Win") then ExistingHwnd = ipc_CallFunc("Test Win","GetHwnd",{}) if ExistingHwnd != -1 then if w32Func(xSetForegroundWindow,{ExistingHwnd}) then -- existing instance set to foreground ipc_CallProc("Test Win", "ReOpenPopup", {}) -- ask existing instance to reopen popup end if end if abort(1) end if constant Main = createEx( Window, "Test Win", 0, 0, 0, 0, 0, WS_POPUP, WS_EX_TOOLWINDOW), MyPopup = create( Menu, "", Main, 0, 0, 0, 0, 0 ), item1 = create(MenuItem, "test", MyPopup, 0, 0, 0, 0, 0) procedure Main_w32HActivate( integer self, integer event, sequence params ) sequence pos captureMouse( Main ) pos = getPointerPos() popup( MyPopup, pos[1], pos[2] ) end procedure setHandler( Main, w32HActivate, routine_id("Main_w32HActivate") ) function GetHwnd() return getHandle(Main) end function procedure ReOpenPopup() sequence pos setFocus(Main) captureMouse( Main ) pos = getPointerPos() popup( MyPopup, pos[1], pos[2] ) end procedure ipc_RegisterFunc("GetHwnd",routine_id("GetHwnd")) ipc_RegisterProc("ReOpenPopup",routine_id("ReOpenPopup")) WinMain( Main, Normal)
Sorry for the long winded post but I was pleased to get it working and thought someone else may find it useful... Pete.