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 |
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 |
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 |
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 |
"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 |
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. it's intention :-( CU, Udo |
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 > |
"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 |
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 |
Free forum by Nabble | Edit this page |