[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ Python ➜ Python COM: how do you get at Mushclient?

Python COM: how do you get at Mushclient?

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Ked   Russia  (524 posts)  Bio
Date Tue 02 Sep 2003 07:23 AM (UTC)
Message
I am hoping that someone already knows the answer to this. I am trying to open a connection between Mushclient and an external program, kind of what Nick did in the Super Health Bar plugin only using Python. I've figured out already how to wrap and import the type library, but all it does for me right now is amuse me with it's coclass and a single interface.

I've tried Python's standard 'Quick Starts' for COM, but those are bent on starting the application before calling it from the outside, while I want to start the Python client from within a running Mushclient session (in other words - the other way around). Anyone knows how to do this and/or what to read on this?
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #1 on Tue 02 Sep 2003 08:12 AM (UTC)
Message
The 'createobject' approach should work, as COM doesn't really care what language the object is in.

Search this forum for that word.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #2 on Tue 02 Sep 2003 03:22 PM (UTC)
Message
Don't think CreateObject will work, as it simply calls a COM server (and it looks like Python scripts can't be registered with LocalServer32 support without building executables out of them first). What I need to do is to run the script, then from within that script find a running instance of Mushclient and connect to it as a server. What CreateObject would do is exactly the opposite: run the script as a COM server and create an object to allow Mushclient to call that server, with the server being able to call Mushclient back, but only when Mushclient feels like it.

Not even sure if what I am trying to accomplish is doable, as all the information I've managed to dig up on the subject so far includes starting up the server application from the client before issueing calls to it, which is what I can handle by now but not what I want to be able to handle :( But maybe I just didn't browse the references well enough yet...
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #3 on Fri 05 Sep 2003 07:54 AM (UTC)
Message
That is correct, you start MUSHclient first. Trying to do it first hasn't been a big success, because the new instance of MUSHclient tries to make a new world.

However I don't agree you can only call MUSHclient "when it feels like it".

For instance, with my recent "super info bar" I built a check into the bar that if you closed it, it called the current world and notified it the bar had been closed. This was effectively an unsolicited callback, so you could do them at any time, within reason.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Shadowfyr   USA  (1,787 posts)  Bio
Date Reply #4 on Fri 05 Sep 2003 06:47 PM (UTC)
Message
I think one major issue in making a Python script that runs independant work is what exactly is it that actually gets passed to a COM object? Is the world reference a Process ID, a Window reference, an object or what? If it is some sort of Process ID, then it is merely an integer value that references that specific process. Passing this to a normal COM program isn't a big issue, so passing it from a script to another script shouldn't be a major complication either.

However, if you mean that Python's COM support seems to prevent you from having it self register, so that you can talk to it both ways, then it should still be able too, I'm just not sure how. The key issue here though is that you would need to do this:

1. Run the external script.
2. Have that script self-register for use as a COM app (this can I beleive be done only while executing, I think...).
3. Use createobject to make a COM reference to the running script.
4. Pass it the Mushclient reference needed to let the script talk to Mushclient directly.

The only real issue is that is must be running 'first' to reference it.

I have found that for the most part Mushlient's use of scripts tends to exist in a limbo world all its own. This is especially true of anyone using one of the Windows Scripting based languages, but Python runs into the same issues, it just provides, in theory, a less complicated solution to the issue, requiring that you merely run a second script to get to the features that the MS based scripts can't do without client intervention. While Python's ability to get around it is a good thing, the assumption to 90% of the rest of the world is still that Python is going to be talking to a 'normal' program and not another copy of itself. Makes getting help on it complicated. lol

Oh BTW. I wouldn't mind if you shared how you wrapped and imported the type library for Mushclient. I haven't looked into it much myself, but I suspect it is slightly complex. I have frankly been waiting for someone else to solve it. ;) lol
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #5 on Sun 07 Sep 2003 09:25 AM (UTC)
Message
Ok, I know where I screwed up now... My prejudice against CreateObject seems to stem from the fact that last time I tried to use it for the same task, it froze up Mushclient. But I guess it wouldn't have done so had I not made the COM object enter a loop right on the CreateObject call. So, I take my criticism of CreateObject back. Though I still can't
get 'theworld.Note()' to work, there's some progress now that I looked through the Health Bar plugin once again, or so I hope.

