Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

connect to running python COM server

1,674 views
Skip to first unread message

DMA

unread,
Aug 28, 1998, 3:00:00 AM8/28/98
to
Hi,

Here's a question from a COM newbie. I'm trying to understand
as much of the various pieces of COM documentation as I can but I guess
I still need to buy a good book on the subject.

I studied the connectable server/client COM example and was able to
register and create a simple connectable server. In one program I can
create multiple clients that connect to the same server instance. How
can I create a client that connects to an already running server
without having a reference to that server instance? Much like
connecting to an already running Word app.

I haven't got a clue where I have to look (and even if it's either
very easy or very hard):

- when registering the server? (using a different policy? or
other args?)
- in the server code?
- when doing win32com.dynamic.Dispatch?
- all of the above?

-- Marc


P.S. I changed my anti-spam policy so my return-address is valid
now. I sure don't want to wind up in people's kill-files because
of something stupid like that.

--
Marc van Grootel
bwa...@urc.tue.nl

Robin Becker

unread,
Aug 28, 1998, 3:00:00 AM8/28/98
to
In article <6s7ht7$3ca$1...@arachne.labyrinth.net.au>, Mark Hammond
<MHam...@skippinet.com.au> writes

>>I studied the connectable server/client COM example and was able to
>>register and create a simple connectable server. In one program I can
>>create multiple clients that connect to the same server instance. How
>>can I create a client that connects to an already running server
>>without having a reference to that server instance? Much like
>>connecting to an already running Word app.
>
>The connect samples are demos for how to use COM "connection points" -
>basically a standardized way of implementing callbacks. These are typically
>use for COM control events, such as the "OnClick" stuff handlers in VB.

>
>>I haven't got a clue where I have to look (and even if it's either
>>very easy or very hard):
>
>Very hard, Im afraid. Sort of. The best we can do at the moment is note
>that it is possible to force a COM object you implement to be hosted in a
>.EXE. Then, regardless of how many clients connect, only 1 .exe will exist
>with your objects. They will all be seperate objects, but currently they
>will share the same Python namespace.
>
>I should comment that this behaviour _may_ break in the future.
>
>Here is some sample code that should work with any recent Pythoncom:
>
># In 2 Python processes, type:
>>>> from win32com.client import Dispatch
>>>> import pythoncom
>>>> p=Dispatch("Python.Interpreter", clsctx=pythoncom.CLSCTX_LOCAL_SERVER)
>>>> p.Exec("import sys")
>
># You will notice a "pythonw.exe" running if you look at your task manager.
>
># In one process type:
>>>> p.Exec("sys.foo='bar'")
>
># In the other type:
>>>> p.Eval("sys.foo")
>'bar'
>
>As you can see, they share the same namespace. You should note:
>* I needed to use a module's namespace to demonstrate this. Each
>"Python.Interpreter" object has its own local and global variables - but
>abusing a module, for example, can get the behaviour you want. Your server
>can do whatever it wants!!
>* Python.interpreter is implemented in "win32com\servers\interp.py
>* The clsctx param is needed to force the .exe behaviour. This is only
>because by default Python registers an object to be used "inproc" or
>"localserver". It is possible for you to register your COM object as
>"localserver only", so that clients dont get a choice - a .EXE is always
>started up.
>
>
>I hope that makes some sense...

>
>>P.S. I changed my anti-spam policy so my return-address is valid
>>now. I sure don't want to wind up in people's kill-files because
>>of something stupid like that.
>
>Good decision :-)
>
>Mark.
>
>
>
I tried this and get
Python 1.5.1 (#0, Apr 13 1998, 20:22:04) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from win32com.client import Dispatch
>>> import pythoncom
>>> p=Dispatch("Python.Interpreter", clsctx=pythoncom.CLSCTX_LOCAL_SERVE
R)
Traceback (innermost last):
File "<stdin>", line 1, in ?
File "C:\Python\win32com\client\__init__.py", line 18, in Dispatch
dispatch = dynamic._GetGoodDispatch(dispatch,clsctx)
File "C:\Python\win32com\client\dynamic.py", line 55, in
_GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx,
pythoncom.II
D_IDispatch)
pywintypes.com_error: (-2147467259, 'Unspecified error', None, None)
>>>

