I'm trying to get a simple COM server object (implemented in
Dolphin) to work. I've created a model class called ListKeeper which has the instance method >>addItem: aString which adds the string to the list. I want to expose this class and method as a COM server object. I've created COMListKeeper (subclass of COMInterfaceImp) and added the class methods: >>clsid "Generated by cut and pasting the result of: GUID newUnique displayString" ^ CLSID fromString: '{4D773A77-7492-4AB3-862D-201B19D36C31}' >>progID "Answer the receiver's programmatic ID, typically used from scripting or macro languagues to create instances of the receiver, e.g. 'Dolphin.Object.1'" ^ 'ListKeeper.1' and the instance methods: >>supportedInterfaces "Answer the set of interface classes supported by the receiver." ^#(##(IListKeeper)) >>addItem: addItem "Implement the IListKeeper::addItem() interface function." Sound beep. ^E_NOTIMPL. #todo "Implement me" "Need to connect this method up to ListKeeper>>addItem:. For now, just testing to verify it's called" Also created the class IListKeeper (subclass of IUknown) with the class methods: >>clsid "Used the same CLSID as from COMListKeeper." ^ CLSID fromString: '{4D773A77-7492-4AB3-862D-201B19D36C31}' >>defineFunctions "Declare the virtual functions supported by the receiver." self defineFunction: #addItem: argumentTypes: 'BSTR' Additionally, I've performed the following in a workspace: IListKeeper compileFunctionsIntoImplementor: COMListKeeper COMListKeeper register QUESTION: When I try to invoke my COM server object from the calling application (a web browser), I see the Dolphin splash screen for several seconds, then it disappears and that's it. I don't hear the beep that should sound if the COMListKeeper>>addItem: method was called. Is there a simpler test that I can perform to determine that my Dolphin COM server object is working and that the problem is with my calling application (web browser)? What's the simplest way to call and test my COM server object? Thanks. |
drtau wrote:
> ... > QUESTION: When I try to invoke my COM server object from the > calling application (a web browser), I see the Dolphin splash screen > for several seconds, then it disappears and that's it. I don't hear the > beep that should sound if the COMListKeeper>>addItem: method > was called. Is there a simpler test that I can perform to determine that > my Dolphin COM server object is working and that the problem is > with my calling application (web browser)? What's the simplest way > to call and test my COM server object? Thanks. > > > The simplest way to develop COM servers I've found is to create an instance of your interface from an internal instance of the server. IMyInterface on: COMInterfaceSubclass this'll let you do all the development of your server without worrying about the COM stuff. You can even write unit tests easily. As far as your server not responding to the web browser, I noticed that you didn't register the class factory in the code you submitted. Try opening the dolphin image that contains the COM server, then type COMServer registerClassFactory Then leave the image open and try to use your web browser again. If that doesn't help, then try looking in the newsgroup archives. I know that Blair had some good suggestions when COM doesn't want to play nice. Eric |
Thanks for the help Eric. I tried your suggestions (calling
COMListKeeper registerClassFactory) but still no luck. I keep the image running with the code loaded, and when I make the call from the web browser, I get the browser error: "Object doesn't support this property or method" In the Transcript I see: "Server shutdown request". If I set a breakpoint in COMClassFactory>>CreateInstance:riid:ppvObject:, the debugger appears, so it is reaching that point. And, to the best of my newbie COM knowledge, it seems to be working ok?? (The "inst" temp variable is set to "a COMListKeeper"). But, alas, something must be wrong/missing because I get the error message and my test method (COMListKeeper>>beep) is not called. Some things that I was wondering about: - Do I need to do anything (eg. create a class, ...) in terms of the COM class factory, or do I _only_ need to call "COMListKeeper registerClassFactory"? - In my IListKeeper>>defineFuctions method, do I need to call "super defineFuctions" to invoke the code in IUknown? - Whenever I change/add a method, I perform: IListKeeper compileFunctions. IListKeeper compileFunctionsIntoImplementor: COMListKeeper. COMListKeeper register. and whenever I start a fresh image and load the package, I perform: COMListKeeper registerClassFactory is this correct? Thanks. Eric Winger wrote: > drtau wrote: > > > > ... > > > QUESTION: When I try to invoke my COM server object from the > > calling application (a web browser), I see the Dolphin splash screen > > for several seconds, then it disappears and that's it. I don't hear the > > beep that should sound if the COMListKeeper>>addItem: method > > was called. Is there a simpler test that I can perform to determine that > > my Dolphin COM server object is working and that the problem is > > with my calling application (web browser)? What's the simplest way > > to call and test my COM server object? Thanks. > > > > > > > > The simplest way to develop COM servers I've found is to create an > instance of your interface from an internal instance of the server. > > IMyInterface on: COMInterfaceSubclass > > this'll let you do all the development of your server without worrying > about the COM stuff. You can even write unit tests easily. > > As far as your server not responding to the web browser, I noticed that > you didn't register the class factory in the code you submitted. Try > opening the dolphin image that contains the COM server, then type > > COMServer registerClassFactory > > Then leave the image open and try to use your web browser again. If that > doesn't help, then try looking in the newsgroup archives. I know that > Blair had some good suggestions when COM doesn't want to play nice. > > Eric |
Also, do I need a marshalling DLL? The only arguments I pass
are strings. Thanks. drtau wrote: > Thanks for the help Eric. I tried your suggestions (calling > COMListKeeper registerClassFactory) but still no luck. I keep > the image running with the code loaded, and when I make the > call from the web browser, I get the browser error: > > "Object doesn't support this property or method" > > In the Transcript I see: "Server shutdown request". If I set > a breakpoint in COMClassFactory>>CreateInstance:riid:ppvObject:, > the debugger appears, so it is reaching that point. And, to the best of > my newbie COM knowledge, it seems to be working ok?? (The "inst" > temp variable is set to "a COMListKeeper"). But, alas, something must > be wrong/missing because I get the error message and my test method > (COMListKeeper>>beep) is not called. > > Some things that I was wondering about: > > - Do I need to do anything (eg. create a class, ...) in terms of the COM class > factory, or do I _only_ need to call "COMListKeeper registerClassFactory"? > > - In my IListKeeper>>defineFuctions method, do I need to call > "super defineFuctions" to invoke the code in IUknown? > > - Whenever I change/add a method, I perform: > > IListKeeper compileFunctions. > IListKeeper compileFunctionsIntoImplementor: COMListKeeper. > COMListKeeper register. > > and whenever I start a fresh image and load the package, I perform: > > COMListKeeper registerClassFactory > > is this correct? > > Thanks. > > Eric Winger wrote: > > > drtau wrote: > > > > > > > ... > > > > > QUESTION: When I try to invoke my COM server object from the > > > calling application (a web browser), I see the Dolphin splash screen > > > for several seconds, then it disappears and that's it. I don't hear the > > > beep that should sound if the COMListKeeper>>addItem: method > > > was called. Is there a simpler test that I can perform to determine that > > > my Dolphin COM server object is working and that the problem is > > > with my calling application (web browser)? What's the simplest way > > > to call and test my COM server object? Thanks. > > > > > > > > > > > > > The simplest way to develop COM servers I've found is to create an > > instance of your interface from an internal instance of the server. > > > > IMyInterface on: COMInterfaceSubclass > > > > this'll let you do all the development of your server without worrying > > about the COM stuff. You can even write unit tests easily. > > > > As far as your server not responding to the web browser, I noticed that > > you didn't register the class factory in the code you submitted. Try > > opening the dolphin image that contains the COM server, then type > > > > COMServer registerClassFactory > > > > Then leave the image open and try to use your web browser again. If that > > doesn't help, then try looking in the newsgroup archives. I know that > > Blair had some good suggestions when COM doesn't want to play nice. > > > > Eric |
I haven't had a chance to read through your previous post, but here's
some comments Blair had about marshalling in response to a post I had... My question... However, I've got a wee bit of a problem in trying to connect to the COM > implmentation object. Whenever I do a ... createObject: 'foo.bar', COM > starts up the Dolphin server, but the server times out without a valid > connection being established. > ....[snip]... Blair's response.... If you are still experiencing a problem with this, then I have two suggestions: 1) Check that marshalling code is available for the interfaces you are trying to implement. The symptoms of marshalling code not being available are usually that the server is started, and the class factory invoked to create the object which is then sucessfully queried for the required interface, but this never makes it back to the client. In order to perform out-of-process COM calls it is necessary to have "marshalling" code available so that COM can serialise the calls for IPC. This requires that either the interface be "automation compatible" (basically this means it is VB compatible and uses only the automation datatypes such as VARIANT, BSTR, LONG, etc), in which case OLEAUT32.DLL can do the marshalling use information from the type library, or that a custom marshalling DLL has been built. The latter involves running a C compiler against the *_i.c and *_p.c output that MIDL tool creates from the IDL file. You can use the OLEVIEW tool to find out is a marshalling DLL is registered for the interfaces. 2) If the server is starting and marshalling code is available, the class factory may be failing to register or failing to create an instance. Set a breakpoint or two in Dolphin's class factory code (e.g. at the start of the COMClassFactory>>register and COMClassFactory>>CreateInstance:etc) and then attempt to create the object from your client again. On hitting the breakpoint Dolphin will pop a walkback in the normal way, and you can then invoke the debugger and debug through. The same technique can be used for debugging one's COM objects by inserting 'self halt's in the implementations of the As Bill suggests it is sensible during development to pre-start a development image in which the class factories are registered and then let that service automation requests for the objects. Registering a factory for COM server class need only be done once and there should be no need to revoke the class factories. .... I'll try to respond to your other post soon, Eric drtau wrote: > Also, do I need a marshalling DLL? The only arguments I pass > are strings. Thanks. > > drtau wrote: > > >>Thanks for the help Eric. I tried your suggestions (calling >>COMListKeeper registerClassFactory) but still no luck. I keep >>the image running with the code loaded, and when I make the >>call from the web browser, I get the browser error: >> >>"Object doesn't support this property or method" >> >>In the Transcript I see: "Server shutdown request". If I set >>a breakpoint in COMClassFactory>>CreateInstance:riid:ppvObject:, >>the debugger appears, so it is reaching that point. And, to the best of >>my newbie COM knowledge, it seems to be working ok?? (The "inst" >>temp variable is set to "a COMListKeeper"). But, alas, something must >>be wrong/missing because I get the error message and my test method >>(COMListKeeper>>beep) is not called. >> >>Some things that I was wondering about: >> >>- Do I need to do anything (eg. create a class, ...) in terms of the COM class >>factory, or do I _only_ need to call "COMListKeeper registerClassFactory"? >> >>- In my IListKeeper>>defineFuctions method, do I need to call >>"super defineFuctions" to invoke the code in IUknown? >> >>- Whenever I change/add a method, I perform: >> >> IListKeeper compileFunctions. >> IListKeeper compileFunctionsIntoImplementor: COMListKeeper. >> COMListKeeper register. >> >> and whenever I start a fresh image and load the package, I perform: >> >> COMListKeeper registerClassFactory >> >> is this correct? >> >>Thanks. >> >>Eric Winger wrote: >> >> >>>drtau wrote: >>> >>>... >>> >>> >>>>QUESTION: When I try to invoke my COM server object from the >>>>calling application (a web browser), I see the Dolphin splash screen >>>>for several seconds, then it disappears and that's it. I don't hear the >>>>beep that should sound if the COMListKeeper>>addItem: method >>>>was called. Is there a simpler test that I can perform to determine that >>>>my Dolphin COM server object is working and that the problem is >>>>with my calling application (web browser)? What's the simplest way >>>>to call and test my COM server object? Thanks. >>>> >>>> >>>> >>>> >>>The simplest way to develop COM servers I've found is to create an >>>instance of your interface from an internal instance of the server. >>> >>>IMyInterface on: COMInterfaceSubclass >>> >>>this'll let you do all the development of your server without worrying >>>about the COM stuff. You can even write unit tests easily. >>> >>>As far as your server not responding to the web browser, I noticed that >>>you didn't register the class factory in the code you submitted. Try >>>opening the dolphin image that contains the COM server, then type >>> >>>COMServer registerClassFactory >>> >>>Then leave the image open and try to use your web browser again. If that >>>doesn't help, then try looking in the newsgroup archives. I know that >>>Blair had some good suggestions when COM doesn't want to play nice. >>> >>>Eric >>> > |
In reply to this post by drtau-3
In looking at your original post you wrote:
" >>addItem: addItem "Implement the IListKeeper::addItem() interface function." Sound beep. ^E_NOTIMPL. #todo "Implement me" "Need to connect this method up to ListKeeper>>addItem:. For now, just testing to verify it's called"" I'm just thinking out loud here, but.... functions are case sensitive in COM I believe. You said that you were calling your COM server from a web browser. Are you calling the function as lower case? The only reason I bring this up is that I've only dealt with upper-case functions in COM. So the lower case seems strange. Something else I should verify is that you put the beep in the implementation class, not the interface class. (based on the code, I think you did, but I wanted to make sure) drtau wrote: > Thanks for the help Eric. I tried your suggestions (calling > COMListKeeper registerClassFactory) but still no luck. I keep > the image running with the code loaded, and when I make the > call from the web browser, I get the browser error: > > "Object doesn't support this property or method" This seems to me that you are connecting, but it can't find the method you are calling. You might double check that the browser is calling the method you think is being called. Also, something else to double check is if the browser trying to use the IListKeeper interface. > > In the Transcript I see: "Server shutdown request". If I set > a breakpoint in COMClassFactory>>CreateInstance:riid:ppvObject:, > the debugger appears, so it is reaching that point. And, to the best of > my newbie COM knowledge, it seems to be working ok?? (The "inst" > temp variable is set to "a COMListKeeper"). But, alas, something must > be wrong/missing because I get the error message and my test method > (COMListKeeper>>beep) is not called. > > Some things that I was wondering about: > > - Do I need to do anything (eg. create a class, ...) in terms of the COM class > factory, or do I _only_ need to call "COMListKeeper registerClassFactory"? I didn't have to add any subclasses of COMClassFactory. > > - In my IListKeeper>>defineFuctions method, do I need to call > "super defineFuctions" to invoke the code in IUknown? > > - Whenever I change/add a method, I perform: > > IListKeeper compileFunctions. > IListKeeper compileFunctionsIntoImplementor: COMListKeeper. > COMListKeeper register. > > and whenever I start a fresh image and load the package, I perform: > > COMListKeeper registerClassFactory > > is this correct? think so, btw - Does the addItem function work when you invoke it internally in Dolphin? i.e. Can you make it beep when do... i := IListKeeper on: COMListKeeper. i AddItem: blah. hope some of this helps, Eric > > Thanks. > > > Eric Winger wrote: > > >>drtau wrote: >> >>... >> >> >>>QUESTION: When I try to invoke my COM server object from the >>>calling application (a web browser), I see the Dolphin splash screen >>>for several seconds, then it disappears and that's it. I don't hear the >>>beep that should sound if the COMListKeeper>>addItem: method >>>was called. Is there a simpler test that I can perform to determine that >>>my Dolphin COM server object is working and that the problem is >>>with my calling application (web browser)? What's the simplest way >>>to call and test my COM server object? Thanks. >>> >>> >>> >>> >>The simplest way to develop COM servers I've found is to create an >>instance of your interface from an internal instance of the server. >> >>IMyInterface on: COMInterfaceSubclass >> >>this'll let you do all the development of your server without worrying >>about the COM stuff. You can even write unit tests easily. >> >>As far as your server not responding to the web browser, I noticed that >>you didn't register the class factory in the code you submitted. Try >>opening the dolphin image that contains the COM server, then type >> >>COMServer registerClassFactory >> >>Then leave the image open and try to use your web browser again. If that >>doesn't help, then try looking in the newsgroup archives. I know that >>Blair had some good suggestions when COM doesn't want to play nice. >> >>Eric >> > |
In reply to this post by drtau-3
"drtau" <[hidden email]> wrote in message
news:[hidden email]... > Also, do I need a marshalling DLL? The only arguments I pass > are strings. Thanks. Yes (or a type library). Even though the arguments are only strings, COM needs some description of the interface so that it knows that the arguments are only strings (and how many there are, and in which direction they are going) in order that it can copy the data between processes (or threads). The symptoms you describe (of seeing the class factory invoked, and the instance of your object created) are absolutely classic for the absence of marshalling code. COM proceeds as far as creating the object and querying off the interface, but then fails on attempting to marshal the interface. This is because the COM architecture allows an object to perform its own marshalling, and hence COM doesn't find out that it can't marshal the interface until quite late on. Unfortunately the error reporting doesn't make it clear that the problem is a lack of marshalling information/code. My recommendation when developing COM server's in Dolphin would be to proceed as follows: 1) Don't start by defining the interface in Dolphin using the #defineFunctions method and #compileInterfaceIntoImplementor: (or whatever it is), but instead start by defining the IDL. You can always revise the IDL later so don't get into analysis paralysis when defining the interface. Generally I would recommend sticking to automation compatible interfaces (i.e. use the VB compatible types) since this will make the object usable from the widest possible range of clients, and simplifies marshalling issues. 2) Run the IDL through 'midl /Oicf'. Repeat until it "compiles" (which can be tedious since MIDLs error reporting is, ahem, not top notch). 3) Now use Dolphin's Active-X component wizard to generate the Dolphin interface class(es). In loading the type library it will automatically register, which takes care of the marshalling issue (assuming only automation compatible types have been used - if not then a marshalling DLL will have to be compiled up). 4) Add a COMInterfaceImp subclass that will implement the COM 'server', e.g. MyCOMServer. 5) Add a #supportedInterfaces method to MyCOMServer, this needs to return an array containing the interface class generated at (3). 6) Add a #clsid method to the class side of MyCOMServer which answers the correct CLSID (this should be the one associated with the coclass declaration in the IDL). - if the coclasses default interface is specified in the IDL then the generated wrapper class will have a clsid method that can simply be copied over). You might also like to add a #progId method which answers the user-friendly string name of the object. 7) In Dolphin drag/copy across COM interface's methods (which can be done by dragging the category, or the method protocol) to MyCOMServer. If the protocol is dragged the implementations will all consist of 'Error notYetImplemented'. If not replace the bodies with 'self halt'. Later these can be fleshed out with a bit of 'programming in the debugger'. 8) Send #registerClassFactor to MyCOMServer to register a class factory. Now with Dolphin running it should be possible to create an instance of the COM object, either from within Dolphin (e.g. 'IMyInterface new' or 'IMyInterface createObject: 'MyCOMObject.1'), or from some external client. Because the development system is running with a class factory registered, actual registry entries are not needed, even for the external client. Personally I wouldn't start fleshing out the object implementation until this point, because I find it so much easier to program COM servers in the debugger when I can see the actual types of the arguments that arrive. 9) Send #register to MyCOMServer to create the necessary registry entries. It should now be possible to instantiate a MyCOMServer object from an external client (such as VB) with the Dolphin development image shut down. In Dolphin 4.0 an out-of-process (.EXE based) server is the only option, but in the forthcoming Dolphin 5 it is also possible, at this point, to deploy the result as an in-process server DLL, which is both self registering and has the type-library bound in. Regards Blair |
In reply to this post by Eric Winger-4
Eric Winger wrote:
> I'm just thinking out loud here, but.... functions are case sensitive in > COM I believe. You said that you were calling your COM server from a web > browser. Are you calling the function as lower case? The only reason I > bring this up is that I've only dealt with upper-case functions in COM. > So the lower case seems strange. Yes, I'm calling the function as lowercase. I'll try capitalizing the first letterto see if it makes a difference. > > > Something else I should verify is that you put the beep in the > implementation class, not the interface class. (based on the code, I > think you did, but I wanted to make sure) Yes. I just wanted a quick test to see if the implementation methodis being called (which I gather it should be?). Thanks again Eric. |
In reply to this post by Blair McGlashan
Thanks Blair for the Step-by-Step! It's very helpful for someone new
to COM. :-) I'm a bit stuck on the MIDL compiler. I've created the follwing IDL file (lk.idl): ============= import "unknwn.idl"; [ object, uuid(799845b6-683f-453a-a953-dbfc4fd2e88c), local ] interface ListKeeper : IUnknown { void Beep(void); void AddItem( [in, string] unsigned char *anItemString ); } ============= And then executed: midl /Oicf /I C:\Progra~1\Micros~4\VC98\Include lk.idl from a DOS prompt. It complies fine and generates: dlldata.c, lk.h, lk_i.c lk_p.c, but does not create lk.tlb. I tried to force it with the switch "/tlb lk.tlb", but still no luck. Any idea how to create the lk.tlb file so I can bring it into Dolphin with: AXTypeLibraryAnalyzer open: 'lk.tlb' ? |
> from a DOS prompt. It complies fine and generates:
> dlldata.c, lk.h, lk_i.c lk_p.c, but does not create lk.tlb. I tried > to force it with the switch "/tlb lk.tlb", but still no luck. Any idea > how to create the lk.tlb file so I can bring it into Dolphin with: > AXTypeLibraryAnalyzer open: 'lk.tlb' ? A "library" statement in your IDL will probably take care of it. Rogerson is my reference of choice for that kind of stuff. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
"Bill Schwab" <[hidden email]> wrote in message
news:9vb8mf$e81pp$[hidden email]... > > from a DOS prompt. It complies fine and generates: > > dlldata.c, lk.h, lk_i.c lk_p.c, but does not create lk.tlb. I tried > > to force it with the switch "/tlb lk.tlb", but still no luck. Any idea > > how to create the lk.tlb file so I can bring it into Dolphin with: > > AXTypeLibraryAnalyzer open: 'lk.tlb' ? > > A "library" statement in your IDL will probably take care of it. Exactly. Without a library statement MIDL does not emit a .tlb. >...Rogerson > is my reference of choice for that kind of stuff. Alternatively use the 'Browse IDL' command in the AX Component Wizard (or alternatively Microsoft's OLEVIEW tool) to look at the IDL for an existing component. BTW: The AX Component Wizard can open and register new typelibs by pressing first the Browse... button, and then the Open... button in the subsequent dialog, and then changing the file type dropdown in the resulting File Open dialog to display *.tlb's. Regards Blair |
Thanks for the "library" tip - that did the trick! It was also very useful to use
the "Browse IDL" command in Dolphin to see other examples - cool!! I don't get the "Object doesn't support this property or method" error anymore (as a matter of fact I don't get any error messages), but my ListKeeper>>Beep method is still not being executed. My lk.idl file is: ======== import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(799845b6-683f-453a-a953-dbfc4fd2e88c), oleautomation, local ] interface IListKeeper : IUnknown { void Beep(void); void AddItem( [in, string] BSTR anItemString ); }; [ uuid(df3f985d-ec2c-47d3-a517-c3db55a43f64) ] library ListKeeperLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(9cd4d38e-6000-4fe7-8360-dff9048a0504) ] coclass ListKeeper { [default] interface IListKeeper; }; }; ======= is this correct? Am I missing anything? Also, when I invoke the >>Beep method from the web browser I see "Server shutdown request" in the Transcript - is this ok? Does it mean anything? Can I put a breakpoint somewhere in the COM code in Dolphin to trace the incoming method call? Thanks again. - drtau Blair McGlashan wrote: > "Bill Schwab" <[hidden email]> wrote in message > news:9vb8mf$e81pp$[hidden email]... > > > from a DOS prompt. It complies fine and generates: > > > dlldata.c, lk.h, lk_i.c lk_p.c, but does not create lk.tlb. I tried > > > to force it with the switch "/tlb lk.tlb", but still no luck. Any idea > > > how to create the lk.tlb file so I can bring it into Dolphin with: > > > AXTypeLibraryAnalyzer open: 'lk.tlb' ? > > > > A "library" statement in your IDL will probably take care of it. > > Exactly. Without a library statement MIDL does not emit a .tlb. > > >...Rogerson > > is my reference of choice for that kind of stuff. > > Alternatively use the 'Browse IDL' command in the AX Component Wizard (or > alternatively Microsoft's OLEVIEW tool) to look at the IDL for an existing > component. > > BTW: The AX Component Wizard can open and register new typelibs by pressing > first the Browse... button, and then the Open... button in the subsequent > dialog, and then changing the file type dropdown in the resulting File Open > dialog to display *.tlb's. > > Regards > > Blair |
"drtau" <[hidden email]> wrote in message
news:[hidden email]... > Thanks for the "library" tip - that did the trick! It was also very useful to use > the "Browse IDL" command in Dolphin to see other examples - cool!! > > I don't get the "Object doesn't support this property or method" error anymore > (as a matter of fact I don't get any error messages), but my ListKeeper>>Beep > method is still not being executed. > > My lk.idl file is: >.... oleautomation, > local > ] > interface IListKeeper : IUnknown > { > void Beep(void); >... > ======= > > is this correct? Am I missing anything? I believe that 'local' attribute is telling the MIDL compiler that no marshalling code is required for that interface. Since you need to remote it, I'd imagine this is unhelpful. > > Also, when I invoke the >>Beep method from the web browser I see > "Server shutdown request" in the Transcript - is this ok? Does it mean anything? It means that an instance of the object is being created, and then destroyed. When running as an out-of-process server Dolphin uses the destruction of a COM object as an opportunity to check whether it needs to remain "up". That informational debug message gets printed as part of the "keep alive" processing. > Can I put a breakpoint somewhere in the COM code in Dolphin to trace > the incoming method call? You can, e.g. in the class factory's create instance related methods (can't remember the selectors offhand, but try looking in COMClassFactory). I suspect you will find that the COM object is created just fine and the create instance call appears to succeed, but that the something fails down in the depths when COM is passing back the interface pointer to the IExplore process. As I said before this is a classic symptom of marshalling support not being available for the interface. Regards Blair |
> > My lk.idl file is:
> >.... oleautomation, > > local > > ] > > interface IListKeeper : IUnknown > > { > > void Beep(void); > >... > > ======= > > Been doing a bit of reading, trying to get a grasp of COM, and I was wondering if "interface IListKeeper : IUnknown" in my IDL is correct; or whether it should be "interface IListKeeper : IDispatch" as I am using OLE automation. |
Well, I think I'm getting a bit further. Currently, my idl file is:
========= import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(799845b6-683f-453a-a953-dbfc4fd2e88c), oleautomation, pointer_default(unique) ] interface IListKeeper : IDispatch { [id(1)] HRESULT AddItem( [in, string] BSTR anItemString ); }; [ uuid(df3f985d-ec2c-47d3-a517-c3db55a43f64), version(1.0) ] library ListKeeperLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(9cd4d38e-6000-4fe7-8360-dff9048a0504) ] coclass ListKeeper { [default] interface IListKeeper; }; }; ======= This results in the class IListKeeper being generated as a subclass of IDispatch. I've made COMListKeeper to be subclass of AXDispatchImpAbstract, and have added the following methods for it (along with the COM usuals): >> supportedInterfaces "Answer the set of interface classes supported by the receiver. This is IDispatch plus the IDispatch subclass of the dual interface." ^Array with: IDispatch with: IListKeeper >> idsOfNames "Answer a Dictionary containing the id for each function name." idsOfNames isNil ifTrue: [idsOfNames := Dictionary new. idsOfNames at: 'AddItem' put: 1]. ^ idsOfNames >> idsOfNames: anArray whenNotKnown: execeptionHandler "Answer a <sequencedReadableCollection> of <SmallInteger> being the dispatch ids of each of the <readableString> names in the argument <Array>, anArray. Evaluate the <monadicValuable>, exceptionHandler, for each of the names which are not recognised." ^ anArray collect: [:aName | self idsOfNames at: aName ifAbsent: [execeptionHandler value: aName]] PROBLEM: When the COM client calls the "AddItem" function, the "dispid" argument in the method >>invokeId: dispid flags: callTypeFlags withArguments: argArray is -5511, not 1 as I would have expected. Anyone knows why this would be? Any help would be appreciated. (Also, if I don't call "AddItem" at all from the COM client (which is Internet Explorer) but just reference the COM server in web page like: <object id="ListKeeper" classid="clsid:9CD4D38E-6000-4FE7-8360-DFF9048A0504"> then the >>InvokeID:.. method is called twice with a dispid of -514. I'm surprised it's even called since the web page makes no function calls. Any explainations? Thanks.) |
"drtau" <[hidden email]> wrote in message
news:[hidden email]... > Well, I think I'm getting a bit further. Currently, my idl file is: > ...[snip]... > > This results in the class IListKeeper being generated as a subclass of > IDispatch. I've made COMListKeeper to be subclass of AXDispatchImpAbstract, >... Since you have a type-library, and the interface is "dual", it is easier to let the type-library subsystem take care of mapping IDispatch calls to the custom methods for you. This is most easily done in Dolphin by subclassing AXDualImp. See COMRandomStream sample (IDL and .PAC contents pasted below) for an example of how to do this. It is still possible to implement IDispatch yourself, however, just more work since you'll need to take care of mapping names to dispids (as you have done). >... > and have added the following methods for it (along with the COM usuals): > ... [code snipped]... > PROBLEM: When the COM client calls the "AddItem" function, the "dispid" argument in > the method >>invokeId: dispid flags: callTypeFlags withArguments: argArray > is -5511, not 1 as I would have expected. Anyone knows why this would be? > Any help would be appreciated. What makes you think IE is attemping to invoke AddItem :-). A lot of the negative DISPIDs are reserved for standard purposes. -5512 is DISPID_AMBIENT_DLCONTROL. This is something to do with downloading stuff, though I don't quite understand why it should be called since it seems to be related to hosting the IE control, rather than being hosted by it. For further information you'll have to look it up on MSDN. > (Also, if I don't call "AddItem" at all from the COM client (which is Internet Explorer) > but just reference the COM server in web page like: > <object id="ListKeeper" classid="clsid:9CD4D38E-6000-4FE7-8360-DFF9048A0504"> > then the >>InvokeID:.. method is called twice with a dispid of -514. I'm > surprised it's even called since the web page makes no function calls. > Any explainations? Thanks.) -514 is a standard DISPID, DISPID_ENABLE. I would think that IE is sending an instruction to enable/disable, in case your object is a visual control. I'd suggest testing your object initially using some other client, at least initially, since it seems that IE is going to attempt to invoke a lot of "optional" methods. Also it is only calling through IDispatch, and it is handy to test both late and early bound implementations of a dual interface. VB is probably the best test client if you have it. Regards Blair ------------------------------------- // Random.idl : IDL source for COM Random Stream sample // // This IDL file needs to be compiled with MIDL (midl /Oicf random.idl) and the // resulting .TLB registered. Opening the .tlb in either the Dolphin AX component // wizard, or in VB, will register it. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(0E2CEA3B-E6C4-11D2-833B-0020AFAB8EFE), dual, helpstring("IRandomStream Interface"), pointer_default(unique), nonextensible ] interface IRandomStream : IDispatch { [id(1), helpstring("Answer the next random number in the stream")] HRESULT Next([out,retval]long* plNext); [propget, id(2), helpstring("Current random seed")] HRESULT Seed([out, retval] long *pVal); [propput, id(2), helpstring("Current random seed")] HRESULT Seed([in] long newVal); [propget, id(3), helpstring("Lower bound of range of random numbers generated")] HRESULT LowerBound([out, retval] long *pVal); [propput, id(3), helpstring("Lower bound of range of random numbers generated")] HRESULT LowerBound([in] long newVal); [propget, id(4), helpstring("Upper bound of range of random numbers generated")] HRESULT UpperBound([out, retval] long *pVal); [propput, id(4), helpstring("Upper bound of range of random numbers generated")] HRESULT UpperBound([in] long newVal); }; [ uuid(0E2CEA2F-E6C4-11D2-833B-0020AFAB8EFE), version(1.0), helpstring("Random 1.0 Type Library") ] library RANDOMLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); interface IRandomStream; [ uuid(A1D42F35-E6C0-11D2-833B-0020AFAB8EFE) ] coclass RandomStream { [default] interface IRandomStream; }; }; ------------------------------------- | package | package := Package name: 'COM Random Stream'. package paxVersion: 0; basicComment: 'Dolphin Smalltalk Random Stream COM Server Sample Copyright (c) Object Arts Ltd, 1997-2000. Portions copyright IMRglobal Ltd, 1997. The RandomStream Sample demonstrates Dolphin''s basic COM facilities (i.e. calling and implementation of COM interfaces, Dolphin as an out-of-process server, class factories, automatic reference counting, etc). N.B. This package must be distributed with its type library, Random.tlb, if it is to be invoked from another process, e.g. VB. The package provides client (IRandomStream) and server (COMRandomStream) implementations of a simple Random Number Generator object. The interface is OLE automation compatible, and can therefore be used without a marshalling DLL if the supplied type library is first registered. This sample is compatible with Visual Basic 5 and later. When VB (or another tool) is used as a client then Dolphin will be started automatically in embedded (headless) mode and will act as the server. Dolphin automatically registers the class factory when this package is installed (see the post install script to see how this is done). When you release the last interface pointer from a client then Dolphin should close down automatically. You can also use Dolphin as both client and server. Note: This sample is not compatible with the sample of the same name in the 2.1 and earlier releases of Dolphin.'. package basicPackageVersion: ''. "Add the package scripts" package basicScriptAt: #postinstall put: ' "Register a class factory for the Random Number Stream" COMRandomStream register; registerClassFactory'. package basicScriptAt: #preuninstall put: ' "Revoke the class factory registration." COMRandomStream revokeClassFactories'. "Add the class names, loose method names, global names, resource names" package classNames add: #COMRandomStream; add: #IRandomStream; yourself. package methodNames yourself. package globalNames add: #RANDOMLib; yourself. package resourceNames yourself. "Binary Global Names" package binaryGlobalNames: (Set new add: #RANDOMLib; yourself). "Resource Names" package allResourceNames: (Set new yourself). "Add the prerequisite names" package setPrerequisites: (IdentitySet new add: 'ActiveX Automation'; add: 'Dolphin'; add: 'OLE COM'; yourself). package! "Class Definitions"! AXDualImp subclass: #COMRandomStream instanceVariableNames: 'lowerBound upperBound stream' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! IDispatch subclass: #IRandomStream instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! "Loose Methods"! "End of package definition"! COMRandomStream comment: 'COMRandomStream is part of the COM Random Stream sample. See the "RandomStream sample" package for further details.'! COMRandomStream guid: (GUID fromString: '{F560CD26-00A1-4A4F-9833-03AE165A2E11}')! !COMRandomStream categoriesForClass!COM-Implementations! ! !COMRandomStream methodsFor! get_LowerBound: plLowerBound "Private - Implement IRandomStream::LowerBound(). Answer the receiver's lower bound via the [out] parameter, plLowerBound." plLowerBound value: lowerBound. ^S_OK! get_Seed: plSeed "Private - Implement IRandomStream::Seed(). Answer the receiver's seed through the [out] parameter, seed" plSeed value: stream seed. ^S_OK! get_UpperBound: plUpperBound "Private - Implement IRandomStream::get_UpperBound(). Answer the receiver's upper bound via the [out] parameter, plUpperBound." plUpperBound value: upperBound. ^S_OK! initialize "Private - Initialize the reciever's instance variables (suitably for the lottery)." lowerBound := 1. upperBound := 49. stream := Random new. ! interfaceClass "Answer the dual interface supported by the receiver." ^IRandomStream! Next: next "Private - Implement IRandomStream::Next(). Answer the next pseudo-random number in the sequence defined by the receiver via the [out] parameter, next." next value: (stream next * (upperBound-lowerBound) + lowerBound) asInteger. ^S_OK! put_LowerBound: value "Private - Implement IRandomStream::put_LowerBound(). Set the receivers lowerBound from the [in] parameter, value." lowerBound := value. ^S_OK! put_Seed: newValue "Private - Implement IRandomStream::put_Seed(). Set the receiver's seed from the [in] parameter, dwValue." stream seed: newValue. ^S_OK! put_UpperBound: value "Private - Implement IRandomStream::put_UpperBound. Set the receiver's upperBound from the [in] parameter, value." upperBound := value. ^S_OK! ! !COMRandomStream categoriesFor: #get_LowerBound:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #get_Seed:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #get_UpperBound:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #initialize!initializing!private! ! !COMRandomStream categoriesFor: #interfaceClass!constants!public! ! !COMRandomStream categoriesFor: #Next:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #put_LowerBound:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #put_Seed:!COM Interfaces-IRandomStream!private! ! !COMRandomStream categoriesFor: #put_UpperBound:!COM Interfaces-IRandomStream!private! ! !COMRandomStream class methodsFor! clsid "Answer the receiver's CLSID." ^CLSID fromString: '{A1D42F35-E6C0-11D2-833B-0020AFAB8EFE}'! new "Answer a new initialized instance of the receiver." ^super new initialize! progID "Answer the receiver's programmatic ID, typically used from scripting or macro languagues to create instances of the receiver, e.g. 'Dolphin.Object.1'" ^'Dolphin.RandomStream.1'! ! !COMRandomStream class categoriesFor: #clsid!constants!private! ! !COMRandomStream class categoriesFor: #new!instance creation!private! ! !COMRandomStream class categoriesFor: #progID!constants!public! ! IRandomStream comment: '<IRandomStream> is a wrapper class for the COM interface ''RANDOMLib.IRandomStream'' generated from type information in the ''Random 1.0 Type Library'' library. It contains methods to invoke the member functions exposed by that interface. The type library contains the following helpstring for this interface: "IRandomStream Interface" WARNING: This comment was automatically generated from the interface''s type information and any changes made here may be overwritten the next time this wrapper class is so generated.'! IRandomStream guid: (IID fromString: '{0E2CEA3B-E6C4-11D2-833B-0020AFAB8EFE}')! !IRandomStream categoriesForClass!COM-Interfaces! ! !IRandomStream methodsFor! get_LowerBound: pVal "Private - Get the value of the 'LowerBound' property of the receiver. Helpstring: 'Lower bound of range of random numbers generated' HRESULT __stdcall LowerBound( [out, retval] long* pVal); " <virtual stdcall: hresult 11 sdword*> ^self invalidCall ! get_Seed: pVal "Private - Get the value of the 'Seed' property of the receiver. Helpstring: 'Current random seed' HRESULT __stdcall Seed( [out, retval] long* pVal); " <virtual stdcall: hresult 9 sdword*> ^self invalidCall ! get_UpperBound: pVal "Private - Get the value of the 'UpperBound' property of the receiver. Helpstring: 'Upper bound of range of random numbers generated' HRESULT __stdcall UpperBound( [out, retval] long* pVal); " <virtual stdcall: hresult 13 sdword*> ^self invalidCall ! isExtensible "Answer whether the receiver may add methods at run-time." ^false! isVBCollection "Answer whether the receiver is a VB style collection." ^false! lowerBound "Answer the <sdword> value of the 'LowerBound' property of the receiver. Helpstring: 'Lower bound of range of random numbers generated'" | pVal | pVal := SDWORD new. self get_LowerBound: pVal. ^pVal value ! lowerBound: pVal "Set the 'LowerBound' property of the receiver to the <sdword> value of the argument. Helpstring: 'Lower bound of range of random numbers generated'" self put_LowerBound: pVal ! next "Answer the <SDWORD> result of invoking the COM Object's Next() method. Helpstring: 'Answer the next random number in the stream'" | plNext | plNext := SDWORD new. self Next: plNext. ^plNext value ! Next: plNext "Private - Invoke the Next() method of the COM object wrapped by the receiver. Helpstring: 'Answer the next random number in the stream' HRESULT __stdcall Next( [out, retval] long* plNext); " <virtual stdcall: hresult 8 sdword*> ^self invalidCall ! put_LowerBound: pVal "Private - Set the value of the 'LowerBound' property of the object wrapped by the receiver to the <sdword> argument, pVal. Helpstring: 'Lower bound of range of random numbers generated' HRESULT __stdcall LowerBound( [in] long pVal); " <virtual stdcall: hresult 12 sdword> ^self invalidCall ! put_Seed: pVal "Private - Set the value of the 'Seed' property of the object wrapped by the receiver to the <sdword> argument, pVal. Helpstring: 'Current random seed' HRESULT __stdcall Seed( [in] long pVal); " <virtual stdcall: hresult 10 sdword> ^self invalidCall ! put_UpperBound: pVal "Private - Set the value of the 'UpperBound' property of the object wrapped by the receiver to the <sdword> argument, pVal. Helpstring: 'Upper bound of range of random numbers generated' HRESULT __stdcall UpperBound( [in] long pVal); " <virtual stdcall: hresult 14 sdword> ^self invalidCall ! seed "Answer the <sdword> value of the 'Seed' property of the receiver. Helpstring: 'Current random seed'" | pVal | pVal := SDWORD new. self get_Seed: pVal. ^pVal value ! seed: pVal "Set the 'Seed' property of the receiver to the <sdword> value of the argument. Helpstring: 'Current random seed'" self put_Seed: pVal ! upperBound "Answer the <sdword> value of the 'UpperBound' property of the receiver. Helpstring: 'Upper bound of range of random numbers generated'" | pVal | pVal := SDWORD new. self get_UpperBound: pVal. ^pVal value ! upperBound: pVal "Set the 'UpperBound' property of the receiver to the <sdword> value of the argument. Helpstring: 'Upper bound of range of random numbers generated'" self put_UpperBound: pVal ! ! !IRandomStream categoriesFor: #get_LowerBound:!*-primitives!COM Interfaces-RANDOMLib.IRandomStream!private! ! !IRandomStream categoriesFor: #get_Seed:!*-primitives!COM Interfaces-RANDOMLib.IRandomStream!private! ! !IRandomStream categoriesFor: #get_UpperBound:!*-primitives!COM Interfaces-RANDOMLib.IRandomStream!private! ! !IRandomStream categoriesFor: #isExtensible!**auto generated**!public!testing! ! !IRandomStream categoriesFor: #isVBCollection!**auto generated**!public!testing! ! !IRandomStream categoriesFor: #lowerBound!**auto generated**-properties!public! ! !IRandomStream categoriesFor: #lowerBound:!**auto generated**-properties!public! ! !IRandomStream categoriesFor: #next!**auto generated**-methods!public! ! !IRandomStream categoriesFor: #Next:!*-primitives!COM Interfaces-RANDOMLib.IRandomStream!private! ! !IRandomStream categoriesFor: #put_LowerBound:!**auto generated**-properties!*-primitives!private! ! !IRandomStream categoriesFor: #put_Seed:!**auto generated**-properties!*-primitives!private! ! !IRandomStream categoriesFor: #put_UpperBound:!**auto generated**-properties!*-primitives!private! ! !IRandomStream categoriesFor: #seed!**auto generated**-properties!public! ! !IRandomStream categoriesFor: #seed:!**auto generated**-properties!public! ! !IRandomStream categoriesFor: #upperBound!**auto generated**-properties!public! ! !IRandomStream categoriesFor: #upperBound:!**auto generated**-properties!public! ! !IRandomStream class methodsFor! clsid "Private - Answer the CLSID of the coclass (RandomStream) for which the receiver is the default interface. " ^CLSID fromString: '{A1D42F35-E6C0-11D2-833B-0020AFAB8EFE}' ! defineFunctions "Declare the virtual function table for the COM interface 'RANDOMLib.IRandomStream' IRandomStream defineTemplate " self defineFunction: #Next: argumentTypes: 'sdword*'; defineFunction: #get_Seed: argumentTypes: 'sdword*'; defineFunction: #put_Seed: argumentTypes: 'sdword'; defineFunction: #get_LowerBound: argumentTypes: 'sdword*'; defineFunction: #put_LowerBound: argumentTypes: 'sdword'; defineFunction: #get_UpperBound: argumentTypes: 'sdword*'; defineFunction: #put_UpperBound: argumentTypes: 'sdword' ! initializeTypeLib "Private - Establish a connection to the receiver's type library. IRandomStream initializeTypeLib " typeLib := RANDOMLib! ! !IRandomStream class categoriesFor: #clsid!**auto generated**!constants!private! ! !IRandomStream class categoriesFor: #defineFunctions!**auto generated**!initializing!public! ! !IRandomStream class categoriesFor: #initializeTypeLib!**auto generated**!initializing!private! ! "Binary Globals"! RANDOMLib := Object fromBinaryStoreBytes: (ByteArray fromHexString: '2153544220302046091500010000004158547970654C696272617279416E616C797A6572000 000000602080049547970654C696200000000000000000000000006010800544C49424154545 20000000036000900427974654172726179200000002FEA2C0EC4E6D211833B0020AFAB8EFE0 000000001000000010000000800000036000600537472696E670900000052414E444F4D4C696 201000000D20000001300000052616E646F6D53747265616D2053616D706C650E010E0053544 253796D626F6C50726F787900000000D20000000900000052414E444F4D4C6962D2000000000 000000E021A005354424964656E7469747944696374696F6E61727950726F7879000000004E0 20D0001000000535442436C61737350726F787900000000D200000007000000446F6C7068696 ED20000000B0000004C6F6F6B75705461626C65260005004172726179020000000A010000000 00000D20000000400000047554944C001000000000000')! "Resources"! |
Blair, that COMRandomStream example was _great_!! It is just what
I was looking for (and I was starting to give up hope). Thanks so very much!! I took your COMRandomStream example and applied it to my ListKeeper example (using dual interface), and it worked!! I can even call the AddItem function in my Dolphin ListKeeper server from a VBA client (in Excel - I don't have Visual Basic) and from an IE client. I've added a view to my ListKeeper example (it's just a window with a listbox containing all the strings in the list) and deployed it as an exe. Now when I call AddItem from my COM client, it will start up the exe and display the listbox containing the string I passed in AddItem - perfect!! However, if I then close the exe (press the close box on the ListKeeper window), the window closes, but if I look at the task list I still see the ListKeeper.exe running. How can I get ListKeeper.exe to terminate when its window is closed? BTW, I don't experience this problem if I launch ListKeeper.exe by double-clicking the file. I only get this problem when ListKeeper.exe is lauched by an AddItem call from a COM client. FYI, here is my VBA code that I've hooked up to a button in Excel: Private Sub CommandButton3_Click() Dim lkr As Object Set lkr = CreateObject("ListKeeper") lkr.AddItem ("abc") Set lkr = Nothing End Sub Also, what is the best way to package/deploy an out-of-process (exe) COM server in Dolphin? For example, where should one put the >>register and >>registerClassFactory calls? If I were to pass this exe to someone else to install, those >>register.. calls would have to be executed at some point. And, I presume the type library would have to be registered (using regtlib) for COM to work. Would that registration be done by the installation software? Thanks again. |
drtau wrote:
> > However, if I then close the exe (press the close box on the ListKeeper > window), the window closes, but if I look at the task list I still see > the ListKeeper.exe running. How can I get ListKeeper.exe to terminate > when its window is closed? BTW, I don't experience this problem > if I launch ListKeeper.exe by double-clicking the file. I only get this problem > when ListKeeper.exe is lauched by an AddItem call from a COM client. I think I figured it out. I just need to re-implement the "keep alive" policy as follows: ListKeeperSessionManager>>keepAlive "The inputState has determined that there are no live windows. By default we therefore shutdown if not acting as an embedded server. This can be overridden by derived classes that want to employ a different policy for deciding when to shut down." self quit Tried it out and it works like a charm :-) Still wondering about "best practices" concerning placement of >>register and >>registerClassFactory calls, and registration of the type library (is that needed? I thought I read somewhere that a type library was not needed for automation) during installation of the application. Any thoughts? |
All,
First of all I want to thank everybody in this discussion. Reading the articles helped me finally in writing my (working) COM server accessible from PowerPoint. Unfortunately I have a COM server that opens no windows when opened as a COM server. Therefore I can not test for open windows in the SessionManager. Any hints here? Thanks, Udo "drtau" <[hidden email]> schrieb im Newsbeitrag news:[hidden email]... > drtau wrote: > > > > > However, if I then close the exe (press the close box on the ListKeeper > > window), the window closes, but if I look at the task list I still see > > the ListKeeper.exe running. How can I get ListKeeper.exe to terminate > > when its window is closed? BTW, I don't experience this problem > > if I launch ListKeeper.exe by double-clicking the file. I only get this problem > > when ListKeeper.exe is lauched by an AddItem call from a COM client. > > I think I figured it out. I just need to re-implement the "keep alive" policy > as follows: > > ListKeeperSessionManager>>keepAlive > "The inputState has determined that there are no live windows. > By default we therefore shutdown if not acting as an embedded server. > > This can be overridden by derived classes that want to employ > a different policy for deciding when to shut down." > > self quit > > Tried it out and it works like a charm :-) > > Still wondering about "best practices" concerning placement of > >>register and >>registerClassFactory calls, and registration > of the type library (is that needed? I thought I read somewhere > that a type library was not needed for automation) during > installation of the application. Any thoughts? > > > |
Udo Schneider wrote:
> All, > > First of all I want to thank everybody in this discussion. Reading the > articles > helped me finally in writing my (working) COM server accessible from > PowerPoint. > > Unfortunately I have a COM server that opens no windows when opened as > a COM server. Therefore I can not test for open windows in the > SessionManager. > Any hints here? The default implementation of SessionManager>>keepAlive will not exit if there are active COM servers, so I would suspect that your server will not exit until the last COM client has released its interface - it that what you see (and want)? > > > Thanks, > > Udo > |
Free forum by Nabble | Edit this page |