下沉 DWebBrowserEvents2 事件似乎会挂起程序化导航

发布于 2024-09-27 04:22:42 字数 2804 浏览 9 评论 0原文

我在 MSDN 论坛上发布了这个问题,但我的经验是 Stack Overflow 上的答案质量更好,所以我也在这里发布。正如我之前多次发布的,我正在开发一个浏览器自动化框架,从外部进程自动化 Internet Explorer。我的架构如下:我有一个服务器,它打开一个命名管道,我的自动化客户端将命令推送到该命名管道。服务器解释命令,并在 IWebBrowser2 对象上执行它们,我已将其包装在我自己的 C++ 类中。一切正常,直到我尝试在 IE 实例上接收事件。我的包装器类实现了 IDispEventSimpleImpl,但是当我尝试接收事件时,浏览器实例不会以编程方式或通过 UI 响应任何通信。以下是我最相关的两个主要方法:

void BrowserManager::Start(void)
{
  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  std::basic_string<TCHAR> pipeName =L"\\\\.\\pipe\\managerpipe";
  HANDLE hPipe = ::CreateNamedPipe(pipeName.c_str(), 
    PIPE_ACCESS_DUPLEX, 
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 
    PIPE_UNLIMITED_INSTANCES,
    1024,
    1024,
    0,
    NULL);

  if (hPipe == INVALID_HANDLE_VALUE)
  {
    DWORD dwError = ::GetLastError();
  }

  this->m_isRunning = true;

  while (this->m_isRunning)
  {
    BOOL result = ::ConnectNamedPipe(hPipe, NULL);
    std::vector<CHAR> inputBuffer(1024);
    DWORD bytesRead = 0;
    ::ReadFile(hPipe, &inputBuffer[0], 1024, &bytesRead, NULL);

    std::string command = &inputBuffer[0];
    std::string response = DispatchCommand(command);

    std::vector<CHAR> outputBuffer(response.begin(), response.end());
    ::WriteFile(hPipe, &outputBuffer[0], outputBuffer.size(), &bytesRead, NULL);
    ::FlushFileBuffers(hPipe);
    ::DisconnectNamedPipe(hPipe);

    if (strcmp(command.c_str(), "quit\r\n") == 0)
    {
      this->m_isRunning = false;
    }
  }

  ::CloseHandle(hPipe);
  CoUninitialize();
}

std::string BrowserManager::DispatchCommand(std::string command)
{
  std::string response;
  if (strcmp(command.c_str(), "start\r\n") == 0)
  {
    // Launch the browser process using CreateProcess on XP 
    // or IELaunchURL on Vista or higher. This is done on a
    // low-integrity thread so we have the correct integrity.
    DWORD procId = this->m_factory->LaunchBrowserProcess();
    CComPtr<IWebBrowser2> pBrowser(this->m_factory->AttachToBrowser(procId));
    BrowserWrapper wrapper(pBrowser);
    this->m_wrapper = wrapper;
    response = "started";
  }
  else if (strcmp(command.c_str(), "goto\r\n") == 0)
  {
    this->m_wrapper.GoToUrl("http://www.google.com/");
    response = "navigated";
  }
  else if (strcmp(command.c_str(), "quit\r\n") == 0)
  {
    this->m_wrapper.CloseBrowser();
    response = "closed";
  }
  else
  {
    response = "invalid command";
  }

  return response;
}

有趣的是,我在将其转换为非托管 C++ 之前在 C# 中构建了相同的机制原型,以确保我尝试的方法能够发挥作用,因为我的 C++ 技能与我的 C# 技能不在同一水平。 。不用说,它在 C# 中工作得很好,但要求该组件用非托管代码编写。我确信我忽略了 .NET Framework 抽象出来的一些明显的东西,但无论它是什么,它对我来说并不明显。

为了帮助我从错误中吸取教训,我希望您能指出 .NET Framework 正在做什么来让这项工作正常进行。在 C# 版本中,我在管道上使用带有阻塞 I/O 的单线程,就像我(认为我)在这里一样。如果发布的代码片段不足以指出诊断,我非常乐意提供完整的 Visual Studio 2008 解决方案来演示这一困难。