this with Win95 Rev B; any ideas?

--
Robin Becker

Robin Becker

unread,
Aug 28, 1998, 3:00:00 AM8/28/98
to
In article <KUQjSEAR...@jessikat.demon.co.uk>, Robin Becker
<ro...@jessikat.demon.co.uk> writes
...

>I tried this and get
>Python 1.5.1 (#0, Apr 13 1998, 20:22:04) [MSC 32 bit (Intel)] on win32
>Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>>> from win32com.client import Dispatch
>>>> import pythoncom
>>>> p=Dispatch("Python.Interpreter", clsctx=pythoncom.CLSCTX_LOCAL_SERVE
>R)
>Traceback (innermost last):
> File "<stdin>", line 1, in ?
> File "C:\Python\win32com\client\__init__.py", line 18, in Dispatch
> dispatch = dynamic._GetGoodDispatch(dispatch,clsctx)
> File "C:\Python\win32com\client\dynamic.py", line 55, in
>_GetGoodDispatch
> IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx,
>pythoncom.II
>D_IDispatch)
>pywintypes.com_error: (-2147467259, 'Unspecified error', None, None)
>>>>
>
>this with Win95 Rev B; any ideas?
>
like a moron I deleted the .pyc automatically assuming it could be
recompiled. I uninstalled, reinstalled with 108 & now I get
a C++ runtime error from pythonw.exe (abnormal program termination)
after the p=Dispatch and the client output is

Python 1.5.1 (#0, Apr 13 1998, 20:22:04) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from win32com.client import Dispatch
>>> import pythoncom
>>> p=Dispatch("Python.Interpreter", clsctx=pythoncom.CLSCTX_LOCAL_SERVE
R)
Traceback (innermost last):
File "<stdin>", line 1, in ?
File "C:\Python\win32com\client\__init__.py", line 18, in Dispatch
dispatch = dynamic._GetGoodDispatch(dispatch,clsctx)
File "C:\Python\win32com\client\dynamic.py", line 55, in
_GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx,
pythoncom.II
D_IDispatch)
pywintypes.com_error: (-2146959355, 'Server execution failed', None,
None)
>>>
the spamserver still works though :)
--
Robin Becker

Mark Hammond

unread,
Aug 29, 1998, 3:00:00 AM8/29/98
to
>I studied the connectable server/client COM example and was able to
>register and create a simple connectable server. In one program I can
>create multiple clients that connect to the same server instance. How
>can I create a client that connects to an already running server
>without having a reference to that server instance? Much like
>connecting to an already running Word app.

The connect samples are demos for how to use COM "connection points" -
basically a standardized way of implementing callbacks. These are typically
use for COM control events, such as the "OnClick" stuff handlers in VB.

>I haven't got a clue where I have to look (and even if it's either
>very easy or very hard):

Very hard, Im afraid. Sort of. The best we can do at the moment is note
that it is possible to force a COM object you implement to be hosted in a
.EXE. Then, regardless of how many clients connect, only 1 .exe will exist
with your objects. They will all be seperate objects, but currently they
will share the same Python namespace.

I should comment that this behaviour _may_ break in the future.

Here is some sample code that should work with any recent Pythoncom:

# In 2 Python processes, type:

>>> from win32com.client import Dispatch
>>> import pythoncom

Christian Tismer

unread,
Aug 29, 1998, 3:00:00 AM8/29/98
to
Mark Hammond wrote:

[DMA about connecting to a running server]

> Very hard, Im afraid. Sort of. The best we can do at the moment is note
> that it is possible to force a COM object you implement to be hosted in a
> .EXE. Then, regardless of how many clients connect, only 1 .exe will exist
> with your objects. They will all be seperate objects, but currently they
> will share the same Python namespace.
>
> I should comment that this behaviour _may_ break in the future.

Mark, please do not break this feature!
I have a running ASP application which makes much use of it.

On MS IIS with SQL Server 6.5, I'm running a set of Python ASP pages
which contain some page specific Python code. This is parsed and
evaluated again every time the pages gets a hit.
But the great savings is this: All the "meat" code is stored in some
modules to be imported. This works very efficiently since I can
import as many modules as I want. They are all kept in the single
Pythonw process which holds them.
This is also perfect to hold global objects like the current
database DSN, some cached tables and other static information
in the imported module's namespace.
Every application specific data is delivered by means of class
instances to my ASP pages.

