COM newbie question

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

COM newbie question

drtau-3
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.


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Eric Winger-4
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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-3
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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-3
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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Eric Winger-4
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
>>>
>


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Eric Winger-4
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
>>
>


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Blair McGlashan
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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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.


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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' ?


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Bill Schwab-2
> 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]


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Blair McGlashan
"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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Blair McGlashan
"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


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-3
> > 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.


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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.)


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Blair McGlashan
"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"!


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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.


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-4
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?


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

Udo Schneider
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?
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: COM newbie question

drtau-3
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
>


12