2

In IE11, multiple threads will fire into the APP when visiting a site such as www.yahoo.com - and only a very small subset of those threads can be associated with the browser window (by querying the service provider, et cetera) that was passed through in the ::SetSite() call when the tab was created.

This doesn't work for the majority of threads on modern sites (e.g. www.evernote.com)

How can I figure out which IE tab a thread is acting on the behalf of - or is this impossible?

I would love to know that there's some way to match those threads up to the pUnkSite passed into ::SetSite() (or something similar) - but that seems unlike the IE model as I've experienced it so far.

Thanks.

Hans
  • 83
  • 1
  • 8
  • 2
    Try the `Switch`/`Continue` trick. It used to work reliably back when I was playing with this (which, admittedly, was years ago, when IE6 was fresh and new). Look [here](https://social.msdn.microsoft.com/Forums/ie/en-US/838987a3-0277-4aae-ab65-77743ad4fae8/) for `PD_FORCE_SWITCH` (sorry for the second-hand reference, all the original discussions didn't seem to survive the passage of time). – Igor Tandetnik Feb 28 '15 at 21:55
  • Even like that, it should be an answer instead of merely a comment. – acelent Mar 01 '15 at 00:58
  • Igor, much appreciated, and you should drop it as an answer so that I can mark it as so. I didn't realize I could tell the sink to switch the call to a thread context that I can associate properly. – Hans Mar 01 '15 at 13:38
  • @IgorTandetnik - Igor, is it possible to switch onto the main thread, grab a GIT token (for example), and then switch **back** to the original thread? I worry about running everything down the same thread... – Hans Mar 01 '15 at 14:03
  • You don't get to choose which thread you are running on. WinInet will issue callbacks on whatever thread it damn well pleases - including threads that never even initialized COM, never called any flavor of `CoInitialize[Ex]`. Marshaling doesn't work in this environment - only direct method calls, and only because COM runtime is not involved in those and doesn't have a chance to complain. Objects implemented by UrlMon (like `IInternetProtocolSink`) are prepared for that - but the browser isn't, so be very careful where and how you call methods on that pointer you want to obtain. – Igor Tandetnik Mar 01 '15 at 14:44
  • @IgorTandetnik "never even initialized COM" - sorry, this confused me a bit, all of the calls we deal with initiate from some aspect of IHttpNegotiate or IInternetProtocolSink. I will be ware of any callbacks outside of COM methods though. Cheers. – Hans Mar 01 '15 at 22:19

1 Answers1

2

Try the Switch/Continue trick. Your APP would call IInternetProtocolSink::Switch like this, e.g. in Start:

PROTOCOLDATA data = {0};
data.grfFlags = PD_FORCE_SWITCH;  // important
pProtocolSink->Switch(&data);

Eventually, the client will turn around and call IInternetProtocol::Continue on you, on the main UI thread. Once on the main thread, things like IServiceProvider::QueryService(IID_IWindowForBindingUI) should work, and help you connect back to the requesting browser and/or document.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Thx again Igor. BTW, if won't let me upvote this since I just joined :(. – Hans Mar 01 '15 at 22:12
  • If I call ->Switch() in GetBindInfoEx, for example, should I return from the method immediately? I'm unclear about what actually happens behind the scenes - continue will get called, but does that just operate as a signal or should I be calling GetBindInfoEx from Continue to 'continue'? (That would mean stuffing bind info data someplace I presume.) – Hans Mar 11 '15 at 00:48
  • I assume you are implementing a passthrough APP (an object that sits between the client and real APP), otherwise you wouldn't be implementing the sink side. `GetBindInfoEx` is synchronous - by the time it's called, it's too late to do Switch/Continue. You must return a valid value then and there. If you need to get information from the browser before starting the actual request, do Switch/Continue in your `Start[Ex]`, and delay calling `Start` on the real APP. – Igor Tandetnik Mar 11 '15 at 02:17
  • Thank you Igor, as usual you are IMMENSELY helpful. This seems to have gotten everything on the request side of the operation onto the correct thread. I presume that the other side of that coin is that ::OnResponse can then be triggered and need to be switched as well? Or do we have to store information associated with the sink request and use that to finagle something when the response comes in on a different thread? – Hans Mar 11 '15 at 20:48
  • Why do you care which thread `OnResponse` comes on? The client doesn't. I suppose now might be a good time to take a step back and explain what problem you are trying to solve. – Igor Tandetnik Mar 12 '15 at 01:55
  • Sorry, we care about requests because we want to add tags to outgoing data on certain types of content, we care about the incoming response because we want to remove those tags on incoming data as well as reject certain types of responses based upon that information. Because the business logic is contained in an STA object that implements IObjectWithSite (which basically represents a 'tab' for us) I want to ensure that the ::OnResponse/::Read calls happen on the document thread. – Hans Mar 12 '15 at 15:44
  • Perhaps during the request portion we simply identify the STA object (since we are able to identify it via a map of thread IDs to STA objects) and in OnResponse instead of switching it onto the STA thread, we could marshal the pointer - but it would be cleaner overall to have all of these operations on the document thread. Tab closure (and the ensuing destruction of the STA object) can cause problems when other threads are trying to pull the interface out of the GIT or unmarshal the pointer through CoMarshal/Unmarshal. Your advice is highly appreciated :). – Hans Mar 12 '15 at 15:57
  • `the business logic is contained in an STA object` This is unwise. I'd recommend that the business logic be divorced from COM, and made thread-safe. The environment in which an APP operates is essentially hostile to COM. UrlMon is only superficially COM-like. As I mentioned already, it doesn't obey COM threading model rules; callbacks often arrive on WinInet-created threads that haven't even initialized COM, and so, say, GIT won't work (you'd get `CO_E_NOTINITIALIZED` if you try). – Igor Tandetnik Mar 12 '15 at 16:36
  • Yes, for performance reasons alone I am refactoring the entire approach to use normal, threadsafe, C++ classes; however, for the next two months I am stuck with this - for many reasons.) Is there anyway that you can think of that would get the ::OnResponse and ::Read calls onto the main thread like you can the request? Both of these methods are COM methods so presumably they're not arriving on WinInet threads. – Hans Mar 12 '15 at 17:04
  • All APP methods are notionally COM methods - that doesn't stop them from arriving on WinInet threads. Anyway, you can play the Switch/Continue trick in `OnResponse` - you delay forwarding the call to the real sink, but in the process lose the opportunity to forward an error code (if the real sink decides to abort the request) or additional headers. The error code is not too bad - you can call `Abort` at any time. – Igor Tandetnik Mar 12 '15 at 23:12
  • As to `Read` - it is normally called in response to you calling `IInternetProtocolSink::ReportData`. So you can Switch/Continue in `ReportData`, forward the call to the real sink on the main thread, and then it should call `Read` on you also on the main thread. – Igor Tandetnik Mar 12 '15 at 23:12
  • As always - thanks Igor. God knows what BHO writers would do without you :). BTW, is there any recommended way of getting in touch with you outside of SO? Our company would benefit from bringing you in for a couple of days to simply talk about the recommended way to architect a BHO given particular constraints. No code writing, just commenting and validation of architectural designs. I'd supply you with my contact info but I can't for several more months ;). – Hans Mar 14 '15 at 00:55