If it is considered to change this in the future, I would need
a way to enable this again.

There is a little drawback with it: When I'm changing imported
modules, I have to explicitly reload them. A special ASP page
does this for me, so it is no problem when you know it.

<topicshift complain-to="/dev/null">

Mark, BTW: I also found a way to use Python with ASP the same way
as VBScript does. I didn't find a hint anywhere but I assume you
knew this before? See my extract from an ASP page which will run
at the German "Bundestagswahl" elections in Berlin on Sept 27:

...

debug = 0

def render(s):
code = string.replace(s, " Response.", "\nResponse.")+"\n"
if debug: Response.Write("<pre>"+ code +"</pre>")
exec code

...

Now, my mixing of code and HTML looks like this:
--------------------

def res_mehrere() :
# Response.Redirect("Seite2.asp")
render("""
%> <!-- mehrere Treffer, Verfeinerung nötig -->
<p><font face="Arial, Helvetica, sans-serif" size="2">Die Angaben
reichen nicht
aus, um das Wahllokal eindeutig zu bestimmen.<br>
Bitte wählen Sie aus der folgenden Liste die gewünschte Anschrift.
</font></p>
<form method="POST" action="suche.asp" TARGET="<%= glob.frame %>" >
<% """) ; save_vars_hidden() ; render(""" %>
<font face="Arial, Helvetica, sans-serif" size="2">
<select name="select">
<% """)
for i in glob.res:
i = StaLa.query.info(i)
strasse = string.strip(i.strnamek + " " + info.hausnr + info.zusatz)
adr = "%s, %s Berlin" % (strasse, i.plz)
link = encurl(strasse)+"::"+i.plz
adr = encode(adr)
Response.Write('''<option value="%s">%s</option>\n''' % (link, adr))
render("""
%>
</select>
<input type="submit" name="absenden" value="absenden">
</font>
</form>
<p><font face="Arial, Helvetica, sans-serif" size="2"> </font><%
""")

--------------------

Including HTML pieces in triple quoted strings gives me a variant
from IIS which evaluates to a string which contains space seperated
entries like
"Response.WriteBlock(5)"
The contiguous HTML pieces are numbered like this internally.
WriteBlock is nowhere documented in the IIS docs, but it is exposed
by the C interface.
In the above example, I get a sequence like
"Response.WriteBlock(5) Response.Write(glob.frame)
Response.WriteBlock(6)"
and so on.
Replacing these spaces by newlines and then exec-ing the string in
global namespaces does exactly what we need.

This kind of ASP pages enables you to edit the HTML parts with
editors like Forntpage, the same way as with VBScript.

If someone wants a full-blown example, drop me a note.

</topicshift>

cheers - pirx

--
Christian Tismer :^) <mailto:tis...@appliedbiometrics.com>
Applied Biometrics GmbH : Have a break! Take a ride on Python's
Kaiserin-Augusta-Allee 101 : *Starship* http://starship.skyport.net
10553 Berlin : PGP key -> http://pgpkeys.mit.edu
we're tired of banana software - shipped green, ripens at home

Andrew Robinson

unread,
Aug 30, 1998, 3:00:00 AM8/30/98
to
Robin Becker <ro...@jessikat.demon.co.uk> wrote:
[big error message]

>>this with Win95 Rev B; any ideas?

Yeeehaaa! I just got my first local server to work (DLL version
1.5.0.112, Win98). But there are problems - maybe Mark can tell us if
it is a platform problem or a bug. The module is attached.

1. Use win32com.client.dynamic.Dispatch instead of
win32com.client.Dispatch.

2. You need to write some more python code to ensure the SpamServer is
a singleton. Look in the attached module. What I did was to create a
single global variable in my module called "TheSpamServer", and a new
COM class called "SpamServerFactory" whose only job is to initialize
it if not yet done, then hand it over to you.

