Unexplainable ActiveX Error

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

Unexplainable ActiveX Error

Eric Taylor
Hello Forum,

We're using Codejock's ActiveX Control Suite, and we've encountered a problem.

We've tested the CommandBars ActiveX control in four different development
environments, including Visual FoxPro, which is notoriously finicky when
it comes to ActiveX.  CommandBars works fine in three of these environments.
 The only exception is Dolphin.

The code is located in the #onOpenView method (just for testing purposes;
we're not claiming that this is where it would ultimately go):

| myMenuBar myFileMenu |
super onViewOpened.
self view showMaximized.
myMenuBar := menuBarPresenter view controlDispatch addMenuBar: 'My Bar'.
myFileMenu := myMenuBar commandBars activeMenuBar controls add: xtpControlPopup
id: 0 caption: '&File'.
myFileMenu commandBar controls add: xtpControlButton id: 105 caption: '&Undo'.

Dolphin generates an exception at the last line.  The other lines execute
fine, so that when the view opens we have a menu bar and the menu "File"
showing.  The id of 105 above simply refers to a type of menu, but it's arbitrary
otherwise.  The debugger returns the following in the top four lines:

IDispatch>>invokeId: id flags: wFlags parms: parms retVal: varRes
IDispatch>>invokeId: id flags: wFlags parms: parms
IDispatch>>doesNotUnderstand:
PFGShell>>onViewOpened

The approach we're taking works in all of the other environments, even Eiffel
where we also have to use an ActiveX Control Wizard to generate the interface.
 Basically, all we're doing is creating a menu bar, adding the menu 'File'
to the menu bar, and then attempting to add an 'Undo' menu item to the File
menu.  Dolphin appears to be complaining about the return value.  We've tried
a number of variations with no success.

Codejock's (www.codejock.com) suite of ActiveX controls can be downloaded
at no charge and used for 30 days.  Perhaps someone with more experience
with Dolphin could try them out.

Thanks very much.

Cheers,

Eric


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Eric Taylor
Hello Eric,

I apologize, but my newsreader formatted the debugger information a bit strange.
 The four lines are (with the '>>' characters omitted):

IDispatch invokeId: id flags: wFlags parms: parms retVal: varRes
IDispatch invokeId: id flags: wFlags parms: parms
IDispatch doesNotUnderstand:
PFGShell onViewOpened

Cheers,

Eric


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Blair McGlashan-4
In reply to this post by Eric Taylor
"Eric Taylor" <[hidden email]> wrote in message
news:[hidden email]...

> Hello Forum,
>
> We're using Codejock's ActiveX Control Suite, and we've encountered a
> problem.
>
> We've tested the CommandBars ActiveX control in four different development
> environments, including Visual FoxPro, which is notoriously finicky when
> it comes to ActiveX.  CommandBars works fine in three of these
> environments. The only exception is Dolphin.
>
> The code is located in the #onOpenView method (just for testing purposes;
> we're not claiming that this is where it would ultimately go):
>
> | myMenuBar myFileMenu |
> super onViewOpened.
> self view showMaximized.
> myMenuBar := menuBarPresenter view controlDispatch addMenuBar: 'My Bar'.
> myFileMenu := myMenuBar commandBars activeMenuBar controls add:
> xtpControlPopup id: 0 caption: '&File'.
> myFileMenu commandBar controls add: xtpControlButton id: 105 caption:
> '&Undo'.
>
> Dolphin generates an exception at the last line.  The other lines execute
> fine, so that when the view opens we have a menu bar and the menu "File"
> showing.  The id of 105 above simply refers to a type of menu, but it's
> arbitrary otherwise.  The debugger returns the following in the top four
> lines:
>
> IDispatch>>invokeId: id flags: wFlags parms: parms retVal: varRes
> IDispatch>>invokeId: id flags: wFlags parms: parms
> IDispatch>>doesNotUnderstand:
> PFGShell>>onViewOpened
>
> The approach we're taking works in all of the other environments, even
> Eiffel where we also have to use an ActiveX Control Wizard to generate the
> interface. Basically, all we're doing is creating a menu bar, adding the
> menu 'File' to the menu bar, and then attempting to add an 'Undo' menu
> item to the File menu.  Dolphin appears to be complaining about the return
> value.  We've tried a number of variations with no success.
>

 From the stack details I can see that your code is going through the
