Implementing a COM Server

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

Implementing a COM Server

Scott
I am trying to create a simple COM server for the following method in
a subclass of AXDualImp named 'SampleApplication' where I want to
return an instance of 'SampleUnit' (also a subclass of AXDualImp).

The following method works (but is it correct?), but when the client
releases the Interface (the referenceCount = 0), the server instance
of SampleUnit's interface still has a referenceCount = 1 and therefore
is hanging around in the image (COMObjectStub registry) (even though
it should have (I believe) a referenceCount of 0).


Unit: unitFloat unitString: unitString Unit: unit
        "Private - Invoke the Unit() method of the COM object.
        Helpstring: 'Return aUnit'

                HRESULT __stdcall Unit(
                        [in] VARIANT unitFloat,
                        [in] VARIANT unitString,
                        [out, retval] VARIANT* Unit);"

        | newValue |
        newValue := SampleUnit unitValue: unitFloat asObject unitType:
unitString asObject.
        unit value: newValue interface asVariant.
        ^S_OK


I have created a zip file that has IDL and PAC file (Interface classes
generated, AXDualImp subclasses implemented, and a Test Class that
demonstrates the recreats the problem using Dolphin 5.01.


Here is the revelant portion of the IDL.

        [
                object,
                uuid(51895187-3D52-4474-8E33-62F4A344E300),
                dual,
                helpstring("COM Server ActiveX Interface"),
                pointer_default(unique),
                oleautomation,
                nonextensible
        ]
        interface _Application : IDispatch
        {
                [id(1), helpstring("Return aUnit")]
                        HRESULT Unit(
                                [in] VARIANT unitFloat,
                                [in] VARIANT unitString,
                                [out, retval] VARIANT* unit);
        };
        [
                object,
                uuid(765540BC-2729-4103-8A07-B75DEE31E220),
                dual,
                helpstring("Unit"),
                pointer_default(unique),
                oleautomation,
                nonextensible
        ]
        interface _Unit : IDispatch
        {
                [id(1), propget]
                        HRESULT unitValue(
                                [out, retval] VARIANT* unitFloat);
                [id(2), propget]
                        HRESULT unitType(
                                [out, retval] VARIANT* unitString);
                [id(4), helpstring("Add Units")]
                        HRESULT add(
                                [in] VARIANT addUnit,
                                [out, retval] VARIANT* unit);
        };



Thanks,
Scott


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Bill Schwab-2
Scott,

> Unit: unitFloat unitString: unitString Unit: unit
> "Private - Invoke the Unit() method of the COM object.
> Helpstring: 'Return aUnit'
>
> HRESULT __stdcall Unit(
> [in] VARIANT unitFloat,
> [in] VARIANT unitString,
> [out, retval] VARIANT* Unit);"
>
> | newValue |
> newValue := SampleUnit unitValue: unitFloat asObject unitType:
> unitString asObject.
> unit value: newValue interface asVariant.
> ^S_OK

**If** I'm reading this correctly, it looks like you are trying to make COM
return an object by value rather than allowing the interface pointer to act
as a proxy in one process for the object/server in another.  My advice would
be to leave the functionality in SampleUnit and expose just enough via IDL
to allow a client in another process to access the behavior via an interface
pointer.  Is that helpful, or am I missing your point?

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Scott
How would you define the IDL and the Smalltalk server method to return
the interface.  That sounds like what I am trying to do.

Any help would be great,
Scott


"Bill Schwab" <[hidden email]> wrote in message news:<ajjath$1b1e7f$[hidden email]>...

> Scott,
>
> > Unit: unitFloat unitString: unitString Unit: unit
> > "Private - Invoke the Unit() method of the COM object.
> > Helpstring: 'Return aUnit'
> >
> > HRESULT __stdcall Unit(
> > [in] VARIANT unitFloat,
> > [in] VARIANT unitString,
> > [out, retval] VARIANT* Unit);"
> >
> > | newValue |
> > newValue := SampleUnit unitValue: unitFloat asObject unitType:
> > unitString asObject.
> > unit value: newValue interface asVariant.
> > ^S_OK
>
> **If** I'm reading this correctly, it looks like you are trying to make COM
> return an object by value rather than allowing the interface pointer to act
> as a proxy in one process for the object/server in another.  My advice would
> be to leave the functionality in SampleUnit and expose just enough via IDL
> to allow a client in another process to access the behavior via an interface
> pointer.  Is that helpful, or am I missing your point?
>
> Have a good one,
>
> Bill


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Bill Schwab
Scott,

> > **If** I'm reading this correctly, it looks like you are trying to make
COM
> > return an object by value rather than allowing the interface pointer to
act
> > as a proxy in one process for the object/server in another.  My advice
would
> > be to leave the functionality in SampleUnit and expose just enough via
IDL
> > to allow a client in another process to access the behavior via an
interface
> > pointer.  Is that helpful, or am I missing your point?

> How would you define the IDL and the Smalltalk server method to return
> the interface.  That sounds like what I am trying to do.

My mission is now to talk you out of it :)  If you have a Smaltalk app on
the client side, then it can do the work for itself.  If you don't, then it
won't know how to make sense of the object you "return" to it.  Instead, you
should focus on defining enough behavior in your interface(s) to allow the
caller to request the services with the server object "remaining in place".