3. I watched a process monitor. When I did
spam = Dispatch('Python.SpamServer',pythoncom.CLSCTX_LOCAL_SERVER),
I dd NOT see pythoncom15.exe. However, I went into the registry,
foudn the key for my CLSID, and renamed the 'InprocServer32' folder to
something else. After doing this, COM had no choice but to make a
local server, and pythoncom15.exe did start. Creating the object
takes visibly longer.

After this, I did this in a DOS box:
>>>import pythoncom
>>>from win32com.client.dynamic import Dispatch
>>>factory = Dispatch("Python.SpamServerFactory", pythoncom.CLSCTX_LOCAL_SERVER)
>>>spam1 = factory.GetComSpamServer() # returns a singleton
>>>spam1.AddCan('beer','bottle',6)
pythoncom15.exe was running in the background.

and the finale you've all been waiting for, in DOS box number two...
>>>import pythoncom
>>>from win32com.client.dynamic import Dispatch
>>>factory2 = Dispatch("Python.SpamServerFactory", pythoncom.CLSCTX_LOCAL_SERVER)
>>>spam2 = factory2.GetComSpamServer() # returns a singleton
>>>spam2.GetDescriptionList()
['3 cans of spam','6 bottles of beer']

After closing both DOS boxes, pythoncom15.exe exits properly.


So, we're in business. But I'd like to know whay the registry hacking
was necessary - any ideas, Mark? does the argument to Dispatch work
on your machine?

We are now a spitting distance (just a few registry entries, in fact)
away from rempte servers on other machines using DCOM!


Regards,


Andy


Andy Robinson
Robinson Analytics Ltd.


DMA

unread,
Aug 30, 1998, 3:00:00 AM8/30/98
to Andrew Robinson
Andrew Robinson wrote:

I think you have to give Dispatch a named argument (as in Mark's
example)

Dispatch("Python.SpamServer", clsctx=pythoncom.CLSCTX_LOCAL_SERVER

When you register a server via UseCommandLine with only the class as
an argument it will get registered with:

clsctx = pythoncom.CLSCTX_INPROC_SERVER |
pythoncom.CLSCTX_LOCAL_SERVER

By removing the 'InprocServer32' registry entry you gave COM no other
option
then to use a local server.

> We are now a spitting distance (just a few registry entries, in fact)
> away from rempte servers on other machines using DCOM!

Cool!

Marc

DMA

unread,
Aug 30, 1998, 3:00:00 AM8/30/98
to
DMA wrote:

>
> Andrew Robinson wrote:
>
> > We are now a spitting distance (just a few registry entries, in > > fact)
> > away from rempte servers on other machines using DCOM!
>
> Cool!
>

I'm back to trying to build an example of the connect.py demo
using this approach.

So I took a working example and changed it so it loads the EXE.

However now my example fails. Thinking about it I guess the COM
security stuff has to be addressed. My intuition tells me that
until now the default security worked well. However with things
like 'outbound' interfaces the default security doesn't work.

conn = win32com.client.connect.SimpleConnection()
conn.Connect(server,ConnectableClient(),IID_IConnectDemoEvents)

The first line works. The second line gives the following traceback:

File "c:\python\win32com\client\connect.py", line 29, in Connect
self.cookie = self.cp.Advise(comEventInstance)
pywintypes.com_error: (-2147220992, 'CONNECT_E_NOCONNECTION', None,
None)

Hmm could it be that access is denied for the client?

Mark Hammond

unread,
Aug 31, 1998, 3:00:00 AM8/31/98
to
>However now my example fails. Thinking about it I guess the COM
>security stuff has to be addressed. My intuition tells me that
>until now the default security worked well. However with things
>like 'outbound' interfaces the default security doesn't work.

Quite likely, as I have never addressed COM security at all (like
marshalling, I kept hoping it was a bad dream that would go away! Like
marshalling, I suspect it wont either :-)

> File "c:\python\win32com\client\connect.py", line 29, in Connect
> self.cookie = self.cp.Advise(comEventInstance)
> pywintypes.com_error: (-2147220992, 'CONNECT_E_NOCONNECTION', None,
>None)
>
>Hmm could it be that access is denied for the client?

See above :-)

Mark.


Andrew Robinson

unread,
Aug 31, 1998, 3:00:00 AM8/31/98
to
an...@robanal.demon.co.uk (Andrew Robinson) wrote:

>3. I watched a process monitor. When I did
>spam = Dispatch('Python.SpamServer',pythoncom.CLSCTX_LOCAL_SERVER),
>I dd NOT see pythoncom15.exe. However, I went into the registry,
>foudn the key for my CLSID, and renamed the 'InprocServer32' folder to
>something else. After doing this, COM had no choice but to make a
>local server, and pythoncom15.exe did start. Creating the object
>takes visibly longer.
>

Apology to the group. I should have said...

spam = Dispatch('Python.SpamServer',
clsctx=pythoncom.CLSCTX_LOCAL_SERVER)

...using a named argument. It all works nicely now. You should also
note that Mark's latest build on starship (win32all_108.exe) includes
a mechanism for starting up a local server process, and a 'factory' as
part of it. Now, anyone here done Remote Servers yet? Any takers?
(I'm out of time for the next week, but we are nearly there).

Neil Hodgson

unread,
Sep 1, 1998, 3:00:00 AM9/1/98
to
Andrew Robinson wrote:

> ... Now, anyone here done Remote Servers yet? Any takers?


> (I'm out of time for the next week, but we are nearly there).

Yes. I used Python to experiment with Microsoft Transaction Server
which works quite well with Python. You can even interact with the MTS
environment by creating a MTxAS.AppServer.1 context object. Here's an
example:

import pythoncom
from win32com.server import util, policy, exception
import winerror
import pywintypes

class pym:
_public_methods = ['Display']
# All registration stuff to support fully automatic register/unregister
_reg_verprogid_ = "Python.Display.1"
_reg_progid_ = "Python.Display"
_reg_desc_ = "Python Display"
_reg_clsid_ = "{30BD3494-2632-11cf-AD5B-524153480031}"
_reg_class_spec_ = "pym.pym"

def Display(self):
objCurr = win32com.client.Dispatch("MTxAS.AppServer.1")
objCurrObjCont = objCurr.GetObjectContext()

# ... Spend some time doing some stuff, just to see
# the little ball spin in MTS Explorer ...

objCurrObjCont.SetComplete()
objCurrObjCont = None
objCurr = None

if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(pym)

Thomas Rodgers

unread,
Sep 1, 1998, 3:00:00 AM9/1/98
to
>>I studied the connectable server/client COM example and was able to
>>register and create a simple connectable server. In one program I can
>>create multiple clients that connect to the same server instance. How
>>can I create a client that connects to an already running server
>>without having a reference to that server instance? Much like
>>connecting to an already running Word app.
>
>The connect samples are demos for how to use COM "connection points" -
>basically a standardized way of implementing callbacks. These are
typically

>use for COM control events, such as the "OnClick" stuff handlers in VB.
>
>>I haven't got a clue where I have to look (and even if it's either
>>very easy or very hard):
>
>Very hard, Im afraid. Sort of. The best we can do at the moment is note
>that it is possible to force a COM object you implement to be hosted in a
>.EXE. Then, regardless of how many clients connect, only 1 .exe will exist
>with your objects. They will all be seperate objects, but currently they
>will share the same Python namespace.
>


This isn't really specific to PythonCom and I haven't dug into Mark's stuff
enough to know if it's wrapped (which means this may be of no help what so
ever), but there exists a beast called the Running Object Table (ROT). This
is a shared resource maintained by the OS. A COM server can advertise
itself by entering itself into the ROT. Clients can discover the server's
existence by examining the ROT. This service is typically used by things
that dispense monikers, but it's useful otherwise.

The relevent Win32 API's and Interfaces are :

WINOLEAPI GetRunningObjectTable(
DWORD reserved, //Reserved
LPRUNNINGOBJECTTABLE *pprot //Address of output variable that receives
the IRunningObjectTable interface pointer
);

IRunningObjectTable methods in VTable Order
IUnknown Methods Description
QueryInterface Returns pointers to supported interfaces.
AddRef Increments reference count.
Release Decrements reference count.

IRunningObjectTable Methods Description
Register Registers an object with the ROT.
Revoke Revokes an object’s registration with the ROT.
IsRunning Checks whether an object is running.
GetObject Returns a pointer to an object given its moniker.
NoteChangeTime Notifies the ROT that an object has changed.
GetTimeOfLastChange Returns the time an object was last changed.
EnumRunning Returns an enumerator for the ROT.

