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.