COM is a very nice technology (though beware of DCOM and its temporal
tantrums) for crossing boundaries between binary executables.  As such,
design for it is different than for traditional OO systems.  Generally (if
not always) you will find that objects with behavior do not get returned by
value; instead, one passes interface pointers to them and the objects
themselves don't move.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Scott
>
> My mission is now to talk you out of it :)  If you have a Smaltalk app on
> the client side, then it can do the work for itself.  If you don't, then it
> won't know how to make sense of the object you "return" to it.  Instead, you
> should focus on defining enough behavior in your interface(s) to allow the
> caller to request the services with the server object "remaining in place".

Most of core objects do "remain in place", just this one class of
object get a new instance for each method sent to it, (which can be
then used as an input to another method).  So I believe I need the
object to remain temporilly until released by the client.

> COM is a very nice technology (though beware of DCOM and its temporal
> tantrums) for crossing boundaries between binary executables.  As such,
> design for it is different than for traditional OO systems.  Generally (if
> not always) you will find that objects with behavior do not get returned by
> value; instead, one passes interface pointers to them and the objects
> themselves don't move.

So can you provide me with a sample of IDL returning an interface
pointer as well as the smalltalk com server method on how to pass that
interface pointer out?

Thanks,
Scott


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Scott
Looking at Microsoft Office typelib as an example of how to define IDL
that returns an Interface.  The following is an example of how I am
defining my IDL.  I believe that the IDL is correct and compiles.

interface _Application : IDispatch
{
        [id(1), helpstring("Return aUnit")]
                HRESULT Unit(
                        [in] VARIANT unitFloat,
                        [in] BSTR unitString,
                        [out, retval] _Unit** unit);
};

So I have SampleApplication (implements _Application) and SampleUnit
(implements _Unit) as subclasses of AXDualImp.

Following is my current implementation:

SampleApplication
Unit: unitFloat unitString: unitString unit: unit
        "Private - Invoke the unit() method of the COM object.
        Helpstring: 'Return aUnit'

                HRESULT __stdcall unit(
                        [in] VARIANT unitFloat,
                        [in] BSTR unitString,
                        [out, retval] _Unit** unit);"

        | newValue |
        newValue := SampleUnit unitValue: unitFloat asObject unitType:
unitString.
        unit value: newValue interface.
        ^S_OK

This method raises an error "_Unit does not understand #asInteger.
'unit' is an instance of LPVOID, but the sender method defined that
parm to be "_Unit newPointer".  How do I return the interface for an
instance of SampleUnit?

Please help,
Scott


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Bill Schwab-2
Scott,

> as an example of how to define IDL
> that returns an Interface.

Take a look at Rogerson's Inside COM, Chapter 10 in the "Structures in IDL
section" - it's page 259 in my copy, and not far after the discussion of
size_is.  In that general area, you'll find a description of iid_is, which
is a nicer way to return an interface.  Sadly, I don't remember whether it
allows you to be "Automation compatible", but using a pointer to your own
interface certainly will not be Automation compatible.  Making a long story
short, Automation compatible interfaces can be marshaled by simply
registering an associated type library; otherwise, you'll have to compile a
custom marshaling DLL.  The latter isn't too difficult, and Rogerson
describes how to get started.

Have a good one,

Bill

---
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

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

>...
> Following is my current implementation:
>
> SampleApplication
> Unit: unitFloat unitString: unitString unit: unit
> "Private - Invoke the unit() method of the COM object.
> Helpstring: 'Return aUnit'
>
> HRESULT __stdcall unit(
> [in] VARIANT unitFloat,
> [in] BSTR unitString,
> [out, retval] _Unit** unit);"
>
> | newValue |
> newValue := SampleUnit unitValue: unitFloat asObject unitType:
> unitString.
> unit value: newValue interface.
> ^S_OK
>
> This method raises an error "_Unit does not understand #asInteger.
> 'unit' is an instance of LPVOID, but the sender method defined that
> parm to be "_Unit newPointer".  How do I return the interface for an
> instance of SampleUnit?
>

See the example package 'EnumRECT', specifically the method
EnumRECT>>clone:. The general pattern is also evident in the base COM
support framework (see for example
COMObjectStub>>innerQueryInterface:ppvObject: and
COMClassFactory>>CreateInstance:riid:ppvObject:).

Passing out an interface pointer is relatively simple, but you must respect
COMs reference counting rules, which is basically that you return an
interface with a elevated ref. count, in anticipation of the client assuming
responsibility for that reference. This is why you will see the interface
being  #detach'd. Alternatively you can explicitly #addRef and set the
#yourAddress of the interface into the output parameter.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Scott
In reply to this post by Bill Schwab-2
I can find IDL examples of how to return an Interface, but I would
like to know what the correct way writing Smalltalk code to return the
Interface.  I would like to write an ActiveX component.  I will be
also interested in how to raise events as Bill has metioned in another
thread.

Maybe Blair or Andy can give us an example of how to accomplish these
items.

Thanks,
Scott


Reply | Threaded
Open this post in threaded view
|

Re: Implementing a COM Server

Scott
In reply to this post by Blair McGlashan
Thanks for the examples of passing interfaces, I will try them out.

Scott