A server object registers itself into the ROT by first calling
GetRunningObjectTable, then
call Register() on the table. Register is defined as follows :

HRESULT Register(
DWORD grfFlags, //Specifies a weak or a strong reference
IUnknown *punkObject, //Pointer to the object being registered
IMoniker *pmkObjectName, //Pointer to the moniker of the object being
registered
DWORD *pdwRegister //Pointer to the value identifying the registration
);

Parameters
grfFlags
[in] Specifies whether the ROT’s reference to punkObject is weak or
strong. This
value must be either zero, indicating a weak registration, or
ROTFLAGS_REGISTRATIONKEEPSALIVE, indicating a strong registration.

punkObject
[in] Pointer to the object that is being registered as running.

pmkObjectName
[in] Pointer to the moniker that identifies punkObject.

pdwRegister
[out] Pointer to a 32-bit value that can be used to identify this
ROT entry in subsequent
calls to IRunningObjectTable::Revoke or
IRunningObjectTable::NoteChangeTime. The
caller cannot specify NULL for this parameter. If an error occurs,
*pdwRegister is set to
zero.

A "weakly" registered object will go away when the last client reference
disappears. A "strongly" registered object will persist in the ROT until
otherwise revoked.

You would need to pass the IDispatch pointer to the server object upon
registration along with a moniker (I don't know if the COM stuff for Python
wraps any of this either) to identify the object. Using the Moniker
interfaces and relevant Win32 API's you could key your server using a file
moniker naming the server, and an item moniker, naming the server object.
To construct a file moniker the win32api is :

WINOLEAPI CreateFileMoniker(
LPCOLESTR lpszPathName, //Pointer to path to be used
LPMONIKER FAR *ppmk //Address of output variable that receives the
IMoniker interface pointer
);

Parameters
lpszPathName
[in] Pointer to a zero-terminated wide character string (two bytes per
character)
containing the path on which this moniker is based.

ppmk
[out] Address of IMoniker* pointer variable that receives the interface
pointer to the new
file moniker. When successful, the function has called IUnknown::AddRef
on the file
moniker and the caller is responsible for calling IUnknown::Release.
When an error
occurs, the value of the interface pointer is NULL.

To construct a Item Moniker the win32api is :
WINOLEAPI CreateItemMoniker(
LPCOLESTR lpszDelim, //Pointer to delimiter string
LPCOLESTR lpszItem, //Pointer to item name
LPMONIKER FAR *ppmk //Address of output variable that receives the
IMoniker interface pointer
);

Parameters
lpszDelim
[in] Pointer to a wide character string (two bytes per character)
zero-terminated
string containing the delimiter (typically “!”) used to separate
this item’s display name
from the display name of its containing object.

lpszItem
[in] Pointer to a zero-terminated string indicating the containing
object’s name for the
object being identified. This name can later be used to retrieve a
pointer to the object
in a call to IOleItemContainer::GetObject.

ppmk
[out] Address of IMoniker* pointer variable that receives the
interface pointer to the
item moniker. When successful, the function has called
IUnknown::AddRef on the item
moniker and the caller is responsible for calling IUnknown::Release.
If an error occurs,
the supplied interface pointer has a NULL value.

Using the returned IMoniker interface from the File Moniker, you can call :

IMoniker::ComposeWith

It's args are :
HRESULT ComposeWith(
IMoniker *pmkRight, //Pointer to moniker to be composed onto this one
BOOL fOnlyIfNotGeneric, //Indicates if generic composition permissible
IMoniker **ppmkComposite //Address of output variable that receives the
IMoniker interface pointer
);

Parameters
pmkRight
[in] Pointer to the IMoniker interface on the moniker to compose
onto the end of this
moniker.

fOnlyIfNotGeneric
[in] If TRUE, the caller requires a non-generic composition, so the
operation should
proceed only if pmkRight is a moniker class that this moniker can
compose with in
some way other than forming a generic composite. If FALSE, the
method can create a
generic composite if necessary.