#doesNotUnderstand: (DNU) processing, and this only really works in simple
cases (like accessing properties). There's a lot of coverage of the
IDispatch/DNU processing in the newsgroup archives.

Your problem is that on the line 'myFileMenu := ...etc...' the return value
of the add:id:caption: method (ICommandBarControls::Add()) is of type
ICommandBarControl, which is the base interface type of the actual type
added. ICommandBarControl does not actually implement #commandBar, so falls
back on the DNU processing, and because the added type is actually
implementing ICommandBarPopup the first message you send to it #commandBar
works through the DNU mechanism (its a simple property access). However
you've now lost all notion of the actual type, you just have an IDispatch,
and so once again #controls will go through DNU. As a simple property access
it works as well, and you get another IDispatch, but this time the
add:id:caption: method call fails because the simple DNU processing isn't
able to handle it.

You can easily correct this by inserting a #downCast as follows:

 myFileMenu := (myMenuBar commandBars activeMenuBar controls
    add: xtpControlPopup
    id: 0
    caption: '&File') downCast.

Or (more explicitly and not relying on type information):

 myFileMenu := (myMenuBar commandBars activeMenuBar controls
    add: xtpControlPopup
    id: 0
    caption: '&File') queryInterface: ICommandBarPopup .

In a statically typed language you would need a similar down cast operation,
or you would have to go through the late bound Invoke operation directly, so
I would guess that your Eiffel code was not directly equivalent.

BTW: Before doing this I suggest you work through it a little in the
debugger. If you press Debug in the walkback you can go into the debugger
and start evaluating some expressions to find out what is going on - just
type them into the source. If you do that, you'll even be able to use the
autocompletion to guide you as to the problem, because once in the debugger
you have full type information for the variables, and it will be obvious
that 'myFileMenu' doesn't implement #commandBar. This is a clue to why
programming in the debugger can be so productive - it is programming with
the concrete, rather than the abstract.

It's a little disappointing that the CodeJock object model appears to
consist of dispinterfaces, rather than dual interfaces. This means you can't
see the inheritance model (not to mention the poorer performance of invoking
through IDispatch). Seems odd, bearing in mind the underlying objects
appears to be implemented in C++. Oh well, you can improve matters a bit by
moving some of the generated interface classes around a bit to make the
structure of the OM a bit easier to visualize. For example if you move
ICommandBarPopup under ICommandBarControl (drag & drop in the browser) , and
then right click on ICommandBarPopup and run Refactorings/Remove Duplicate
Methods from the context menu, you'll be able to see how ICommandBarPopup
extends ICommandBarControl. In fact it adds just two properties (#commandBar
being one obviously) and a method.

Hope this helps

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Udo Schneider
Blair McGlashan wrote:
> You can easily correct this by inserting a #downCast as follows:
Is there any advantage/disadvantage of using #downCast vs. #asImplType?
By default I'm using asImplType and I'm just wondering whether I should
#downCast directly as this is called by #asImplyType anyway.

CU,

Udo


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Blair McGlashan-4
"Udo Schneider" <[hidden email]> wrote in message
news:[hidden email]...
> Blair McGlashan wrote:
>> You can easily correct this by inserting a #downCast as follows:
> Is there any advantage/disadvantage of using #downCast vs. #asImplType? By
> default I'm using asImplType and I'm just wondering whether I should
> #downCast directly as this is called by #asImplyType anyway.

I suggest taking a look at the implementation of #asImplType, which should
make it clear. Its purpose is to avoid the expense of performing the
#downCast if you already have a specialisation of IDispatch - i.e. it is
primarily intended for use in library code where you would normally expect
to have either IDispatch, or the "correct" interface. In this case it
wouldn't do the job, because the method is returning an abstract base type
that needs to be further refined - the #downCast will always be needed.

Actually when dealing with a component that has "abstract" interfaces (as
much as that is a reasonable concept) like this one, I usually override
#asImplType to perform downcast. As a case in point see IXMLDOMNode.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Udo Schneider
Blair McGlashan wrote:

