"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken
"Valter Minute" <vmi...@REMOVEMEinwind.it> wrote in message
news:Xns929C7FA7B95...@207.46.239.39...
> What do you (and other experts, of course) think about this?
> Have you got good references (articles, samples) on implementing a
> "wrapper" protocol that changes HTTP headers and calls the original
> http protocol handler?
I have never tried implementing IHttpNegotiate on the host object of the
WebBrowser control (and I doubt this is possible) but I have successfully
implemented IHttpNegotiate on my IBindStatusCallback implementation. Note
that in this case IHttpNegotiate is requested using a normal
QueryInterface( ), so you don't need to implement the IServiceProvider
interface.
I believe you can obtain MSHTML's IBindHost interface, and pass your
IBindStatusCallback to the IBindHost::MonikerBindToXXX( ) functions (though
this is not how I use my IBindStatusCallback interface; I use it on a URL
moniker I have created myself through CreateURLMoniker( )).
If it can help anyone, here's the relevant parts of the code I use to
implement IBindStatusCallback and IHttpNegotiate (using ATL):
template< class T, int iBindFlags >
class __declspec(novtable) CAsyncDownloaderT :
public ATL::CBindStatusCallback< T, iBindFlags >,
public IHttpNegotiate
{
typedef CAsyncDownloaderT< T, iBindFlags > thisClass;
typedef CBindStatusCallback< T, iBindFlags > baseClass;
public:
CAsyncDownloaderT(void)
: m_pfn( NULL ),
m_dwResponseCode( 0 ),
m_bOfflineIfNotConnected( false )
{
}
BEGIN_COM_MAP( thisClass )
COM_INTERFACE_ENTRY( IHttpNegotiate )
COM_INTERFACE_ENTRY_CHAIN( baseClass )
END_COM_MAP()
[snipped]
// IBindStatusCallback overrides
public:
STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR /*szError*/)
{
[snipped]
return S_OK;
}
STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *
/*pformatetc*/, STGMEDIUM *pstgmed)
{
[snipped]
return hr;
}
STDMETHOD(GetBindInfo)(DWORD * pgrfBINDF, BINDINFO * pbindInfo)
{
[snipped]
return hr;
}
// IHttpNegotiate
public:
STDMETHOD(BeginningTransaction)(LPCWSTR /*pszURL*/, LPCWSTR /*pszHeaders*/,
DWORD /*dwReserved*/, LPWSTR * pszAdditionalHeaders)
{
[snipped]
return S_OK;
}
STDMETHOD(OnResponse)(DWORD dwResponseCode, LPCWSTR /*pszResponseHeaders*/,
LPCWSTR /*pszRequestHeaders*/, LPWSTR * /*pszAdditionalRequestHeaders*/)
{
[snipped]
return S_OK;
}
private:
[snipped]
};
--
Ehsan Akhgari
List Owner: MSVC@*NOSPAMWANTED*BeginThread.com
[Email: ehsan@*NOSPAMWANTED*beginthread.com]
[WWW: http://www.beginthread.com/Ehsan ]
"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken
"Ehsan Akhgari" <Eh...@REMOVETHISTOKENbeginthread.com> wrote in message
news:#VTI7IvaCHA.1688@tkmsftngp09...
> Temporary APP should be fine. I mean the one you install with
> IInternetSession::RegisterNameSpace
I'm trying it this way.
I made a COM object that uses aggregation to expose all the
functionalities of the HttpProtocol object and I'm able to intercept
the calls to IInternetProtocol. Now I've to change the headers inside
the Start call and parse them before I return them to the browser.
--
Valter Minute
vmi...@inwind.it (the reply address of this message is invalid)
Valter,
This is not relevant to your original question, but if you #define the
symbol _ATL_DEBUG_QI before #including any ATL header, ATL will
automatically log *all* the QI calls to the debugger's output window. Of
course there is no such method for dumping out QS calls that I'm aware of.
Just thought I would mention this.
Like I said, I don't use it directly with a WebBrowser control Here's a
rough model of what I do:
1. CreateURLMoniker( ) to create a moniker.
2. CreateAsyncBindCtx( ) to create a bind context (here I pass my
implementation of IBindStatusCallback which returns an IHttpNegotiate
pointer from QI.
3. Create a format enumerator and registering it on the bind context (not
relevant on the whole process).
4. Call IMoniker::BindToStorage( ) using the moniker created in step 1, and
a stream pointer.
5. Collect the data inside the IBindStatusCallback::OnDataAvailable( ).
6. QI MSHTML's document for IPersistStreamInit, and load the data using
IPersistStreamInit::InitNew( ) and IPersistStreamInit::Load( ).
7. Then I get a notification of the end of the load job inside my
implementation of IPropertyNotifySink::OnChanged( ) looking for
DISPID_READYSTATE changes until the ready state equals READYSTATE_COMPLETE.
Note that this step does not include any downloading of the data, since it's
already downloaded at step 5. But you should not skip this part because it
might take some time for MSHTML to parse the document, and the whole DOM
might not be available right after the call to IPersistStreamInit::Load( ).
Maybe this is too much to do, but it's the only way I know of to control the
IHttpNegotiate when using MSHTML/WebBrowser control. Besides, I've never
seen any documentations to state that MSHTML or the WebBrowser control query
their hosts for this interface, because this interface is part of the URL
moniker architecture.
Hope this helps.
I create an ATL object that uses COM aggregation to "wrap" the
HttpProtocol object.
Using ATL it's quite simple to create this kind of objects.
I've to re-implement (mostly with "boilerplate" code) only the
IInternetProtocol interface.
This is the declaration of my class:
class ATL_NO_VTABLE CHttpWrapper :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CHttpWrapper, &CLSID_HttpWrapper>,
public IHttpWrapper,
public IInternetProtocol
And here's its ATL COM map
BEGIN_COM_MAP(CHttpWrapper)
COM_INTERFACE_ENTRY(IHttpWrapper)
COM_INTERFACE_ENTRY(IInternetProtocol)
COM_INTERFACE_ENTRY(IInternetProtocolRoot)
COM_INTERFACE_ENTRY_AGGREGATE_BLIND(httpunk)
END_COM_MAP()
httppunk is a pointer to the IUnknown interface of the HttpProtocol
object.
I found that using CoCreateInstance for this object's class didn't work
(I got a pointer with an invalid virtual functions table!?), so I had
to use its ClassFactory.
Here's the code I use to create an HttpProtocol instance (taken from
the FinalConstruct method of CHttpWrapper):
HRESULT hr;
CComPtr<IClassFactory> cfptr;
if (FAILED(hr=CoGetClassObject(CLSID_HttpProtocol,
CLSCTX_ALL,
NULL,
IID_IClassFactory,(void**)&cfptr)))
return hr;
if (FAILED(hr=cfptr->CreateInstance(GetControllingUnknown(),
IID_IUnknown,(void**)&httpunk)))
return hr;
if (FAILED(hr=httpunk->QueryInterface(IID_IInternetProtocol,
(void**)&ipptr)))
return hr;
Then I had to "override" the Start method of the IInternetProtocolRoot
interface to insert another "wrapper" around the IInternetProtocolSink
object provided by the web-browser control.
All the other members of IInternetProtocol and IInternetProtocolRoot
are implemented by simply calling the corrisponding method on the
pointer to the "wrapped" object kept by my class.
here's the code of IInternetProtocolRoot::Start :
CComObject<CInternetProtocolSinkWrapper>* psptr=new CComObject
<CInternetProtocolSinkWrapper>();
psptr->setPsPtr(pOIProtSink);
return ipptr->Start(szUrl,psptr,pOIBindInfo,grfPI,dwReserved);
The "sink wrapper" is implemented as a com "pseudo" aggregate. I've to
wrap an existing pointer so I can't use COM aggregation and I've to
rely on the fact that the protocol object keeps a reference to the
interface passed to it. This breaks COM rules about reference counting
and QueryInterface implementation but the alternative is a complete re-
implementation (through boilerplate code) of all the interfaces
supported by the original object and that the "wrapper" could have
problems working on different release of IE (the browser and HTTP
protocol are "tightly coupled"...).
Here's the wrapper declaration:
class CInternetProtocolSinkWrapper :
public IInternetProtocolSink,
public IServiceProvider,
public IHttpNegotiate,
public CComObjectRoot,
public CComCoClass<CInternetProtocolSinkWrapper,
&CLSID_InternetProtocolSinkWrapper>
and its COM map:
BEGIN_COM_MAP(CInternetProtocolSinkWrapper)
COM_INTERFACE_ENTRY(IInternetProtocolSink)
COM_INTERFACE_ENTRY(IServiceProvider)
COM_INTERFACE_ENTRY(IHttpNegotiate)
COM_INTERFACE_ENTRY_AGGREGATE_BLIND(unkptr)
END_COM_MAP()
the setSpPtr method (used to store a pointer to the original sink
object):
void setPsPtr(IInternetProtocolSink* psptr)
{
this->psptr=psptr;
psptr->AddRef();
psptr->QueryInterface(IID_IUnknown,(void**)&unkptr);
psptr->QueryInterface(IID_IServiceProvider,(void**)&spptr);
}
I've to ovverride only the QueryService method of IServiceProvider and
both methods of IHttpNavigate (to provide and parse my headers).
In QueryService I store a pointer to the "original" IHttpNavigate (I
need to append my headers to IE request without removing its original
headers).
Here's the code (I hope it's better than my poor english):
if (InlineIsEqualGUID(riid,IID_IHttpNegotiate))
{
CComPtr<IUnknown> hnunkptr;
spptr->QueryService(guidService,riid,(void**)&hnunkptr);
if (hnptr)
{
hnptr->Release();
hnptr=NULL;
}
hnunkptr->QueryInterface(IID_IHttpNegotiate,(void**)&hnptr);
return this->QueryInterface,IID_IHttpNegotiate,ppvObject);
}
return spptr->QueryService(guidService,riid,ppvObject);
Inside my application I've to register my wrapper as protocol handler
for the http namespace:
HRESULT hr;
CComPtr<IInternetSession> sesptr;
if (FAILED(hr=CoInternetGetSession(0L,&sesptr,0L)))
return hr;
CComPtr<IClassFactory> cfptr;
if (FAILED(hr=CoGetClassObject(CLSID_HttpWrapper,
CLSCTX_ALL,
NULL,IID_IClassFactory,
(void**)&cfptr)))
return hr;
if (FAILED(hr=sesptr->RegisterNameSpace(cfptr,
CLSID_HttpWrapper,
L"http",
0,NULL,0)))
return hr;
Thanks to Igor Tandetnik and Ehsan Akhgari for their helpful
suggestions.
"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken
"Ehsan Akhgari" <Eh...@REMOVETHISTOKENbeginthread.com> wrote in message
news:eZUG5g4aCHA.2232@tkmsftngp08...
Yes, you're correct in this regard.
> Also, this way you don't have control over secondary downloads that
> MSHTML may initiate while partsing the page, such as images for <img>
> tags, do you?
Yes, that is also true, but it all fits my purpose. All I needed to do was
writing a tiny interface upon MSHTML to create an HTML parser. So, I don't
need to download any images or any kind of secondary files. Also, because
the parser is UI-less, I don't worry about relative URLs. In some handful
of situations I need to handle relative URLs inside the document I insert a
<base> tag, but this is not the case most of the time.
My solution is not really a perfect solution for a browser with UI which
needs to download all external files like images and scripts, and internal
frames. But it works for my case, and besides it's the only way I've found
to get control of IHttpNegotiate (which was one of my biggest requirements).