As for wrapping the type lib... That's pretty simple actually:


from win32com.client import gencache
gencache.EnsureModule(<MC type lib clsid>, 0, 1, 0)


The latter line creates (if it doesn't exist yet) a <clsid>.py wrapper in Python/Lib/site-packages/win32com (or in there somewhere). The problem with it is that since it's named after the actual clsid, you can't import it directly and are forced to use EnsureModule() every time you want to use that type lib. So I simply renamed the <clsid>.py file to mushclient.py and moved it to Python/Lib, from where you can import it as any other module with:


import mushclient

Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #6 on Sun 07 Sep 2003 09:32 AM (UTC)
Message
And here's the Python script I am using for testing right now:

[code]
import Mushclient

class HelloWorld:

_public_methods_ = ['Hello']
_public_attrs_ = ['softspace', 'noCalls']
_readonly_attrs_ = ['noCalls']
_reg_clsid_ = "{FA41A4A1-DDA4-11D7-B993-444553540000}"
_reg_desc_ = "Python test COM server for Mushclient"
_reg_progid_ = "Python.MushTest"

def __init__(self):

self.softspace = 1
self.noCalls = 0

def Hello(self, callingworld):

self.noCalls = self.noCalls + 1

theworld = callingworld
# Mushclient.IMUSHclient.Note(theworld, "Hello")
theworld.Note("hello")
if __name__ =='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)
[/code]

ignore the self.softSpace and noCalls - those are the remnants of the sample script this one is built from, which I was too lazy to exclude.

Everything works except for the 'theworld.Note("hello")' line, which throws some sort of a type exception. Mucking with world.Note normally gives an error with "unbound function Note() requires an IMUSHclient instance, got <some other> instance instead". However in this case, the error is different, but I am thinking it still expects 2 arguments instead of 1, I just haven't figured out what exactly should be in the first argument (what's IMUSHclient instance exactly?).
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #7 on Sun 07 Sep 2003 03:11 PM (UTC)
Message
And here's what Mushclient's script passes to the COM object as 'world':


<PyIDispatch at 0x1e05284 with obj at 0x1584adc>


After a look at win32com's documentation, I am guessing that Python is different from VB in that the former has a more 'low-level' support for COM. Perhaps, the call needs to be handled manually somehow, so 'theworld.note("hi!")' isn't going to work directly. But I have an idea ;)
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #8 on Sun 07 Sep 2003 05:07 PM (UTC)

Amended on Wed 10 Sep 2003 02:15 PM (UTC) by Ked

Message
Wahoo! It's finally done! Below is the complete and working setup for this example. Note that in order for it to work you need to have the makepy-generated type lib wrapper, renamed to 'Mushclient.py', in your Python/Lib folder.

alias used:

<aliases>
  <alias
   name="hello"
   script="Main"
   match="test"
   enabled="y"
   sequence="100"
  >
  </alias>
</aliases>


vbs script:

sub Main (name, output, wildcs)
 dim pyClient
 set pyClient = CreateObject("Python.MushTest")

 dim strn
 pyClient.Hello(world)
end sub


Python COM object (clicking on it in Explorer will register it):

import Mushclient
import win32com.client

class HelloWorld:

    _public_methods_ = ['Hello']
    _public_attrs_ = ['softspace', 'noCalls']
    _readonly_attrs_ = ['noCalls']
    _reg_clsid_ = "{FA41A4A1-DDA4-11D7-B993-444553540000}"
    _reg_desc_ = "Python test COM server for Mushclient"
    _reg_progid_ = "Python.MushTest"
    
    def __init__(self):

        self.softspace = 1
        self.noCalls = 0

    def Hello(self, callingworld):

        self.noCalls = self.noCalls + 1

        theworld = callingworld
        world = win32com.client.Dispatch(theworld)
        world.Note("hello")

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