> I suggest taking a look at the implementation of #asImplType, which should
> make it clear. Its purpose is to avoid the expense of performing the
> #downCast if you already have a specialisation of IDispatch - i.e. it is
> primarily intended for use in library code where you would normally expect
> to have either IDispatch, or the "correct" interface. In this case it
> wouldn't do the job, because the method is returning an abstract base type
> that needs to be further refined - the #downCast will always be needed.
>
> Actually when dealing with a component that has "abstract" interfaces (as
> much as that is a reasonable concept) like this one, I usually override
> #asImplType to perform downcast. As a case in point see IXMLDOMNode.
Thanks for the info. I did actually take a look at the code but missed
it's intention :-(

CU,

Udo


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Eric Taylor
In reply to this post by Blair McGlashan-4
Hello Blair,

Thank you for your most instructive reply.  Problem solved, except, of course,
the one where we feel we have been reduced to wide-eyed children taken in
by the wonderment of Smalltalk and, more particularly, Dolphin.

When I generated the interface using Eiffel, I did it for the purpose of
establishing a baseline for comparison, and not for technical reasons.  Upon
closer scrutiny, however, you are correct: The Eiffel code (or rather the
underlying intermediate C code it generates) isn't directly equivalent.  
Employing conditional assignment, Eiffel maps all eligible calls to the more
generic (and less elegant) invoke mechanism, or so it would appear.  Since
we have almost always written our own "controls" using Eiffel and its robust
Windows Eiffel Library (WEL), we haven't had too much experience with _practical_
ActiveX in Eiffel.

But I think it's only fair that we should be left wondering why it would
work in C#, Eiffel, and Visual FoxPro (of all places), and not in Dolphin.
 Certainly VFP has no notion of #downCast.  Eiffel forbids it in production
code, preferring instead a conversion pattern or a subclass of MISMATCH_CORRECTOR.
 Is there something that Dolphin could--or should--do to anticipate these
kinds of inefficiencies in the design of the ActiveX component itself?  With
your permission, I would like to pass your answer on to Codejock Software.

 From your reply we now understand the central importance of DNU.  Using your
reply as sort of a cursor, yes, there _is_ much in the newsgroup archives.
 In general, though, we've had mixed results searching the archives.  Sometimes
we find an answer right away.  Many times we do not, even though the answers
may be lurking there.  Forum posts are not always couched in terms that lend
themselves to indexing.  Rather than trouble you, or Andy, or even other
professionals in the forum, with questions like this, we would like to be
able to go to some sort of a knowledge base.  It's our understanding that
there was a Dolphin wiki at one time, and the archives speak of its eventual
reestablishment.  Is there any ETA of when this might happen?

Again, thank you.

Cheers,

Eric

