COM bug (crash) or am I just doing something stupid ?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

COM bug (crash) or am I just doing something stupid ?

Chris Uppal-3
I should start by admitting that I don't really know what I'm doing here, so it
is very likely that I /am/ doing something stupid...

I've been trying to use the asynchronous notifications provided by
IWinHttpRequest objects, but my attempts just crash Dolphin (D5 or D6).

I've generated the wrappers for "Microsoft WinHTTP Services, version 5.1", thus
creating classes IWinHttpRequest and IWinHttpRequestEvents (for more background
see the "HTTP/S Post Get Head" thread).  With them installed, I can download
URLs provided I don't try to monitor the asynchronous progress events.

This is what I'm trying:

    target := Object new.
    sink := AXEventSink
                    target: target
                    sourceTypeInfo: IWinHttpRequestEvents typeInfo.
    request := IWinHttpRequest new.
    sink connect: request.
    request open: 'GET' url: 'http://www.microsoft.com/' async: true.
    request send.

That /seems/ to work up to the point where I invoke #send.  At that point I
get a sequence of invalid reads of addresses like 10, then a few invalid writes
of arbitrary-looking addresses, and then the image crashes.  (With a crash
dump, but the info therein does not appear relevant).

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: COM bug (crash) or am I just doing something stupid ?

Edward Stow
Chris Uppal wrote:

> I should start by admitting that I don't really know what I'm doing here, so it
> is very likely that I /am/ doing something stupid...
>
> I've been trying to use the asynchronous notifications provided by
> IWinHttpRequest objects, but my attempts just crash Dolphin (D5 or D6).
>
> I've generated the wrappers for "Microsoft WinHTTP Services, version 5.1", thus
> creating classes IWinHttpRequest and IWinHttpRequestEvents (for more background
> see the "HTTP/S Post Get Head" thread).  With them installed, I can download
> URLs provided I don't try to monitor the asynchronous progress events.
>
> This is what I'm trying:
>
>     target := Object new.
>     sink := AXEventSink
>                     target: target
>                     sourceTypeInfo: IWinHttpRequestEvents typeInfo.
>     request := IWinHttpRequest new.
>     sink connect: request.
>     request open: 'GET' url: 'http://www.microsoft.com/' async: true.
>     request send.
>
> That /seems/ to work up to the point where I invoke #send.  At that point I
> get a sequence of invalid reads of addresses like 10, then a few invalid writes
> of arbitrary-looking addresses, and then the image crashes.  (With a crash
> dump, but the info therein does not appear relevant).

I have the same errors.

I am pretty much clueless in this area - but reading the documention:
AXEventSink implements the IDispatch interface (Dolphin class comment)
but in MSDN
' IWinHttpRequestEvents interface inherits the methods of the IUnknown
interface'

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winhttp/http/iwinhttprequest_setcredentials.asp

Edward Stow


Reply | Threaded
Open this post in threaded view
|

Re: COM bug (crash) or am I just doing something stupid ?

Chris Uppal-3
In reply to this post by Chris Uppal-3
I wrote:

> I've been trying to use the asynchronous notifications provided by
> IWinHttpRequest objects, but my attempts just crash Dolphin (D5 or D6).

I've made some progress, but I still don't know whether the problem was a
mistake on my part, or a bug in either the Dolphin COM stuff or in the HTML
component.

Edward's suggestion (thanks Edward!), pushed me to try a different track.  I
created a normal COM implementation of IWinHttpRequestEvents, WinHttpWatcher,
as a trivial subclass of COMInterfaceImp which traces the progress events to
the Transcript (I'll post the code if anyone wants to see it).

I didn't #register that class or #registerClassFactory since I don't think it's
necessary (or even correct) for this application[*].  Maybe I'm wrong, if so
then I hope someone will please point it out.

With that created, I can do:

    watcher := WinHttpWatcher new queryInterface: IWinHttpRequestEvents.
    request := IWinHttpRequest new.

    container := request queryInterface: IConnectionPointContainer.
    connector := container findConnectionPoint: IWinHttpRequestEvents.
    cookie := connector advise: watcher.

    request open: 'GET' url: 'http://www.microsoft.com/' async: true.
    request send.

    --- pause for a bit ---

    connector unadvise: cookie.
    watcher free.
    request free.

And that works!  So I am, basically, happy -- but there are a few puzzles...

The first thing that's puzzling is that the code for setting up the connection
point is copied almost verbatim from AXEventSink, so why doesn't an AXEventSink
work here ?

One clue is that, if I stick a bit of tracing into WinHttpWatcher's
#queryInterface, I can see that the HTML object doesn't QueryInterface() the
handle that it is passed -- it seems to just assume that it is in fact a
pointer to an IWinHttpRequestEvents.  I assume that's a bug in the HTTP object.
So maybe the crash is because AXEventSink passes an IDispatch ?  So I tried
moving WinHttpWatcher to be a subclass of AXDualImp (changing the code
accordingly -- a remarkably trivial change, btw ;-), and tried the above
example again, but changing the first line to:

     watcher := WinHttpWatcher new queryInterface: IDispatch.

But that still worked.  So it seems that the HTTP object is happy enough to get
an IUnknown that is in fact the IDispatch part of a dual interface, but is not
happy to get the one supplied by an AXEventSink.

So, is this a flaw in AXEventSink which is being exposed by the bug in the HTTP
object, or only a bug in the HTTP object, or am I daft expecting this to work ?

    -- chris


[*] Actually, at one point I did have the class #registerClassFactory, in a D6
image, and then ran something like the above example in a D5 image, but
changing it so that:
    watcher := IWinHttpRequestEvents new.
And that also worked, but the tracing for the D5 operation was written to the
D6 image's Transcript -- which is exactly what you'd expect, but it still
looked very odd ;-)


