Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Managed Asynchronous Pluggable Protocol and Threads

200 views
Skip to first unread message

Max K.

unread,
Feb 2, 2005, 7:34:43 PM2/2/05
to
I am putting together managed passthru APP to be able to lookup HTTP
response headers and need APP to pass all HTTP traffic to a Microsoft
APP (target) and display page in IE. My starting point was of course
Igor's pAPP toolkit that I am trying to rewrite in C# by adding
Interop and otherwise making as little changes as possible. I made some
progress, but got stock at a certain point and really need help. Here
are specifics.

I created managed COM object in DLL. This DLL contains 2 main
classes: Class IInternetProtocolImpl implements IInternetProtocol and
registered as COM object (for simplicity I use permanent registration
as HTTP namespace). It also implements IInternetPriority,
IInternetThreadSwitch, IWinInetInfo, but I wrote code only for
IInternetProtocol. This class has 2 members:
(a) m_spInternetProtocol - is a pointer to target APP (Microsoft
HTTP APP), which I discover by calling CoCreateInstance in my APP's
constructor
(b) m_internetSink is a pointer to Class CTestSink.

Class CTestSink implements IInternetProtocolSink, IInternetBindInfo,
IServiceProvider, and IHttpNegotiate. Just like in pAPP toolkit, all my
interfaces' methods, call the target APP's implementation of the
same methods.

Here is how IInternetProtocolImpl constructor looks:
public IInternetProtocolImpl()
{
Win32.CLSCTX context = Win32.CLSCTX.CLSCTX_INPROC_SERVER |
Win32.CLSCTX.CLSCTX_INPROC_HANDLER | Win32.CLSCTX.CLSCTX_LOCAL_SERVER |
Win32.CLSCTX.CLSCTX_REMOTE_SERVER;
int hr;
hr =
Win32.CoCreateInstance(ExplorerGUIDs.CLSID_HttpProtocol,IntPtr.Zero,
context, typeof(IInternetProtocol).GUID, out m_spInternetProtocol_ptr);
m_spInternetProtocol =
(IInternetProtocol)Marshal.GetObjectForIUnknown(m_spInternetProtocol_ptr);
}

I compared side-by-side program flow in Igor's pAPP toolkit and my
managed APP while navigating to the same page (I used www.cnn.com).
Order of events was the same up to the certain point:

1. IInternetProtocolImpl constructor is called.
2. IInternetProtocol:Start gets called which calls
m_spInternetProtocol.Start that causes calling
IInternetBindInfo:GetBindInfo and InternetBindInfo:GetBindString after
which IInternetProtocol:Start is completed.
3. Then IInternetProtocolSink:ReportProgress got called several times.
4. Then IInternetProtocolSink:Switch got called to switch back to the
main thread.

At this point in pAPP toolkit IInternetProtocol:Continue is called, but
my APP hangs and in a few minutes my IInternetProtocolSink:ReportResult
gets called with "Connection Timed Out" result.

Here are my observations. Originally my APP is created on a thread in
STA apartment (debugger shows
Thread.CurrentThread.AppartmentState=STA). When ReportProgress is
called Thread.CurrentThread.AppartmentState is changed to MTA. Also,
the value of m_spInternetProtocol (the address of my target APP) was
changed at the same time. Does it mean that new tread in a different
apartment was created? From what I know, COM cross-apartment
communication requires Marshalling. Is there any way to avoid creation
of multiple apartments? My APP TreadingModel in registry is set to
"Both". I tried to change it to Apartment and got the same result.
When I change it to Free or Neutral my Start wan not even called.

I suspected that system has a problem calling my Continue after I
called target Switch and tried just for troubleshooting to call
Continue myself from my Switch method.
public int Switch(ref _tagPROTOCOLDATA pProtocolData)
{
int hr;
try
{
hr = m_spInternetProtocolSink.Switch(ref pProtocolData);
hr = m_spInternetProtocol.Continue(ref pProtocolData);
}
Catch (Exception e) {}
return hr;
}
Switch returns S_OK but on Continue call I get Invalid Cast Exception
"cannot QueryInterface IInternetProtocolImpl. InternetProtocol". It
looks like system cannot get pointer to my InternetProtocol at this
point. I did few more tests.
First used Marshal.GetIUnknownForObject(target_APP) to get target_APP
address and received different addresses before and after
ReportProgress was called. I expected them to be the same since it the
same object. Not sure if this assumption is correct or perhaps multiple
apartments cause creation of multiple objects. Second, when I called
target_APP.QueryInterface(IID_IInternetProtocol) before and after
thread switch it returned pointer before, but did not return it after.
target_APP.QueryInterface(IID_IUnknown) though was returned both before
and after thread switch.

