PassThroughAPP: How to modify data (and its size) in IInternetProtocol::Read
-
Friday, December 30, 2011 5:17 AM
I have implemented a pluggable protocol using PassThoughApp code, and registered it in IE using a BHO for http and https protocols. I need it to do decompression of data received from server. I know I need to modify the data received in IInternetProtocol::Read() and change its overall size. What all do I need to change in my implementation of protocol handler besides Read()?
I have set a pszAdditionalHeaders in BeginningTransaction to indicate to server that it can send compressed data, this works fine. (I couldn't find a way to modify accept-encoding header, so I just added another custom header).
Now, I need to buffer the data received in IInternetProtocol::Read, decompress it after I receive the whole file, and then send back decompressed data in subsequent read calls. I think I am failing because of my implementation of ReportData(); or something similar.
In Read calls, I am able to buffer all data in temp files by setting out value (*pcbRead)=0; and returning S_OK instead of S_FALSE while receiving data from underlying sink. Then I return data in subsequent read calls by setting *pcbRead and not returning S_OK till done (I am not decompressing yet, to keep it simple. Only buffering data, and then returning it from buffer after I have the full file, but even this is failing).
I can see the data come, get buffered, and reach IE from my buffer but sometimes IE gets confused and either doesn't ask for whole data or gets stuck in some internal loop. Most likely I need to update other parts of protocol to be in sync with my changes, I think one such place is ReportData but there might be more.
It seems ReportData gets called by sink's Read() calls and I can't find out how to modify it such that it doesn't confuse IE as it seems to be telling IE that all data is received/available before I want it to.
I also can't tell what other parts of my protocol's implementation need to be updated for this. And once the buffering works, I will need a way to update the Content-Length value received from server in response header (or set it to zero?) so that IE keeps asking for data till I tell 'stop' in Read() by returning S_FALSE. (I tried updating Content-Length header value in OnResponse before calling sink's OnResponse but this doesn't work.)
Can someone please guide me a bit on this?
All Replies
-
Friday, December 30, 2011 5:38 AM
Also, if there is some alternate way to do what I am trying to do, please tell me about that too. (I hope Igor Tandetnik reads this).
In a 2006 post, Igor mentions:
It requires careful coordination between your passthrough protocol and sink to fool the client into accepting altered data and/or headers.
I can't find the details of this coordination anywhere, or any alternate way to achieve the same goal.
-
Friday, December 30, 2011 5:42 AM
SachinGarg wrote:
I have implemented a pluggable protocol using PassThoughApp code, and registered it in IE using a BHO for http and https
protocols. I need it to do decompression of data received from server. I know I need to modify the data received in
IInternetProtocol::Read() and change its overall size. What all do I need to change in my implementation of protocol handler
besides Read()?ReportData. See this thread:
http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thre ad/a7ab3ecc-633c-4a9a-8290-bd6f34c41ada
The part that might interest you starts from a message dated August 26.
I have set a pszAdditionalHeaders in BeginningTransaction to indicate to server that it can send compressed data, this works
fine. (I couldn't find a way to modify accept-encoding header, so I just added another custom header).You should be able to specicy Accept-Encoding in pszAdditionalHeaders, just like any other header. Your value should take precedence over the default one. I haven't tried this myself though.
Igor Tandetnik
- Marked As Answer by SachinGarg Monday, January 02, 2012 12:09 PM
-
Friday, December 30, 2011 6:04 AM
ReportData. See this thread:
http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thre ad/a7ab3ecc-633c-4a9a-8290-bd6f34c41ada
The part that might interest you starts from a message dated August 26.
Thanks a lot for the quick reply.
As I understand it, original PassThrough calls ReportData inside ReportData and Read inside Read. I will need to update this to call Read repeatedly inside ReportData (and prepare my temp file there). Then call my ReportData at end of ReportData after I have all the data ready. Now my Read will be called where I just return more data from my temp file in each call.
Am I correct when I say I can call Read repeatedly inside a single ReportData call? Or maybe I need to take care of values reported in its parameters. And do I need to call my ReportData only once (with final filesize) and then it will call my Read repeatedly (with a small buffer each time)?
I have set a pszAdditionalHeaders in BeginningTransaction to indicate to server that it can send compressed data, this works
fine. (I couldn't find a way to modify accept-encoding header, so I just added another custom header).You should be able to specicy Accept-Encoding in pszAdditionalHeaders, just like any other header. Your value should take precedence over the default one. I haven't tried this myself though.
No, this doesn't work for Accept-Encoding. I could specify other headers this way, but it seems Accept-Encoding is already set in stone before this and attempts to replace it using pszAdditionalHeaders are ignored.
SG
- Edited by SachinGarg Friday, December 30, 2011 6:05 AM typo
-
Friday, December 30, 2011 12:11 PM
I tried implementing this but there seems to be something missing.
If I only call Read once inside first call to Reportdata, then ReportData is not called again - as if it is waiting for something to happen. With default PassThroughApp ReportData gets called multiple times.
If all the data is Read within first call of ReportData in a while loop, and then ReportData is also called within that first call (with BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE flags), then our Read is only called once, when I would expect the Read to be called repeatedly till all the data is returned.
Do I need to do something specific inside ReportData or Read? Like maybe call ReportProgress? or return specific values? (I am returning S_OK from ReportData and Read. And returning S_FALSE from Read after entire file is read).
-
Friday, December 30, 2011 7:28 PM
SachinGarg wrote:
Am I correct when I say I can call Read repeatedly inside a single ReportData call?
You should always call Read in a loop, for as long as it returns S_OK. Stop when it returns S_FALSE (you've gotten all the data), E_PENDING (you've gotten all the data currently available, but more is expected later; when it arrives, ReportData will be called again) or any other error code (an error occurred).
And do I need to call my ReportData only once (with final filesize) and then it will call my Read
repeatedly (with a small buffer each time)?It's the same protocol, viewed from the other side. You call ReportData whenever you have some data to report. The client will turn around and call Read on you repeatedly, until you return S_FALSE, E_PENDING or an error. If you return E_PENDING, the client will expect another ReportData call from you some time later.
In the simplest case, yes, you may call ReportData only once, after you've collected and prepared all the data. More advanced scenarios, where you are decompressing and reporting data on the fly as it arrives, are also possible.
Igor Tandetnik
- Marked As Answer by SachinGarg Monday, January 02, 2012 12:09 PM
-
Friday, December 30, 2011 7:30 PM
SachinGarg wrote:
If all the data is Read within first call of ReportData in a while loop, and then ReportData is also called within that first
call (with BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE flags), then our Read is only called
once, when I would expect the Read to be called repeatedly till all the data is returned.What HRESULT are you returning from Read? Are you returning S_FALSE on the very first call, by any chance?
Do I need to do something specific inside ReportData or Read? Like maybe call ReportProgress? or return specific values? (I am
returning S_OK from ReportData and Read. And returning S_FALSE from Read after entire file is read).Double-check this part.
Igor Tandetnik
-
Friday, December 30, 2011 9:46 PM
I checked everything you mentioned, its not working - for images. For example, if I visit google.com, the page loads but the big google logo in center is 'half' drawn. If I enter URL of a PNG image directly, then ReportData gets called, I am able to retrieve full image in a loop, but after I call ReportData, my Read never gets called. I am not doing any decompression etc yet, only saving data to temp file, and reading from it. I hope it is something trivial that I am missing, here is my code:
STDMETHODIMP CTestAPP::Read(void *pv, ULONG cb, ULONG *pcbRead) { USES_CONVERSION; HRESULT hr; long UniqueId=0; //when loading single image, otherwise use a ID for each transaction //read max=cb bytes from temp file, and move offset (*pcbRead) = min(cb,decompressed_size[UniqueId]-decompressed_read_offset[UniqueId]); std::ifstream fin(GetUniqueFileName(UniqueId).c_str(),std::ios::binary); fin.seekg(decompressed_read_offset[UniqueId]); fin.read((char*)pv,(*pcbRead)); fin.close(); decompressed_read_offset[UniqueId]+=(*pcbRead); //if we are done reading if(decompressed_read_offset[UniqueId]==decompressed_size[UniqueId]) { hr=S_FALSE; //delete file here decompressed_read_offset[UniqueId]=decompressed_size[UniqueId] = 0; } else { hr=S_OK; } return hr; } STDMETHODIMP MonitorSink::ReportData( DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax) { char pv[2048]; DWORD cbRead; long UniqueId=0;
if(decompressed_size.find(UniqueId)==decompressed_size.end()) decompressed_size[UniqueId] = 0; HRESULT hr = S_OK; while (/*decompressed_size[UniqueId] < ulProgress &&*/ hr == S_OK) { //read data while available hr = m_spTargetProtocol->Read(pv, 2048, &cbRead); AppendData(GetUniqueFileName(UniqueId),pv,cbRead); decompressed_size[UniqueId] += cbRead; } if (hr == S_FALSE || (grfBSCF & BSCF_LASTDATANOTIFICATION)) { // at end //decompress here decompressed_read_offset[UniqueId]=0; hr = m_spInternetProtocolSink ? m_spInternetProtocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION /*| BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE*/, decompressed_size[UniqueId], decompressed_size[UniqueId]): E_UNEXPECTED; } else { hr = S_OK; } return hr; }
- Edited by SachinGarg Friday, December 30, 2011 9:49 PM
-
Friday, December 30, 2011 11:29 PMIt seems that MY read gets called only if inside ReportData, I call ReportData before calling Read. Does this give any clue on what might be going on here?
-
Friday, December 30, 2011 11:44 PM
OK, I have more information on this. If I 'forcefully' abort the read loop in ReportData before the call which will return S_FALSE, it all works fine (I took one 9kb test image which was getting read in 5 iterations of 2kb each, I 'break'ed out of loop after iteration 4). It seems like once the Read call has returned S_FALSE, it is doing something inside which makes the browser ignore any subsequent ReportData calls, and then our Read is never called.
This also explains why I was seeing 'half' images earlier. What I can't tell is how to handle this behavior. What do you think of this? Or is this a hint that I am doing something else wrong somewhere else?
- Edited by SachinGarg Friday, December 30, 2011 11:44 PM
- Edited by SachinGarg Saturday, December 31, 2011 12:28 AM
-
Saturday, December 31, 2011 12:56 AM
SachinGarg wrote:
I checked everything you mentioned, its not working - for images. For example, if I visit google.com, the page loads but the big
google logo in center is 'half' drawn. If I enter URL of a PNG image directly, then ReportData gets called, I am able to retrieve
full image in a loop, but after I call ReportData, my Read never gets called. I am not doing any decompression etc yet, only
saving data to temp file, and reading from it. I hope it is something trivial that I am missing, here is my code:[code]
STDMETHODIMP CTestAPP::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
USES_CONVERSION;
HRESULT hr;
long UniqueId=0; //when loading single image, otherwise use a ID for each transactionYou do realize that multiple requests may, and routinely do, proceed in parallel, right? Looking at your code, it seems that, whenever that happens, requests are going to stomp all over each other.
Igor Tandetnik
-
Saturday, December 31, 2011 1:01 AM
You do realize that multiple requests may, and routinely do, proceed in parallel, right? Looking at your code, it seems that, whenever that happens, requests are going to stomp all over each other.
The UniqueId will be unique for each request. I simplified code for current debugging, to test it with only one image load request - and remove any possible bugs due to my non-app code. I hope there is nothing else in code that is thread-unsafe.
Edit: Note my post on ReportData getting ignored after underlying Read returns S_FALSE.- Edited by SachinGarg Saturday, December 31, 2011 1:01 AM
-
Saturday, December 31, 2011 1:11 AM
SachinGarg wrote:
I checked everything you mentioned, its not working - for images. For example, if I visit google.com, the page loads but the big
google logo in center is 'half' drawn. If I enter URL of a PNG image directly, then ReportData gets called, I am able to retrieve
full image in a loop, but after I call ReportData, my Read never gets called.This doesn't add up. If your Read never gets called at all, then the browser isn't receiving any portion of the data. Where would the half-drawn image come from, then?
Clear the browser's cache. I suspect you may have a half-downloaded image left over in the cache from your earlier experiments.
Igor Tandetnik
-
Saturday, December 31, 2011 1:15 AM
SachinGarg wrote:
I checked everything you mentioned, its not working - for images. For example, if I visit google.com, the page loads but the big
google logo in center is 'half' drawn. If I enter URL of a PNG image directly, then ReportData gets called, I am able to retrieve
full image in a loop, but after I call ReportData, my Read never gets called.This doesn't add up. If your Read never gets called at all, then the browser isn't receiving any portion of the data. Where would the half-drawn image come from, then?
Clear the browser's cache. I suspect you may have a half-downloaded image left over in the cache from your earlier experiments.
Reposting this, in case my previous post isn't visible:
If I 'forcefully' abort the read loop in ReportData before the call which will return S_FALSE, it all works fine (I took one 9kb test image which was getting read in 5 iterations of 2kb each, I 'break'ed out of loop after iteration 4). It seems like once the Read call has returned S_FALSE, it is doing something inside which makes the browser ignore any subsequent ReportData calls, and then our Read is never called.
This also explains why I was seeing 'half' images earlier. What I can't tell is how to handle this behavior. What do you think of this? Or is this a hint that I am doing something else wrong somewhere else?
-
Saturday, December 31, 2011 1:21 AM
SachinGarg wrote:
If I 'forcefully' abort the read loop in ReportData before the call which will return S_FALSE, it all works fine (I took one 9kb
test image which was getting read in 5 iterations of 2kb each, I 'break'ed out of loop after iteration 4).I don't understand how that could "work fine". It seems that, in this arrangement, you don't read the last 1K of the image, so again it never reaches the browser. How can the browser draw the image in full, then?
It seems like once the
Read call has returned S_FALSE, it is doing something inside which makes the browser ignore any subsequent ReportData calls, and
then our Read is never called.This shouldn't really be necessary, but try intercepting ReportResult call. That is, don't forward it right away, but instead call ReportResult on the client only after all the data is read.
Igor Tandetnik
- Marked As Answer by SachinGarg Monday, January 02, 2012 12:11 PM
-
Saturday, December 31, 2011 1:23 AM
By 'works fine', I only mean that our Read is then getting called. You are right that its not all fine :-)
I am trying to intercept ReportResult right now.
-
Saturday, December 31, 2011 1:38 AM
SachinGarg wrote:
If I 'forcefully' abort the read loop in ReportData before the call which will return S_FALSE, it all works fine (I took one 9kb
test image which was getting read in 5 iterations of 2kb each, I 'break'ed out of loop after iteration 4).I don't understand how that could "work fine". It seems that, in this arrangement, you don't read the last 1K of the image, so again it never reaches the browser. How can the browser draw the image in full, then?
It seems like once the
Read call has returned S_FALSE, it is doing something inside which makes the browser ignore any subsequent ReportData calls, and
then our Read is never called.This shouldn't really be necessary, but try intercepting ReportResult call. That is, don't forward it right away, but instead call ReportResult on the client only after all the data is read.
Igor Tandetnik
Wohoo. Interceptng ReportResult fixes it. I am now only saving values passed to ReportResult by the default Read, and will later call it at end in my Read with those values.
Thanks a ton for your time :)
Now lets see how far I go with other challenges - especially returning a different number of bytes than what is reported by content-length in response headers. I had already tried modifying the response headers in my OnReponse before calling the default OnResponse. But I can't tell if will help or will be ignored. If all else fails, I might have to get the server updated to not send any content-length header. If you have any thoughts to share on this, please let me know. It might save me many hours later.
- Edited by SachinGarg Saturday, December 31, 2011 1:39 AM
-
Saturday, December 31, 2011 1:44 AM
SachinGarg wrote:
Now lets see how far I go with other challenges - especially returning a different number of bytes than what is reported by
content-length in response headers.It doesn't matter - the browser doesn't look at Content-Length. For one thing, if the response is gzip compressed, Content-Length reports the compressed length, while the browser only sees uncompressed data. For that and other reasons, the header is useless to the browser, and the browser instead relies on values passed to ReportData (purely for the purpose of drawing a nice progress bar) and Read returning S_FALSE (for the purpose of actually detecting when the data is all there).
Igor Tandetnik
-
Saturday, December 31, 2011 2:00 AM
OK. Thanks.SachinGarg wrote:
Now lets see how far I go with other challenges - especially returning a different number of bytes than what is reported by
content-length in response headers.It doesn't matter - the browser doesn't look at Content-Length. For one thing, if the response is gzip compressed, Content-Length reports the compressed length, while the browser only sees uncompressed data. For that and other reasons, the header is useless to the browser, and the browser instead relies on values passed to ReportData (purely for the purpose of drawing a nice progress bar) and Read returning S_FALSE (for the purpose of actually detecting when the data is all there).
-
Tuesday, January 03, 2012 9:17 AMIs there some way to register the protocol for IE without a BHO? I need to let non-admin users install and use the protocol in IE, but it seems BHOs can't be installed without admin rights. I am able to 'register' the Protocol and BHO DLLs without admin rights, but IE seems to be ignoring BHO's registered in HKCU.
- Edited by SachinGarg Tuesday, January 03, 2012 9:18 AM
-
Tuesday, January 03, 2012 12:48 PM
SachinGarg wrote:
Is there some way to register the protocol for IE without a BHO?
You need some way to get your DLL into the browser's process. See if any of these techniques works for you:
http://en.wikipedia.org/wiki/DLL_injection
Igor Tandetnik
-
Tuesday, January 03, 2012 2:41 PM
You need some way to get your DLL into the browser's process. See if any of these techniques works for you:
Can you recommend any one of these techniques? Maybe something that you know works for IE or has a good chance to work? If I am able to inject my DLL in IE process (and thats a big IF), can I register the protocol right away, or do I need to somehow find out and wait for the browser session to start?
What do you think about using an ActiveX control instead of BHO? If the first page loaded contains the object tag that starts the ActiveX, I can maybe register the protocol as namespace there, and then it will be used for all subsequent requests from that process?
Is there any type of ActiveX that gets started when a page starts to load, without showing anything on the page? (I am only aware of ActiveX controls embedded as objects inside html pages).
-
Tuesday, January 03, 2012 2:53 PM
SachinGarg wrote:
You need some way to get your DLL into the browser's process. See if any of these techniques works for you:
http://en.wikipedia.org/wiki/DLL_injectionCan you recommend any one of these techniques? Maybe something that you know works for IE or has a good chance to work?
My knowledge of DLL injection is largely theoretical. I did use Windows hooks successfully in the past, but a) it was a very long time ago, in the world where everyone I cared about ran Win95 (or Win98 at best) and was an admin on her machine, and b) it was not for the purpose of installing an APP.
If I am
able to inject my DLL in IE process (and thats a big IF), can I register the protocol right awayI don't see why not.
or do I need to somehow find
out and wait for the browser session to start?I'm not sure what you mean by "the browser session".
What do you think about using an ActiveX control instead of BHO?
Sure, if you can get IE to load it. It's always better to be invited in, than to try and sneak through a back door.
Is there any type of ActiveX that gets started when a page starts to load, without showing anything on the page?
If you have control of the page, just create an <object> tag of zero size.
Igor Tandetnik
- Marked As Answer by SachinGarg Tuesday, January 03, 2012 5:49 PM
- Unmarked As Answer by SachinGarg Tuesday, January 03, 2012 5:49 PM
-
Tuesday, January 03, 2012 5:58 PM
SachinGarg wrote:
You need some way to get your DLL into the browser's process. See if any of these techniques works for you:
http://en.wikipedia.org/wiki/DLL_injectionCan you recommend any one of these techniques? Maybe something that you know works for IE or has a good chance to work?
My knowledge of DLL injection is largely theoretical. I did use Windows hooks successfully in the past, but a) it was a very long time ago, in the world where everyone I cared about ran Win95 (or Win98 at best) and was an admin on her machine, and b) it was not for the purpose of installing an APP.
If I am
able to inject my DLL in IE process (and thats a big IF), can I register the protocol right awayI don't see why not.
What if I create a small wrapper process that, as soon as it starts, registers the protocol as namespace and then launches iexplore.exe? Will the registered protocol remain registered in IE's context? Or maybe there is some way of starting internet-explorer session such that it isn't a new 'process', if that makes a difference to namespace registration.
(I know this sounds dumb, but it will be awesome if this works).
-
Tuesday, January 03, 2012 7:15 PM
On 1/3/2012 12:58 PM, SachinGarg wrote:
What if I create a small wrapper process that, as soon as it starts, registers the protocol as namespace and then launches iexplore.exe?
Temporary APP registration is per-process.
Will the registered protocol remain registered in IE's context?
No.
Or maybe there is some way of starting internet-explorer session such that it isn't a new 'process'
It's a separate EXE. It can't run in the same process as your EXE.
Igor Tandetnik
-
Sunday, January 15, 2012 4:43 PM
SachinGarg wrote:
Is there some way to register the protocol for IE without a BHO?
You need some way to get your DLL into the browser's process. See if any of these techniques works for you:
I got things working using DLL injection and it works with IE9 on Win7. When I test using IE7 on Vista, CoCreateInstance (in the code injected in IE to create pluggable protocol object and register it) fails with "CoInitialize has not been called" (800401F0 hex). If I manually try to call CoInitialize in the injected code, IE7 crashes.
Also, when I try to use my BHO which works fine with IE9 on Win7, its SetSite method is never called with IE7 on Vista.
Do you have any idea what could be going wrong here? My best guess so far is that the problem could be different versions of ATL used in IE7, compared to my DLLs which are built using visual studio 2010. And a possible solution could be to try building my DLLs using visual studio 2008. Or is something else going wrong?
-
Wednesday, July 18, 2012 4:50 PM
Hi Igor,
I have created a library that allows IE add-on developers to change data before IE processes them. Unlike the implementation described above I don’t register my protocol handler inside a BHO instance. If I did that, it would be impossible to provide successive data processing by several add-ons loaded into IE at the same time. Am I right? Instead of this, I patch virtual table of the IInternetProtocol and IInternetProtocolSink interfaces and intercept calls of the default protocol handler. In order to provide successive data processing in all add-ons (loaded in the active tab) I have to read all data at a time in the Read method. After that I throw the OnDataAvailable event for the developer to be able to read and modify the data before they get processed by IE. Below you can see the code that stores data in a local buffer and provide them to the developer (it is written in C#).
int IInternetProtocolSink.ReportData(int grfBSCF, uint ulProgress, uint ulProgressMax) { this.startReceiveData = ((grfBSCF & (int)BindStatusCallbackFlags.FirstDataNotification) > 0); this.endReceiveData = ((grfBSCF & (int)BindStatusCallbackFlags.LastDatanotification) > 0); this.fullDataAvailable = ((grfBSCF & (int)BindStatusCallbackFlags.DataFullyAvailable) > 0); if ((this.startReceiveData && (!this.endReceiveData)) || this.fullDataAvailable) { if (OnDataAvailable != null) { try { string url = GetBindString(BindStringType.Url); if (!ReportDataObject.Contains(url)) { ReportDataObject obj = ReportDataObject.AddObject(url, ReportDataObject.NewObject); DataAvailableEventArgs e = new DataAvailableEventArgs(this, obj, ReportDataState.DataReadyForDownload); OnDataAvailable(this, e); obj.PreviewData = e.PreviewData; } } catch (Exception) { } } } return Win32.INET_E_USE_DEFAULT_PROTOCOLHANDLER; } int IInternetProtocolSink.ReportResult(int hrResult, int dwError, IntPtr pRRData) { if (ReportDataObject.ReportDateList.Count > 0) { if (this.ProtocolAborted) { ReportDataObject.Clear(); } else { string url = GetBindString(BindStringType.Url); ReportDataObject.RemoveObject(url); } } this.ProtocolAborted = false; return Win32.INET_E_USE_DEFAULT_PROTOCOLHANDLER; } unsafe int IInternetProtocol.Read(IntPtr buffer, uint bufferSize, IntPtr pData) { if (OnDataAvailable == null) return Win32.INET_E_USE_DEFAULT_PROTOCOLHANDLER; string urlRead = GetBindString(BindStringType.Url); ReportDataObject dataObject = ReportDataObject.GetObject(urlRead, false); if (dataObject != null) { if (dataObject.DataFullyTransmitted) { // All data read return 1; } else if (dataObject.PreviewData) { int result = 1; int actualSizeRead; IntPtr pInternetProtocol = Marshal.ReadIntPtr(pData); IntPtr size_read = Marshal.ReadIntPtr((IntPtr)(pData.ToInt64() + Marshal.SizeOf(typeof(IntPtr)))); if (dataObject.DataSentStreamOffset == 0 && bufferSize > 0) { using (MemoryStream localBuffer = new MemoryStream((int)bufferSize)) { fixed (byte* tempBuf = localBuffer.GetBuffer()) { do { Marshal.WriteInt32(dataObject.SizeReadPtr, 0); result = this.ProtocolRead(pInternetProtocol, (IntPtr)tempBuf, bufferSize, dataObject.SizeReadPtr); actualSizeRead = Marshal.ReadInt32(dataObject.SizeReadPtr); if (actualSizeRead > 0) dataObject.DataStream.Write(localBuffer.GetBuffer(), 0, actualSizeRead); } while (result == 0); } } } if (result == 1) { if (dataObject.DataSentStreamOffset == 0) { try { if (dataObject.DataStream.Length > 0) dataObject.DataStream.Seek(0, SeekOrigin.Begin); DataAvailableEventArgs e = new DataAvailableEventArgs(this, dataObject, ReportDataState.DataFullyAvailable); OnDataAvailable(this, e); } catch (Exception) { } finally { if (dataObject.DataStream.Length > 0) dataObject.DataStream.Seek(0, SeekOrigin.Begin); } } if (dataObject.DataStream.Length > 0 && dataObject.DataSentStreamOffset < dataObject.DataStream.Length) { actualSizeRead = Math.Min((int)bufferSize, (int)(dataObject.DataStream.Length - dataObject.DataSentStreamOffset)); Marshal.Copy(dataObject.DataStream.GetBuffer(), dataObject.DataSentStreamOffset, buffer, actualSizeRead); dataObject.DataSentStreamOffset += actualSizeRead; Marshal.WriteInt32(size_read, actualSizeRead); result = (int)(dataObject.DataSentStreamOffset < dataObject.DataStream.Length ? 0 : 1); } } switch (result) { //S_OK - data was not fully read case 0: break; //E_PENDING - data is expected case unchecked((int)0x8000000A): Marshal.WriteInt32(size_read, 0); break; //S_FALSE - All data read case 1: dataObject.PreviewData = false; dataObject.DataFullyTransmitted = true; break; // Any errors default: dataObject.PreviewData = false; break; } return result; } } return Win32.INET_E_USE_DEFAULT_PROTOCOLHANDLER; }After the data have been read and processed, I pass them to the next add-on (if it exists). If the add-on does not read the data in the OnDataAvailable event, I simply skip reading the data and pass control to the next add-on in the chain (this can be one more add-on or the default implementation of the Read method). Finally, the resulting data get into IE where they are displayed. This algorithm works but there is a small problem that arises when a user uses the Back and Forward buttons in IE. In this case the default protocol handler does not call ReportData and IE merely reads data somewhere from cache. I tried to change the contents of the cache file after the data had been modified, but this did not help. IE still refreshes the page with data that were originally read by the default protocol handler. I have a feeling that the data are taken from some dynamic buffer rather than from Temporary Internet Files.
Can you please give me some clue on how to resolve this issue? Probably I need to patch some specific IE interface to intercept the moment when the Browsing History gets into the buffer.
-
Friday, July 27, 2012 3:57 PM
Hi All,
I found a solution. I use the CreateUrlCacheEntry and CommitUrlCacheEntry functions of WinINet API to update the page stored in the cache.