Reply | Threaded
Open this post in threaded view
|

Re: COM bug (crash) or am I just doing something stupid ?

Edward Stow
Chris,

Did you see this post - this seems to be a model for what may be
needed?

http://groups.google.com/group/comp.lang.smalltalk.dolphin/browse_thread/thread/951f2c38dcc38a/fb29b429b946d1ee?lnk=gst&q=iUnknown&rnum=1#fb29b429b946d1ee

Edward Stow

Chris Uppal wrote:

> I wrote:
>
> > I've been trying to use the asynchronous notifications provided by
> > IWinHttpRequest objects, but my attempts just crash Dolphin (D5 or D6).
>
> I've made some progress, but I still don't know whether the problem was a
> mistake on my part, or a bug in either the Dolphin COM stuff or in the HTML
> component.
>
> Edward's suggestion (thanks Edward!), pushed me to try a different track.  I
> created a normal COM implementation of IWinHttpRequestEvents, WinHttpWatcher,
> as a trivial subclass of COMInterfaceImp which traces the progress events to
> the Transcript (I'll post the code if anyone wants to see it).
>
> I didn't #register that class or #registerClassFactory since I don't think it's
> necessary (or even correct) for this application[*].  Maybe I'm wrong, if so
> then I hope someone will please point it out.
>
> With that created, I can do:
>
>     watcher := WinHttpWatcher new queryInterface: IWinHttpRequestEvents.
>     request := IWinHttpRequest new.
>
>     container := request queryInterface: IConnectionPointContainer.
>     connector := container findConnectionPoint: IWinHttpRequestEvents.
>     cookie := connector advise: watcher.
>
>     request open: 'GET' url: 'http://www.microsoft.com/' async: true.
>     request send.
>
>     --- pause for a bit ---
>
>     connector unadvise: cookie.
>     watcher free.
>     request free.
>
> And that works!  So I am, basically, happy -- but there are a few puzzles...
>
> The first thing that's puzzling is that the code for setting up the connection
> point is copied almost verbatim from AXEventSink, so why doesn't an AXEventSink
> work here ?
>
> One clue is that, if I stick a bit of tracing into WinHttpWatcher's
> #queryInterface, I can see that the HTML object doesn't QueryInterface() the
> handle that it is passed -- it seems to just assume that it is in fact a
> pointer to an IWinHttpRequestEvents.  I assume that's a bug in the HTTP object.
> So maybe the crash is because AXEventSink passes an IDispatch ?  So I tried
> moving WinHttpWatcher to be a subclass of AXDualImp (changing the code
> accordingly -- a remarkably trivial change, btw ;-), and tried the above
> example again, but changing the first line to:
>
>      watcher := WinHttpWatcher new queryInterface: IDispatch.
>
> But that still worked.  So it seems that the HTTP object is happy enough to get
> an IUnknown that is in fact the IDispatch part of a dual interface, but is not
> happy to get the one supplied by an AXEventSink.
>
> So, is this a flaw in AXEventSink which is being exposed by the bug in the HTTP
> object, or only a bug in the HTTP object, or am I daft expecting this to work ?
>
>     -- chris
>
>
> [*] Actually, at one point I did have the class #registerClassFactory, in a D6
> image, and then ran something like the above example in a D5 image, but
> changing it so that:
>     watcher := IWinHttpRequestEvents new.
> And that also worked, but the tracing for the D5 operation was written to the
> D6 image's Transcript -- which is exactly what you'd expect, but it still
> looked very odd ;-)


Reply | Threaded
Open this post in threaded view
|

Re: COM bug (crash) or am I just doing something stupid ?

Chris Uppal-3
Edward,