> "Eric Taylor" <[hidden email]> wrote in message
> news:[hidden email]...
>
>> Hello Forum,
>>
>> We're using Codejock's ActiveX Control Suite, and we've encountered a
>> problem.
>>
>> We've tested the CommandBars ActiveX control in four different
>> development environments, including Visual FoxPro, which is
>> notoriously finicky when it comes to ActiveX.  CommandBars works fine
>> in three of these environments. The only exception is Dolphin.
>>
>> The code is located in the #onOpenView method (just for testing
>> purposes; we're not claiming that this is where it would ultimately
>> go):
>>
>> | myMenuBar myFileMenu |
>> super onViewOpened.
>> self view showMaximized.
>> myMenuBar := menuBarPresenter view controlDispatch addMenuBar: 'My
>> Bar'.
>> myFileMenu := myMenuBar commandBars activeMenuBar controls add:
>> xtpControlPopup id: 0 caption: '&File'.
>> myFileMenu commandBar controls add: xtpControlButton id: 105 caption:
>> '&Undo'.
>> Dolphin generates an exception at the last line.  The other lines
>> execute fine, so that when the view opens we have a menu bar and the
>> menu "File" showing.  The id of 105 above simply refers to a type of
>> menu, but it's arbitrary otherwise.  The debugger returns the
>> following in the top four lines:
>>
>> IDispatch>>invokeId: id flags: wFlags parms: parms retVal: varRes
>> IDispatch>>invokeId: id flags: wFlags parms: parms
>> IDispatch>>doesNotUnderstand:
>> PFGShell>>onViewOpened
>> The approach we're taking works in all of the other environments,
>> even Eiffel where we also have to use an ActiveX Control Wizard to
>> generate the interface. Basically, all we're doing is creating a menu
>> bar, adding the menu 'File' to the menu bar, and then attempting to
>> add an 'Undo' menu item to the File menu.  Dolphin appears to be
>> complaining about the return value.  We've tried a number of
>> variations with no success.
>>
> From the stack details I can see that your code is going through the
> #doesNotUnderstand: (DNU) processing, and this only really works in
> simple cases (like accessing properties). There's a lot of coverage of
> the IDispatch/DNU processing in the newsgroup archives.
>
> Your problem is that on the line 'myFileMenu := ...etc...' the return
> value of the add:id:caption: method (ICommandBarControls::Add()) is of
> type ICommandBarControl, which is the base interface type of the
> actual type added. ICommandBarControl does not actually implement
> #commandBar, so falls back on the DNU processing, and because the
> added type is actually implementing ICommandBarPopup the first message
> you send to it #commandBar works through the DNU mechanism (its a
> simple property access). However you've now lost all notion of the
> actual type, you just have an IDispatch, and so once again #controls
> will go through DNU. As a simple property access it works as well, and
> you get another IDispatch, but this time the add:id:caption: method
> call fails because the simple DNU processing isn't able to handle it.
>
> You can easily correct this by inserting a #downCast as follows:
>
> myFileMenu := (myMenuBar commandBars activeMenuBar controls
> add: xtpControlPopup
> id: 0
> caption: '&File') downCast.
> Or (more explicitly and not relying on type information):
>
> myFileMenu := (myMenuBar commandBars activeMenuBar controls
> add: xtpControlPopup
> id: 0
> caption: '&File') queryInterface: ICommandBarPopup .
> In a statically typed language you would need a similar down cast
> operation, or you would have to go through the late bound Invoke
> operation directly, so I would guess that your Eiffel code was not
> directly equivalent.
>
> BTW: Before doing this I suggest you work through it a little in the
> debugger. If you press Debug in the walkback you can go into the
> debugger and start evaluating some expressions to find out what is
> going on - just type them into the source. If you do that, you'll even
> be able to use the autocompletion to guide you as to the problem,
> because once in the debugger you have full type information for the
> variables, and it will be obvious that 'myFileMenu' doesn't implement
> #commandBar. This is a clue to why programming in the debugger can be
> so productive - it is programming with the concrete, rather than the
> abstract.
>
> It's a little disappointing that the CodeJock object model appears to
> consist of dispinterfaces, rather than dual interfaces. This means you
> can't see the inheritance model (not to mention the poorer performance
> of invoking through IDispatch). Seems odd, bearing in mind the
> underlying objects appears to be implemented in C++. Oh well, you can
> improve matters a bit by moving some of the generated interface
> classes around a bit to make the structure of the OM a bit easier to
> visualize. For example if you move ICommandBarPopup under
> ICommandBarControl (drag & drop in the browser) , and then right click
> on ICommandBarPopup and run Refactorings/Remove Duplicate Methods from
> the context menu, you'll be able to see how ICommandBarPopup extends
> ICommandBarControl. In fact it adds just two properties (#commandBar
> being one obviously) and a method.
>
> Hope this helps
>
> Blair
>


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Blair McGlashan-4
"Eric Taylor" <[hidden email]> wrote in message
news:[hidden email]...

> Hello Blair,
>
> Thank you for your most instructive reply.  Problem solved, except, of
> course, the one where we feel we have been reduced to wide-eyed children
> taken in by the wonderment of Smalltalk and, more particularly, Dolphin.
>
> When I generated the interface using Eiffel, I did it for the purpose of
> establishing a baseline for comparison, and not for technical reasons.
> Upon closer scrutiny, however, you are correct: The Eiffel code (or rather
> the underlying intermediate C code it generates) isn't directly
> equivalent.  Employing conditional assignment, Eiffel maps all eligible
> calls to the more generic (and less elegant) invoke mechanism, or so it
> would appear.

