【问题标题】:Delphi - Win7 Window Focus IssueDelphi - Win7 窗口焦点问题
【发布时间】:2011-08-18 03:40:57
【问题描述】:

我通过 CreateProcess() 执行 exe 文件并通过 SetForegroundWindow() 设置前台进程。 但它在Win7中不起作用,所以我必须点击任务栏中的图标。

如何实现我想要的行为(即 Launch 和 BringToForeground)?

【问题讨论】:

  • 您使用CreateProcess 运行的程序应该自动成为前台进程。你根本不需要做任何特别的事情。你做了什么奇怪的事情,一开始就破坏了默认行为?
  • @Rob Kennedy 正如你所说,在 WinXP 中,我不需要任何额外的代码来制作前台进程,但在 Win7 中它不能按我的意愿工作。
  • 在 Windows 7 中也不需要任何特殊代码。你一定是在做其他破坏系统的事情。
  • @David @Rob 我在 ocx(IE8) 中执行 exe 文件。 ActiveX 的情况有什么不同吗?

标签: delphi windows-7 focus


【解决方案1】:

您甚至不应该尝试这样做。 SetForegroundWindow 中的更改是有意的 - 它可以防止应用程序从 用户 想要获得焦点的内容中窃取焦点。请参阅上面链接的备注部分。

Win7 可能不会让非管理员用户更改所需的注册表设置,更不用说不重新启动系统。

按照 Microsoft 的建议,您应该只使用 FlashWindow 来引起用户的注意。任何坚持从我选择做的事情上转移注意力的应用程序都会被立即卸载。

【讨论】:

  • 感谢您的推荐,但在我的情况下,我必须将其设置为前台进程。
  • 如果第二个进程是由第一个进程的 GUI 中的事件处理程序启动的,有时它不是“窃取”焦点(例如,单击一个应用程序中的图标可能会启动另一个应用程序 - 这是用户的意图,那么操作系统为什么要“限制”它)。
  • @Sam,它不限制它,实际上默认行为是使新创建的进程成为前台进程(并给它键盘焦点)。这个问题让我想起了这个:blogs.msdn.com/b/oldnewthing/archive/2011/03/10/10138969.aspx
  • @Sam:没关系。使用Win7,我启动DelphiXE。然后我按下工具栏上的帮助按钮。帮助开始,但没有出现;相反,我在任务栏上看到了帮助图标(一个带问号的蓝色圆圈),它闪烁以引起我的注意。我单击该图标,帮助出现在前台。这是默认(标准)行为,即使对于我显然选择启动的 help 文件也是如此,因为我想查看它。为什么您的应用程序的行为会有所不同?操作系统所知道的是,您的应用正在启动另一个应用;它不知道您是如何/为什么决定这样做的。
  • @Ken,但是如果您想要启动该图标并显示帮助怎么办?如果用户想要它,程序员想要取悦用户,那么操作系统为什么不能满足这种情况呢?
【解决方案2】:

我将发布一个链接(在评论中),指向我曾经用来解决我自己的问题的一段代码。该链接现在已被破坏,因此我在此处发布代码以确定其价值(已在 Windows XP Pro SP2 和 Windows Server 2003 中测试过,但在 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.

我没有在函数中添加任何东西,除了一个小注释“宾果”,它标志着实际产生预期效果的行。

只是为了让你们不认为我在滥用这个功能的用户体验,这里有一些解释。

此功能用于借助用户平板电脑上设置的 Citrix 软件远程调用的应用程序,该应用程序全屏运行。一个典型的工作会话几乎完全由该应用程序组成(其他部分只是用户从未与之交互的系统组件)。

现在我们的应用程序的某些部分必须作为单独的小应用程序来实现,并且它们被设计为在所有其他窗口关闭之前保持在所有其他窗口之上,就像模态窗口一样。有时他们会丢失 Z 顺序并隐藏在主应用程序的主窗口下,这对用户来说是一场彻头彻尾的灾难。使用 'top-most' 属性在那里不是一个选项,所以我们必须找到一种方法来维持他们的 Z 顺序位置。所以我们使用了这个函数。

【讨论】:

  • 我在 Win7(.exe) 中执行此代码,它可以工作。但它在 IE8(.ocx) 中不起作用。我可以就这个问题提出任何建议吗?
  • @도현우:抱歉,帮不了你太多。如果我必须在 IE8 中解决这个问题,我需要像你现在一样搜索答案。
  • @도현우:顺便说一下,由于您的问题似乎专门针对 IE8 OCX,而不是一般的 Win7,您可能想发布另一个问题,具体说明。
【解决方案3】:

ForceForegroundWindow 在 Win10 中为我工作。但是,它不会激活外部程序。它只会使其可见并位于顶部。该程序也仅在调用自身时执行相同操作。我假设如果它激活它也会为用户适当地设置焦点。

瑞克

我找到了激活和设置焦点的解决方案...在“SetAppRestore”过程中,我使用“MainFrm.visible:= false”启动它。然后它转到 SwitchApp,并调用 ForceForegroundWindow。在它返回“SetAppRestore”后,我插入了“MainFrm.visible:= true”。这触发了应用程序变得活跃并专注于定义的组件:DataPge.SetFocus。

对于没有将代码放在代码块中,我深表歉意。我无法理解指示。所以我把它放在两个 ===== 条之间。

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

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;

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

【讨论】:

  • 这似乎没有为 OP 提供任何信息或可操作的信息。
猜你喜欢
  • 1970-01-01
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多