ppmkComposite
[out] Address of IMoniker* pointer variable that receives the
interface pointer to the
resulting composite moniker pointer. When successful, the
implementation must call
IUnknown::AddRef on the resulting moniker; it is the caller’s
responsibility to call
IUnknown::Release. If an error occurs or if the monikers compose to
nothing (e.g.,
composing an anti-moniker with an item moniker or a file moniker),
*ppmkComposite
should be set to NULL.

The returned ppmkComposite can then be used as a key by the server for
registration, and similarly, by the client to see if the server is running.
You could probably also just shove an item moniker into the ROT using the
stringized representation of the CLSID or ProgID for the server. The client
would then be able to determine if the server is running by calling the
ROT's :

IRunningObjectTable::IsRunning

HRESULT IsRunning(
IMoniker *pmkObjectName //Pointer to the moniker of the object whose
status is desired
);


Parameter
pmkObjectName
[in] Pointer to the IMoniker interface on the moniker to search for
in the Running Object
Table.

Return Values
S_OK
The object identified by pmkObjectName is running.

S_FALSE
There is no entry for pmkObjectName in the ROT, or that the object
it identifies is no
longer running (in which case, the entry is revoked).

To actually retrieve the object you call :

IRunningObjectTable::GetObject

HRESULT GetObject(
IMoniker *pmkObjectName, //Pointer to the moniker on the object
IUnknown **ppunkObject //Address of output variable that receives the
IUnknown interface pointer
);

This will work with local servers and inproc servers (though it's probably
of more interest in locating a local server). And should work with
PythonCom objects hosted by an EXE that registers them in the ROT.

FWIW

********************************************************************
Thomas Rodgers, Associate Director
Warburg Dillon Read, LLC (a subsidiary of UBS AG)

I do not speak for my employer, nor do they speak for me.
********************************************************************


Thomas Rodgers

unread,
Sep 1, 1998, 3:00:00 AM9/1/98
to
Thomas Rodgers wrote in message <6si838$r5p$1...@Nntp1.mcs.net>...
> snip

>This isn't really specific to PythonCom and I haven't dug into Mark's stuff
>enough to know if it's wrapped (which means this may be of no help what so


So I poked around a bit in the PythonCOM sources and the running object
table and moniker stuff is wrapped, though it looks like PythonCOM is
missing the CreateItemMoniker method (Q. Does Mark Hammond actually
sleep?).

Mark Hammond

unread,
Sep 2, 1998, 3:00:00 AM9/2/98
to
>This isn't really specific to PythonCom and I haven't dug into Mark's stuff
>enough to know if it's wrapped (which means this may be of no help what so
>ever), but there exists a beast called the Running Object Table (ROT).
This
>is a shared resource maintained by the OS. A COM server can advertise
>itself by entering itself into the ROT. Clients can discover the server's
>existence by examining the ROT. This service is typically used by things
>that dispense monikers, but it's useful otherwise.

This should all be fully wrapped and work correctly.

In a nutshell, there are 2 "traditional" ways to implement COM singletons:

1) Use the ROT, as described by Thomas.
2) Allow the Class Factory's "CreateInstance" method to actually return an
existing instance.

1) looks ugly to me - the C++ code I have seen makes life a bit harder for
the clients. So I didnt take this path for my "sample".
2) Cant be done by Python code at this stage - but would be quite a simple
change (basically, Python can ask pythoncom to _create_ class factories, but
can not _implement_ them. It should be able to!).

In the MSDN documentation, there is an article by Don Box which describes
these options in detail, including sample C++ code (and it is _huge_
compared to what the equivilent Python code would look like :-)

>A "weakly" registered object will go away when the last client reference
>disappears. A "strongly" registered object will persist in the ROT until
>otherwise revoked.