In this case there is no choice. dispinterfaces are used, so one has to call
them through Invoke. If you look at the interface classes Dolphin's type
library analyzer generates you will see that they do the same.

>...Since we have almost always written our own "controls" using Eiffel and
>its robust Windows Eiffel Library (WEL), we haven't had too much experience
>with _practical_ ActiveX in Eiffel.
>
> But I think it's only fair that we should be left wondering why it would
> work in C#, Eiffel, and Visual FoxPro (of all places), and not in Dolphin.

Well I hate to contradict you, but have you actually tried writing the
equivalent code in C#? If you did it would look like this:

    CommandBar myMenuBar = axCommandBars1.AddMenuBar("My Bar");
    CommandBarControl myFileMenu =
myMenuBar.CommandBars.ActiveMenuBar.Controls.Add(
XTPControlType.xtpControlPopup, 0, "&File", null, null);
    myFileMenu.CommandBar.Controls.Add(XTPControlType.xtpControlButton, 105,
"&Undo", null, null);

And when you tried to compile it you'd get the compilation error like the
following (I did):

C:\dev\CodeJockTest\Form1.cs(98): 'XtremeCommandBars.CommandBarControl' does
not contain a definition for 'CommandBar'

To make it work, you'd need to insert the "down cast" on line 2 that allows
you to declare the myFileMenu variable using the correct type, i.e:

    CommandBarPopup myFileMenu =
(CommandBarPopup)myMenuBar.CommandBars....&c

This down-cast presumably maps to a call to QueryInterface somewhere deep in
the bowels of the CLR.

The #downCast operation in Dolphin

> Certainly VFP has no notion of #downCast.  ....

No idea, but it probably late binds everything, similar to the DNU
processing but with two advantages:
    1) The syntax probably makes it possible to distinguish between property
assignments and method calls (which is not possible in Smalltalk as it has
no notion of properties)
    2) Microsoft probably cared about the late bound invocation use case a
lot more, and implemented it more completely.

>...Eiffel forbids it in production code, preferring instead a conversion
>pattern or a subclass of MISMATCH_CORRECTOR.

Well I don't know about Eiffel either, but in order to use this particular
control then a logical "down cast" is unavoidable - you have to down cast
the base type returned by the Add() method to the actual type you've just
added. In practice it would take the form of a QueryInterface call, rather
than having to be implemented as some down cast in whatever way the language
supports casting.

> Is there something that Dolphin could--or should--do to anticipate these
> kinds of inefficiencies in the design of the ActiveX component itself?
> With your permission, I would like to pass your answer on to Codejock
> Software.
>

I don't think I suggested that this interface design is necessarily a
deficiency in Codejock - I haven't really considered their design deeply
enough to see if there would be a better way (although using enumerations to
identify types in method calls is usually a bit of a code smell). Rather it
is a general issue supporting heterogeneous collections in statically typed
languages (such as the IDL, explicit or implicit, used to define COM
interfaces). In order to implement such a collection the collections
elements must share a common base class (or interface) that you can use to
define the collection interface - the most extreme form of this, of course,
is collections of Object. This design then necessitates a "down cast" to the
actual type of object you've accessed from the collection.

If I was complaining then my complaints based on my very limited glance at
it would be:
    1) Exlusive use of dispinterfaces where dual interfaces would normally
be preferred for their ability to better describe the inheritance
relationships and greater efficiency in use.
    2) Not marking non-extensible interfaces as non-extensible.

(2) is very common. One of the points about using IDispatch, is that it
allows you to provide true run-time extensibility - that is you can add
properties and methods at runtime that are not in the static type
information supplied by the object, or in the type library. Very few people
actually use this capability though, and I suspect this is the case here.
The interfaces should be marked as non-extensible so that the calling
environment knows not to attempt to bind members that are not described in
the type library. The effect of this in Dolphin after having generated the
interfaces would be that Dolphin would not attempt to late-bind methods and
properties at all, and your problem would have been more immediately
obvious.

