Demo 

Introduction 

There are various tools available to monitor Http traffic that is being sent and received from different processes. Fiddler is one such good example. All these programs open a port and filter Http traffic based on process id. But if an C# app consists of multiple browsers they fail to tell which request was sent by which browser.

The C# browser control only provides Navigating and Navigated events and does not give any idea about the requests that it sends (e.g. loading of images etc.).

This article provides an ATL COM Dll that can monitor Http traffic from individual browsers.  

Background   

While working on a project which required the same, I stumbled upon PassThruApp by Igor Tandetnik.

csExWBDLMan.dll ("csExWBDLMan COM library" from The most complete C# Webbrowser wrapper control) is one implementation of PassThru App that provides the requests but does not provide information of redirections and data received in the requests. So, I decided to write a custom code just for monitoring Http traffic based on both PassThru App and csExWBDLMan.dll.

About PassThru App

"It is an object that implements both sides of URL moniker-to-APP communication, that is, it implements both IInternetProtocol and IInternetProtocolSink / IInternetBindInfo. We register it as a temporary handler for a standard protocol, such as HTTP. Now whenever an HTTP request needs to be sent, URL moniker will create an instance of our pAPP and ask it to do the job. The pAPP then creates an instance of a standard APP for the protocol in question (I call it a target APP, or tAPP...) and acts as its client. At this point, our pAPP becomes a proverbial man-in-the-middle. In the simplest case,  any method call made by URL Moniker on pAPP is forwarded to tAPP, and any method call made by tAPP on pAPP is forwarded back to URL Moniker. The pAPP gets to observe, and if desired modify, every bit of information relevant to this request passing back and forth between the moniker and the tAPP. QED" - Igor Tandetnik

The Code

The code extends classes provided by PassThru App. There are two main classes

  1. MonitorSink - MonitorSink extends PassthroughAPP::CInternetProtocolSinkWithSP that implements IInternetProtocolSink
  2. CTestAPP  - CTestAPP extends PassthroughAPP::CInternetProtocol that implements IInternetProtocol
class MonitorSink :
public PassthroughAPP::CInternetProtocolSinkWithSP<MonitorSink>,
    public IHttpNegotiate
{.. 

class CTestAPP :
	public PassthroughAPP::CInternetProtocol<TestStartPolicy>
{..

Now we can intercept requests using 

  1. Request - MonitorSink::BeginningTransaction
  2. Response - MonitorSink::OnResponse
  3. Redirection - MonitorSink::ReportProgress when ulStatusCode from IInternetProtocolSink->ReportProgress(ulStatusCode.. is BINDSTATUS_REDIRECTING
  4. Data received - CTestAPP::Read

But the problem is that we are using Asynchronous Pluggable Protocol and all the requests are done asynchronously. So we get all, but cannot say which response belonged to which request. Moreover the data is received asynchronously in chunks.

The best solution is that if we get unique id for a transaction(i.e. unique id attached request, response and data received) then we will be able to weave the async calls back together.  Here we get lucky 

  1. IInternetBindInfo for a request is most of the times unique and is available in all the methods. But sometimes it is reused by the Browser.
  2. The Url to which request is made is also most likely to be unique. 
So if we create an id from both of them we will get a unique id for a transaction.

Using the code

When you attach your browser with HttpMonitor.dll, on each request, response etc. an event is fired with all the required arguments. There are four events available

  1. OnRequest(int id, string url, string headers, string method, object postData)
  2. OnRedirect(int id, int redirectedId, string url, string redirectedUrl, string responseHeaders, string requestHeaders)
  3. OnDataRecieved(int id, string url, object data, int status)
  4. OnResponse(int id, string url, int responseCode, string headers)
You will first need to navigate to about:blank so that "Internet Explorer_Server" window is available. Then attach the browser using handle of "Internet Explorer_Server window".

if (monitor == null)
{
                monitor = new HttpMonitorLib.HttpMonClass();
                monitor.IEWindow = GetTopWindow(GetTopWindow(GetTopWindow(webBrowser1.Handle))).ToInt32();
                monitor.OnRequest += new HttpMonitorLib._IHttpMonEvents_OnRequestEventHandler(monitor_OnRequest);
.
.

For example, the following function will be executed whenever the browser sends a request.

private void monitor_OnRequest(int id, string url, string headers, string method, object postData)
{
//code here
}   

The id specifies unique id associated with that particular Http transaction.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"