简体   繁体   中英

Delphi - Win7 Window Focus Issue

I execute exe file by CreateProcess() and set foreground process by SetForegroundWindow(). but it doesn't work in Win7 so I have to click icon in taskbar.

How can I implement the behaviour I want (which is to Launch and BringToForeground)?

You shouldn't even try to do this. The change in SetForegroundWindow was intentional - it prevents applications from stealing the focus from what the user wants to have focus. See the Remarks section of the link above.

Win7 probably won't let non-administrative users change the needed registry setting, much less do it without a restart of the system.

You should just use FlashWindow instead to get the user's attention, as Microsoft recommends. Any application that insists on stealing focus away from what I choose to do will be uninstalled immediately.

I was going to post a link (in a comment) to a piece of code that I once had to apply to solve a problem of my own. The link has turned out to be broken now, so I'm posting the code here for what it's worth (it has been tested in Windows XP Pro SP2 and Windows Server 2003, but not in Windows 7 ):

function ForceForegroundWindow(hwnd: THandle): boolean;
{
found here:
http://delphi.newswhat.com/geoxml/forumhistorythread?groupname=borland.public.delphi.rtl.win32&messageid=501_3f8aac4b@newsgroups.borland.com
}
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);
  if GetForegroundWindow = hwnd then Result := true
  else begin
    // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
       ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and ((Win32MajorVersion > 4) or
                                                          ((Win32MajorVersion = 4) and (Win32MinorVersion > 0)))) then begin
      // Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
      // Converted to Delphi by Ray Lischner
      // Published in The Delphi Magazine 55, page 16

      Result := false;
      ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow,nil);
      ThisThreadID := GetWindowThreadPRocessId(hwnd,nil);
      if AttachThreadInput(ThisThreadID, ForegroundThreadID, true) then
      begin
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
        AttachThreadInput(ThisThreadID, ForegroundThreadID, false);  // bingo
        Result := (GetForegroundWindow = hwnd);
      end;
      if not Result then begin
        // Code by Daniel P. Stasinski

        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0), SPIF_SENDCHANGE);
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
      end;
    end
    else begin
      BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
    end;

    Result := (GetForegroundWindow = hwnd);
  end;
end; { ForceForegroundWindow }

end.

I didn't add anything to the function apart from a small comment 'bingo', which marks the line which actually brought about the desired effect.

Just so you guys didn't think I was abusing users' experience with this function, here's some explanation.

This function was used in an application that was called remotely with the help of Citrix software set up on users' Tablet PCs, and the application ran in full screen. A typical working session almost entirely consisted of that application (other parts were just system components which user never interacted with).

Now some parts of our application had to be implemented as separate small applications, and they were designed to stay on top of all other windows until closed, just like modal windows. Once in a while they used to lose their Z order and hide under the main application's main window, and that was a total disaster for users. Using the 'top-most' property wasn't an option there, so we had to find a way to sustain their Z-order positions. And so we used this function.

ForceForegroundWindow worked for me in Win10. However, it does not activate the external program. It only makes it visible and on top. The program also only does the same when calling itself. I am assuming that if it activated it would also setfocus appropriately for the user.

Rick

I found a resolution for activating and setting focus... In the "SetAppRestore" procedure I initiated it with "MainFrm.visible:= false". Then it goes to SwitchApp, and it calls ForceForegroundWindow. After it returns to "SetAppRestore", I inserted "MainFrm.visible:= true". This triggered the app to become active and have focus on defined component: DataPge.SetFocus.

I apologize for not placing the code in a code block. I couldn't understand the instructions. So I put it all between 2 ===== bars.

//==========================

function TMainFrm.FindWindowExtd(partialTitle: string): HWND;  // get with wildcard
var                                   // by Dorin Duminica, September 10, 2009
  hWndTemp: hWnd;
  iLenText: Integer;
  cTitletemp: array [0..254] of Char;
  sTitleTemp: string;