> From your reply we now understand the central importance of DNU.  Using
> your reply as sort of a cursor, yes, there _is_ much in the newsgroup
> archives. In general, though, we've had mixed results searching the
> archives.  Sometimes we find an answer right away.  Many times we do not,
> even though the answers may be lurking there.  Forum posts are not always
> couched in terms that lend themselves to indexing.  Rather than trouble
> you, or Andy, or even other professionals in the forum, with questions
> like this, we would like to be able to go to some sort of a knowledge
> base.  It's our understanding that there was a Dolphin wiki at one time,
> and the archives speak of its eventual reestablishment.  Is there any ETA
> of when this might happen?
>

Well actually my opinion on those sort of things is that they are better
maintained in the community. Andy probably disagrees, but he knows he won't
be getting much help from me in sorting it out :-)

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Unexplainable ActiveX Error

Eric Taylor
Hello Blair,

>Well I hate to contradict you...

This is a most welcome contradiction.  I went off exploring in greater
depth the lack of an error in the C# compilation.  In short, I don't
know.  As you say, we should have received one.

When I typed in the C# code, I was working from their tutorial, not
their sample.  Bottom line: My omission of the explicit cast was an
oversight. Since Eiffel does not support explicit casts, I suppose my
mind chose to block out such horror ;).  Seriously though,
theoretically, I should not have had success.  When I bring up the C#
example they provide, the explicit cast is very clearly and very
deliberately there, as you say it should be.

Yet I can compile and run my little test over and over.  Since my
overarching goal is to make sure that Codejock's controls work properly
in Dolphin, I simply sent this off to Codejock's technical support.  I
don't have time to explore in depth (not that there should be any depth
here) what is going on.

I can speak more confidently about Eiffel, however, and I can assure you
that there are no unexplainable quirks as in the C# case above.  From
the intermediate C code, it appears, on the surface, that Eiffel is
taking the same approach as VFP must be.  Eiffel, like Smalltalk, has no
notion of properties either.  It sees a variable, as Smalltalk calls it,
as nothing more than a method with no arguments, but returning a type
that is the type of the variable.  But the Eiffel compiler knows, as the
intermediate C code bears out.  I say all this because VFP makes an
exquisite difference between properties and methods (and events), so I
suspect its underlying implementation could very easily late-detect the
difference between the two types of calls and avoid the need for the
developer to interpose.  And, although Eiffel normalizes the distinction
at the developer's level, it makes an equally exquisite distinction
between the two during compilation and, therefore, like VFP, would
appear not to require down-casting _by the developer_.

>I don't think I suggested that this interface design is necessarily a
>deficiency in Codejock - I haven't really considered their design
deeply
>enough to see if there would be a better way...

Deficiency, no, but inefficiency, yes:

>It's a little disappointing that the CodeJock object model appears to
>consist of dispinterfaces, rather than dual interfaces. This means you
>can't see the inheritance model (not to mention the poorer performance
>of invoking through IDispatch). Seems odd, bearing in mind the
>underlying objects appears to be implemented in C++. Oh well, you can
>improve matters a bit...

We would agree with you, and it was only in this regard that we wanted
to pass your response onto Codejock.  In all fairness to them, though,
they're trying to satisfy as many different environments as possible
(even though their website would lead you to believe otherwise).  I have
spoken to Mike in technical support, and he is most interested in our
reporting back to him on the results of our incorporation of their
controls into Dolphin.

Blair, I would like to interject at this point that I do hope I'm not
coming across as disappointed with Dolphin.  I don't want other readers
of my various posts to construe them that way, either.  Oftentimes posts
to a forum come after many hours, or even days, of frustration in some
minutiae a long way off from the main goal.  That frustration may
unintentionally flow out onto the page.  There are three of us here, two
skeptics, who are still using DCE, and I, the only one so far who has
taken the leap with DPE.  My love of the Eiffel language
notwithstanding, it would be impossible now to dissuade me from
Smalltalk or from Dolphin.  I'm not exaggerating when I say that I'm
having the time of life, and any problems or issues that I encounter
with Dolphin, if indeed they could be called that, will be well worth
the trouble of overcoming.

Cheers,

Eric