I posted this question on the MSDN forums, but my experience has been a better quality of answer here on Stack Overflow, so I'm posting here as well. As I've posted several times before, I'm working on a browser automation framework, automating Internet Explorer from an external process. My architecture is as follows: I have a server which opens a named pipe that my automation client pushes commands to. The server interprets the commands, and executes them on the IWebBrowser2 object, which I have wrapped in my own C++ class. Everything works fine until I try to sink events on the IE instance. My wrapper class implements IDispEventSimpleImpl, but when I try to sink the events, the browser instance doesn't respond to any communication, either programmatically or via the UI. Here are my main two methods with the most relevance:

void BrowserManager::Start(void)
{
  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  std::basic_string<TCHAR> pipeName =L"\\\\.\\pipe\\managerpipe";
  HANDLE hPipe = ::CreateNamedPipe(pipeName.c_str(), 
    PIPE_ACCESS_DUPLEX, 
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 
    PIPE_UNLIMITED_INSTANCES,
    1024,
    1024,
    0,
    NULL);

  if (hPipe == INVALID_HANDLE_VALUE)
  {
    DWORD dwError = ::GetLastError();
  }

  this->m_isRunning = true;

  while (this->m_isRunning)
  {
    BOOL result = ::ConnectNamedPipe(hPipe, NULL);
    std::vector<CHAR> inputBuffer(1024);
    DWORD bytesRead = 0;
    ::ReadFile(hPipe, &inputBuffer[0], 1024, &bytesRead, NULL);

    std::string command = &inputBuffer[0];
    std::string response = DispatchCommand(command);

    std::vector<CHAR> outputBuffer(response.begin(), response.end());
    ::WriteFile(hPipe, &outputBuffer[0], outputBuffer.size(), &bytesRead, NULL);
    ::FlushFileBuffers(hPipe);
    ::DisconnectNamedPipe(hPipe);

    if (strcmp(command.c_str(), "quit\r\n") == 0)
    {
      this->m_isRunning = false;
    }
  }

  ::CloseHandle(hPipe);
  CoUninitialize();
}

std::string BrowserManager::DispatchCommand(std::string command)
{
  std::string response;
  if (strcmp(command.c_str(), "start\r\n") == 0)
  {
    // Launch the browser process using CreateProcess on XP 
    // or IELaunchURL on Vista or higher. This is done on a
    // low-integrity thread so we have the correct integrity.
    DWORD procId = this->m_factory->LaunchBrowserProcess();
    CComPtr<IWebBrowser2> pBrowser(this->m_factory->AttachToBrowser(procId));
    BrowserWrapper wrapper(pBrowser);
    this->m_wrapper = wrapper;
    response = "started";
  }
  else if (strcmp(command.c_str(), "goto\r\n") == 0)
  {
    this->m_wrapper.GoToUrl("http://www.google.com/");
    response = "navigated";
  }
  else if (strcmp(command.c_str(), "quit\r\n") == 0)
  {
    this->m_wrapper.CloseBrowser();
    response = "closed";
  }
  else
  {
    response = "invalid command";
  }

  return response;
}

Interestingly, I prototyped this same mechanism in C# before translating it into unmanaged C++ to make sure what I was attempting would work, since my C++ skills are not at the same level as my C# skills. Needless to say, it works fine in C#, but it is a requirement that this component be written in unmanaged code. I'm sure I'm overlooking something obvious that the .NET Framework abstracts away, but whatever it is, it isn't obvious to me.

To help me learn from my mistake, I'd appreciate a pointer to what it is that the .NET Framework is doing to let this work. In the C# version, I'm using a single thread with blocking I/O on the pipe, just like I (think I) am here. If the posted code snippet isn't enough to point toward a diagnosis, I'm more than happy to provide a full Visual Studio 2008 solution demonstrating the difficulty.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雄赳赳气昂昂 2024-10-04 04:22:42

通过提供事件接收器,您的应用程序正在成为一个 com 服务器。 com 应用程序需要活动的“消息泵”。

如果您在执行管道/调度命令时阻止消息泵,那么它将阻止 IE 调用您的事件接收器。

C# 可能仅适用于它具有的其他隐藏窗口,无论您如何设置该应用程序的其余部分。

Your app is becoming a com server by providing event sinks. The com app needs active 'message pump'(s).

If your blocking the message pump while your doing your pipe/dispatch command then it will block IE from calling on your event sink.

C# may work simply for the other hidden windows it has and however you have set up the rest of that app.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文