One of the painful thinks I noticed - the COM server will need to respond to
QI's for IExternalConnection etc - all supported by Python, but a pain
none-the-less. (Also a big difficult using Python's semantics - how can an
object say "there are no references on me, but I do need to stay alive" - my
solution has always been to keep a reference to "self" in a global :-(

>You would need to pass the IDispatch pointer to the server object upon
>registration along with a moniker (I don't know if the COM stuff for Python
>wraps any of this either) to identify the object. Using the Moniker
>interfaces and relevant Win32 API's you could key your server using a file
>moniker naming the server, and an item moniker, naming the server object.

It sure does - and this is exactly the mess I mention above. Not at all
intuitive for the client side programmers - IMO, they just want to treat it
as a COM object, not use funky moniker functions.

>This will work with local servers and inproc servers (though it's probably
>of more interest in locating a local server). And should work with
>PythonCom objects hosted by an EXE that registers them in the ROT.

Yup - all you mention should be 100% doable in Python today. Monikers are
100% supported. Even asynch monikers are basically supported via the
new(ish) win32com.internet module (meaning you can allow the OS itself to
asynchronously download files etc, which is very nice.)

Thanks for that info!

Mark.


tav...@connix.com

unread,
Sep 2, 1998, 3:00:00 AM9/2/98
to
In article <6sjci4$ehh$1...@arachne.labyrinth.net.au>,

"Mark Hammond" <MHam...@skippinet.com.au> wrote:
> >This isn't really specific to PythonCom and I haven't dug into Mark's stuff
> >enough to know if it's wrapped (which means this may be of no help what so
> >ever), but there exists a beast called the Running Object Table (ROT).
> This
> >is a shared resource maintained by the OS. A COM server can advertise
> >itself by entering itself into the ROT. Clients can discover the server's
> >existence by examining the ROT. This service is typically used by things
> >that dispense monikers, but it's useful otherwise.
>
> This should all be fully wrapped and work correctly.
>
> In a nutshell, there are 2 "traditional" ways to implement COM singletons:
>
> 1) Use the ROT, as described by Thomas.
> 2) Allow the Class Factory's "CreateInstance" method to actually return an
> existing instance.
>
> 1) looks ugly to me - the C++ code I have seen makes life a bit harder for
> the clients. So I didnt take this path for my "sample".
> 2) Cant be done by Python code at this stage - but would be quite a simple
> change (basically, Python can ask pythoncom to _create_ class factories, but
> can not _implement_ them. It should be able to!).
>
> In the MSDN documentation, there is an article by Don Box which describes
> these options in detail, including sample C++ code (and it is _huge_
> compared to what the equivilent Python code would look like :-)
>

[... SNIP ...]

For what its worth, there's an excellent article in the October 1998 MSJ
titled "Effective COM Programming: Seven Tips for Building Better COM-based
Applications."

Tip 2 is "Beware the COM Singleton". It brings up the point that if you're
using an in-proc server, and you've hacked CreateInstance so that you always
return the same object, if you're being called from an MTA you may get
in trouble with marshalling.

The "real" way to do a singleton is to use your class object, and expose
a custom interface off it. Can PythonCOM do that?

-Chris

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp Create Your Own Free Member Forum

Andrew Robinson

unread,
Sep 6, 1998, 3:00:00 AM9/6/98
to
tav...@connix.com wrote:

>>
>> In a nutshell, there are 2 "traditional" ways to implement COM singletons:
>>
>> 1) Use the ROT, as described by Thomas.
>> 2) Allow the Class Factory's "CreateInstance" method to actually return an
>> existing instance.

>


>Tip 2 is "Beware the COM Singleton". It brings up the point that if you're
>using an in-proc server, and you've hacked CreateInstance so that you always
>return the same object, if you're being called from an MTA you may get
>in trouble with marshalling.
>
>The "real" way to do a singleton is to use your class object, and expose
>a custom interface off it. Can PythonCOM do that?

Not knowing or wanting to know any gory COM details, I ended up with
this last weekend.

(1) In the module that defines the server, add a line like
TheSpamServer = None

(2) Created a new COM class called 'SpamServerFactory', with one
method called 'GetTheSpamServer'. This does
def GetTheSpamServer(self):
if TheSpamServer = None:
TheSpamServer = SpamServer()
return TheSpamServer
...in other words, instantiate our global if needed, and return it.

Clients who want a singleton just do (assuming Visual Basic):
Set factory = CreateObject("Python.SpamServerFactory")
Set server = factory.GetTheSpamServer

Client who don't want a singleton can do
Set server = CreateObject("Python.SpamServer")

This seems nice and simple and avoids having to fiddle with COM
internals Is there anything wrong with this method, or any pitfalls
down the road?

0 new messages