> Did you see this post - this seems to be a model for what may be
> needed?
>
>
http://groups.google.com/group/comp.lang.smalltalk.dolphin/browse_thread/thread/951f2c38dcc38a/fb29b429b946d1ee?lnk=gst&q=iUnknown&rnum=1#fb29b429b946d1ee

No, I hadn't seen it (or had forgotten it).  Thanks.  It seems I had managed to
reinvent something pretty close to that (nice to have the confirmation ;-)

I guess that the reason the first version crashes is that ACEventSink /only/
implements IDispatch, and that the HTTP component doesn't bother to check what
kind of IUnknown it is passed.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: COM bug (crash) or am I just doing something stupid ?

Edward Stow
Following is my final take on implementing an IUnknown event sink.Two
classes:

AXIUnknownEventSink - implements #connect:, #disconnect
IWinHttpRequestEventsSink  implements the OnXXX methods.
See IWinHttpRequestEventsSink  class>>exampleRequest for sample usage.

You must use disconnect on the sink object to break the circular
references between the sink and the request object.

"Filed out from Dolphin Smalltalk X6"!

COMInterfaceImp subclass: #AXIUnknownEventSink
        instanceVariableNames: 'connector cookie'
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
AXIUnknownEventSink guid: (GUID fromString:
'{A329F9AE-B31E-4312-BEDA-512356EEBA61}')!
AXIUnknownEventSink comment: ''!
!AXIUnknownEventSink categoriesForClass!Unclassified! !
!AXIUnknownEventSink methodsFor!

connect: anIUnknown
        | container |
        self disconnect.
        container := anIUnknown queryInterface: IConnectionPointContainer.
        connector := container findConnectionPoint: self interfaceClass.
        cookie := connector advise: (self server queryInterface: IUnknown).
        "Transcript nextPutAll: 'Connected '; print: self; cr."
        ^cookie!

disconnect
        "Disconnect the receiver from the connection point to which it has
previously been connected."

        connector notNull
                ifTrue:
                        ["Transcript nextPutAll: 'Disconnecting '; print: self; cr."
                        connector Unadvise: cookie.
                        connector free.
                        connector := nil]!

interfaceClass
        ^self class interfaceClass!

supportedInterfaces
        ^Array with: self interfaceClass! !
!AXIUnknownEventSink categoriesFor: #connect:!public! !
!AXIUnknownEventSink categoriesFor: #disconnect!public! !
!AXIUnknownEventSink categoriesFor: #interfaceClass!public! !
!AXIUnknownEventSink categoriesFor: #supportedInterfaces!public! !

!AXIUnknownEventSink class methodsFor!

interfaceClass
        ^self subclassResponsibility! !
!AXIUnknownEventSink class categoriesFor: #interfaceClass!public! !

AXIUnknownEventSink subclass: #IWinHttpRequestEventsSink
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
IWinHttpRequestEventsSink guid: (GUID fromString:
'{F1F9763B-FFA6-4426-ADDD-86DBE4FEE08B}')!
IWinHttpRequestEventsSink comment: ''!
!IWinHttpRequestEventsSink categoriesForClass!Unclassified! !
!IWinHttpRequestEventsSink methodsFor!

OnError: errorNumber errorDescription: errorDescription
        "Invoke the OnResponseStart() method of the COM object."

        Transcript
                show: 'OnError called: errorNumber ' , errorNumber printString , '
errorDescription '
                                        , errorDescription printString;
                cr!

OnResponseDataAvailable: data
        Transcript
                show: 'OnResponseDataAvailable called ' , 'data: ' , data
printString;
                cr.!

OnResponseFinished
        "Invoke the OnResponseFinished() method of the COM object"

        Transcript
                show: 'OnResponseFinished called';
                cr!

OnResponseStart: status contentType: contentType
        "Invoke the OnResponseStart() method of the COM object."

        Transcript
                show: 'OnResponseStart called: status ' , status printString , '
contentType '
                                        , contentType printString;
                cr! !
!IWinHttpRequestEventsSink categoriesFor:
#OnError:errorDescription:!public! !
!IWinHttpRequestEventsSink categoriesFor:
#OnResponseDataAvailable:!public! !
!IWinHttpRequestEventsSink categoriesFor: #OnResponseFinished!public! !
!IWinHttpRequestEventsSink categoriesFor:
#OnResponseStart:contentType:!public! !

!IWinHttpRequestEventsSink class methodsFor!

exampleRequest
        "
self exampleRequest
"

        | request sink |
        request := IWinHttpRequest new.
        sink := self new.
        sink connect: request.
        request
                open: 'GET'
                url: 'http://www.google.com/'
                async: true.
        request send.
!

interfaceClass
        ^IWinHttpRequestEvents! !
!IWinHttpRequestEventsSink class categoriesFor: #exampleRequest!public!
!
!IWinHttpRequestEventsSink class categoriesFor: #interfaceClass!public!
!