Apparently, Mushclient script passes an IDispatch (?) object as 'world' to the COMponent, but since Python can't get at methods and properties in the object's type library directly using the IDispatch reference you need to convert the IDispatch object to a CDispatch (?) one, using:


<CDispatch obj> = win32com.client.Dispatch(<IDispatch obj>)


This obviously wasn't my invention - I simply found it in ActiveState's mailing list after a search on PyIDispatch, so basically Shadowfyr did most of the job by asking the right question in his reply. Now I'll probably try to port the Super Health Bar plugin to Python - as this means more messing with stuff I have absolutely no knowledge about, I'll be playing on my favourite ground. Heh.
Top

Posted by Shadowfyr   USA  (1,787 posts)  Bio
Date Reply #9 on Mon 15 Sep 2003 07:50 PM (UTC)
Message
Replying a bit late here, but I actually missed some of the last postings. They got buried under other stuff I hadn't read.. :(

> The latter line creates (if it doesn't exist yet) a <clsid>.py wrapper in Python/Lib/site-packages/win32com (or
> in there somewhere). The problem with it is that since it's named after the actual clsid, you can't import it
> directly and are forced to use EnsureModule() every time you want to use that type lib. So I simply renamed the
> <clsid>.py file to ...

The irony here is that you 'should' be able to run makepy.py and it will give you a complete list of all available registered COM objects to select from and generates the same file. I say 'should' because this only seems to work if you run it from the DOS prompt, for some stupid reason PyShell and Idle refuse to recognize the win32com directory as part of the library path, even though both scripts use it in their own internals. In theory you can also do this:

python makepy.py mushclient.tlb -o mushclient

and have it produce a file called mushclient.py and mushclient.pyc automatically, but I couldn't get it to work that way. A nice 'fix' would be to add an extra dialog to makepy.py to ask what file name to export. Hopefully sometime down the road they will think of this. You can definitely tell that Python is originally a Linux application though. lol
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #10 on Tue 16 Sep 2003 01:43 PM (UTC)
Message
Personally, using the prompt in Windows is just too tiresome for me (especially taken into account the crazy organisation of my directories) so I just stick to Pythonwin's interactive window and scripts for most tasks. Besides, you just have to run makepy once and then a few clicks of a mouse make an importable module for you. Compared to trying to make a GUI com server in Python, that's a minor incovenience. Linux... why would anyone creating an open source anything choose Windows as the main platform? ;)
Top

Posted by Shadowfyr   USA  (1,787 posts)  Bio
Date Reply #11 on Tue 16 Sep 2003 05:33 PM (UTC)
Message
Hmm. I can use win32com.client.makepy, but it still won't run the dang thing.. However, Pyshell gives me this:

>>>import win32com
>>>win32com.client.makepy
<module 'win32com.client.makepy' from 'C:\PYTHON22\lib\site-packages\win32com\client\makepy.pyc'>

Nothing happens and I certainly don't get the dialog it is supposed to show. Same thing in Idle. This makes no sense. It means that makepy not only won't show a dialog, but it also won't accept command line arguements. This is an improvement over running it is DOS how? Imho the Python shell sucks and leaves a lot more to be desired than is at all reasonable. I don't see what I am doing wrong here and having to hunt down gencache because makepy refuses to work right is not at all helpful. I shouldn't have to.

So any idea what and why this goes wrong, because if not, then I have to seriously wonder what Pyshell is actually good for?
Top

Posted by Shadowfyr   USA  (1,787 posts)  Bio
Date Reply #12 on Tue 16 Sep 2003 05:53 PM (UTC)
Message
Arrggg...! Turns out that PythonWin lets you run it from the tools menu... Sigh.. This is like trying to program in the days of command line compilers, when they introduced the concept of IDE's, but half the bloody tools still had to be run from the prompt. Sigh.. I'll shut up now. :(
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


21,819 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]