Otherwise I did not notice anything significant. I would appreciate any
help.

Max K.

Igor Tandetnik

unread,
Feb 2, 2005, 8:02:05 PM2/2/05
to
"Max K." <ma...@eden.rutgers.edu> wrote in message
news:1107390883.3...@g14g2000cwa.googlegroups.com

> Here are my observations. Originally my APP is created on a thread in
> STA apartment (debugger shows
> Thread.CurrentThread.AppartmentState=STA). When ReportProgress is
> called Thread.CurrentThread.AppartmentState is changed to MTA.

I suspect CLR got confused. In my experience, HTTP APP calls you back on
a worker thread that did not initialize COM at all.

URLMon blatantly disregards COM threading rules. It only works because
direct interface pointers are used throughout, no marshalling is
involved, so COM runtime is out of the picture and does not have a
chance to complain. The whole thing is held together with bubble gum and
duct tape, I had to tread very carefully to avoid breaking it.

I shudder to think about how .NET runtime would behave when thrown into
this mess. I have my doubts about the feasibility of a managed APP
handler.

> I suspected that system has a problem calling my Continue after I
> called target Switch and tried just for troubleshooting to call
> Continue myself from my Switch method.

But that defeats the whole point. Switch / Continue pair is there for a
reason - it allows the APP to request the host application to call it
back on the main STA thread, mostly when it needs to do honest COM stuff
that would break from the worker. Essentially, when you call Switch from
the worker, MSTHML posts a message to the hidden window it maintains on
the main thread. When the message arrives, it calls Continue on you,
thus effectively switching to the main thread.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


Max K.

unread,
Feb 6, 2005, 12:51:58 PM2/6/05
to
Igor,

Thank a lot for your thoughts and explanation about Continue.
Everything makes sense, and at this point I am giving up on managed APP
and will use unmanaged instead. I truly believe that your help saved me
a lot of time and frustrations.

Here is what I really need. I implemented MIME filter and need to
access HTTP response headers. From reading of several of your past
postings I made an impression that MIME filter by itself cannot get
headers. Your pAPP toolkit can access headers perfectly. Is there any
way to create a custom communication between APP and MIME filter to
send headers?

Thank you for your help

Max K.

Igor Tandetnik

unread,
Feb 7, 2005, 10:38:59 AM2/7/05
to
"Max K." <ma...@eden.rutgers.edu> wrote in message
news:1107712318.1...@f14g2000cwb.googlegroups.com

> Here is what I really need. I implemented MIME filter and need to
> access HTTP response headers. From reading of several of your past
> postings I made an impression that MIME filter by itself cannot get
> headers.

I believe it can. I'd at least try it first before giving up. This is
what you do: in your IInternetProtocol::Start, you are given
PROTOCOLFILTERDATA structure, which contains IInternetProtocol pointer
of the APP doing the downloading. Query it for IWinInetHttpInfo, call
QueryInfo. This call maps directly to HttpQueryInfo (sans HINTERNET
handle) and allows you to read all request and response headers.

Max K.

unread,
Feb 7, 2005, 8:44:45 PM2/7/05
to
As you suggested, I tried to query IInternetProtocol pointer for
IWinInetHttpInfo but it did not work. Then I tried to query it for
IServiceProvider and received a valid pointer, however I could not
QueryService for IWinInetHttpInfo. Is there anything else I should
try?

Thanks.

Max K.

Igor Tandetnik

unread,
Feb 8, 2005, 11:06:16 AM2/8/05
to
"Max K." <ma...@eden.rutgers.edu> wrote in message
news:1107827085.8...@f14g2000cwb.googlegroups.com

Do you have a small sample I can play with? You can e-mail it to me at
itand...@mvps.org if you want, I'll look at it. I have not actually
worked with MIME filters myself, only with APPs.

0 new messages