【问题标题】:How to force Internet Explorer to open it's current running instance rather than creating a new one?如何强制 Internet Explorer 打开它当前正在运行的实例而不是创建一个新实例?
【发布时间】:2012-04-13 11:11:00
【问题描述】:

如何以编程方式启动 IE (iexplore.exe) 并在当前运行的实例中导航(通过打开新选项卡或替换当前 URL)而不是创建新实例。

我已经搜索了一个命令行开关,并尝试使用InternetExplorer.Application,但无济于事。

这是我需要的(IE6-IE9 会很好):

ShellExecute(Handle, 'open', 'iexplore.exe',
    '"http://google.com" -single_instance',
    nil, SW_RESTORE);

这里有一些代码来展示我的尝试。 Delphi 中的代码(部分基于How to get IHTMLDocument2 from a HWND):

implementation

uses ShellApi, ComObj, ActiveX, SHDocVw, MSHTML;        

function GetIEFromHWND(WHandle: HWND; var IE: IWebbrowser2): Boolean;
type
  TObjectFromLResult = function(LRESULT: lResult; const IID: TIID;
    wParam: WPARAM; out pObject): HRESULT; stdcall;
var
  hInst: HMODULE;
  lRes: Cardinal;
  Msg: UINT;
  pDoc: IHTMLDocument2;
  ObjectFromLresult: TObjectFromLresult;
begin
  Result := False;
  hInst := LoadLibrary('oleacc.dll');
  if hInst <> 0 then
  try
    @ObjectFromLresult := GetProcAddress(hInst, 'ObjectFromLresult');
    if @ObjectFromLresult <> nil then
    begin
      Msg := RegisterWindowMessage('WM_HTML_GETOBJECT');
      if SendMessageTimeOut(WHandle, Msg, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes) <> 0 then
        if ObjectFromLresult(lRes, IHTMLDocument2, 0, pDoc) = S_OK then
        begin
          (pDoc.parentWindow as IServiceprovider).QueryService(
            IWebbrowserApp, IWebbrowser2, IE);
          Result := IE <> nil;
        end;
      end;
  finally
    FreeLibrary(hInst);
  end;
end;

function GetActiveIEServerWindow(const Activate: Boolean=True): HWND;
var
  Wnd, WndChild: HWND;
begin
  Result := 0;
  Wnd := FindWindow('IEFrame', nil); // top level IE
  if Wnd <> 0 then
  begin
    WndChild := FindWindowEx(Wnd, 0, 'Shell DocObject View', nil);
    if WndChild <> 0 then
    begin
      WndChild := FindWindowEx(WndChild, 0, 'Internet Explorer_Server', nil);
      if WndChild <> 0 then
      begin
        Result := WndChild;
        if Activate then
        begin
          if IsIconic(Wnd) then
            ShowWindow(Wnd, SW_RESTORE)
          else
            SetForegroundWindow(Wnd);
        end;
      end;
    end;
  end;
end;

// Method 1
procedure TForm1.Button1Click(Sender: TObject);
const
  navOpenInNewTab = $800;
var
  IEServerWnd: HWND;
  IE: IWebBrowser2;
begin
  IEServerWnd := GetActiveIEServerWindow;
  if (IEServerWnd <> 0) and GetIEFromHWnd(IEServerWnd, IE) then
  begin
    // *** this opens the Default browser, e.g Google Chrome 
    // *** if IE is the Default browser, an empty new window is opened.
    OleVariant(IE).Navigate('http://www.yahoo.com', Longint(navOpenInNewTab));
  end
  else
  begin
    ShellExecute(Handle, 'open', 'iexplore.exe',
    '"http://google.com"',
    nil, SW_RESTORE);
  end;
end;

procedure InternetExplorerNavigate(URL: WideString);
const
  navOpenInNewTab = $800;
var
  IE: OleVariant;
begin
  try
    // *** this always fails (security constraints?)
    IE := GetActiveOleObject('InternetExplorer.Application');
  except
    IE := CreateOleObject('InternetExplorer.Application');
  end;
  IE.Visible := True;
  IE.Navigate(URL, Longint(navOpenInNewTab));
end;

// Method 2
procedure TForm1.Button2Click(Sender: TObject);
begin
  InternetExplorerNavigate('http://google.com');
end;

【问题讨论】:

  • 这不应该归结为用户配置吗?如果我已将其设置为在新窗口中打开,那么我期待新窗口。如果我将其设置为在一个窗口中加载所有选项卡,我也希望如此。

标签: c# c++ delphi internet-explorer winapi


【解决方案1】:

您的方法“1”确实有效。至少在这里,问题在于“Shell DocObject View”窗口不是顶级即窗口的直接子级。在 IE8 中,“Internet Explorer_Server”窗口是“Shell DocObject View”的子窗口,它是“TabWindowClass”的子窗口,“TabWindowClass”是“Frame Tab”的子窗口。如果您可以确认“FindWindowEx”在方法 1 中返回 0,这就是它失败的原因。下面是修改为使用EnumChildWindows的代码:

function EnumChilds(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
  Server = 'Internet Explorer_Server';
var
  ClassName: array[0..24] of Char;
begin
  GetClassName(hwnd, ClassName, Length(ClassName));
  Result := ClassName <> Server;
  if not Result then
    PLongWord(lParam)^ := hwnd;
end;

function GetActiveIEServerWindow(const Activate: Boolean=True): HWND;
var
  Wnd, WndChild: HWND;
begin
  Result := 0;
  Wnd := FindWindow('IEFrame', nil); // top level IE
  if Wnd <> 0 then
  begin

//    WndChild := FindWindowEx(Wnd, 0, 'Shell DocObject View', nil);
//    if WndChild <> 0 then
//    begin
//      WndChild := FindWindowEx(WndChild, 0, 'Internet Explorer_Server', nil);

    WndChild := 0;
    EnumChildWindows(Wnd, @EnumChilds, LongWord(@WndChild));

    if WndChild <> 0 then
    begin
      Result := WndChild;
      if Activate then
      begin
        if IsIconic(Wnd) then
          ShowWindow(Wnd, SW_RESTORE)
        else
          SetForegroundWindow(Wnd);
      end;
    end;
//    end;
  end;
end;

至于方法“2”,我在网上看到一些链接表明 IE 不支持为其活动对象返回引用,但我没有任何官方参考。

【讨论】:

  • 您好,感谢您的回答。我正在使用 IE6/XP 进行测试。所以这里的子窗口可能会有所不同(这不是问题)。顺便说一句,您的默认浏览器是什么?
  • 但是ie6没有标签,800美元对ie6没有意义。我正在使用答案中提到的 ıe8。
  • 我知道,我希望 IE6 会忽略这一点(我也尝试将 0 作为标志传递),并且由于我获得了活动的IHTMLDocument2,我应该能够Navigate 否?
  • @kobik - 看起来很合理。我测试了我在带有 XPSP2 和 Ie6.0.2900.2180(SP2) 的 WM 中发布的代码,现有的 ie 窗口在没有启动新实例的情况下导航到链接,并且该标志根本没有引起任何问题。顺便说一句,您对窗口层次结构是正确的。
  • 好吧,我突然意识到我的 IE6 安装搞砸了。感谢您的确认,EnumChildWindows 确实是要走的路。 +1 并被接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
相关资源
最近更新 更多