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 解决方案来证明这一困难。

4

1 回答 1

2

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

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

C# 可能仅适用于它拥有的其他隐藏窗口,但是您已经设置了该应用程序的其余部分。

于 2010-10-12T22:08:13.193 回答