begin
  hWndTemp := FindWindow(nil, nil);
  while hWndTemp <> 0 do
    begin
    iLenText := GetWindowText(hWndTemp, cTitletemp, 255);
    sTitleTemp := cTitletemp;
    sTitleTemp := UpperCase(copy( sTitleTemp, 1, iLenText));
    partialTitle := UpperCase(partialTitle);
    if pos(partialTitle, sTitleTemp) <> 0 then Break;
    hWndTemp := GetWindow(hWndTemp, GW_HWNDNEXT);
    end;
  result := hWndTemp;
end;

function ForceForegroundWindow(hwnd: THandle): boolean;
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);
  if GetForegroundWindow = hwnd
     then Result:= true
     else begin
          // Windows 98/2000 doesn't want to foreground a window when some other
          // window has keyboard focus
          if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
             ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and ((Win32MajorVersion > 4) or
             ((Win32MajorVersion = 4) and (Win32MinorVersion > 0)))) then
               begin
               // Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
               // Converted to Delphi by Ray Lischner
               // Published in The Delphi Magazine 55, page 16
               Result:= false;
               ForegroundThreadID:= GetWindowThreadProcessID(GetForegroundWindow,nil);
               ThisThreadID:= GetWindowThreadPRocessId(hwnd,nil);
               if AttachThreadInput(ThisThreadID, ForegroundThreadID, true) then
                  begin
                  BringWindowToTop(hwnd); // IE 5.5 related hack
                  SetForegroundWindow(hwnd);
                  AttachThreadInput(ThisThreadID, ForegroundThreadID, false);  // bingo
                  Result:= (GetForegroundWindow = hwnd);
                  //showmessage('case 1');
                  end;
               if not Result then
                  begin
                  // Code by Daniel P. Stasinski
                  SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
                  SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0), SPIF_SENDCHANGE);
                  BringWindowToTop(hwnd); // IE 5.5 related hack
                  SetForegroundWindow(hWnd);
                  SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
                  //showmessage('case 2');
                  end;
               end
               else begin
                    BringWindowToTop(hwnd); // IE 5.5 related hack
                    SetForegroundWindow(hwnd);
                    //showmessage('case 3');
                    end;
          Result:= (GetForegroundWindow = hwnd);
          end;
end; { ForceForegroundWindow }

procedure TMainFrm.SwitchApp(AppCaption:string); // application.restore;
          begin
          //TmpAppHandle:= FindWindow(nil, PChar(AppCaption)); // uses Windows unit - must be entire caption
          TmpAppHandle:= FindWindowExtd(AppCaption);   //  finds 'notepad' as partial of 'Document - Notepad'
          if (TmpAppHandle<>0)
             then begin
                  //SetForegroundWindow(TmpAppHandle); // worked by itself for WinXP and Win7
                  ForceForegroundWindow(TmpAppHandle);
                  end
             else ShowAlert(AppCaption+' *not found*');
          end;

// application.restore can't restore from MainForm.windowstate:=wsMinimized
// SetAppMinimize and SetAppRestore fix that issue and manual minimizations
procedure TMainFrm.SetAppMinimize; // application.minimize
          begin
          if not(MainFrm.WindowState=wsMinimized) then
             begin
             MainFrm.WindowState:= wsMinimized;
             end;
          SwitchApp(ServerName); // autocad or bricscad
          end;

procedure TMainFrm.SetAppRestore; // application.restore
          begin
          MainFrm.visible:= false;  // ** to reinsate and focus in win10 **
          if (MainFrm.WindowState=wsMinimized) then
             begin
             MainFrm.WindowState:= wsNormal;
             end;
          SwitchApp('CmdData');  // partial string for app title
          MainFrm.visible:= true;   // ** to reinsate and focus in win10 **
          FormatGrid; // added for activex crash
          DataPge.SetFocus;
          Update;
          end;

//==========================

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM