2

From my other question here on SO, I asked how to retrieve the current playing song from Windows Media Player and Zune, I got an answer from a c++ dev who gave me an explanation of how I would do this for WMP.

However, I am no C++ dev, nor am I very experienced with the pywin32 library. And on-top of all that, the documentation on all this (especially concerning WMP) is horrible.

Therefor, I need your help understanding how I would do the following in Python.

Source

I have working code in C++ to print the name of media currently playing in WMP. It's a simple console application (78 lines of code).

Steps:

1) implements a basic COM object implementing IUnknown, IOleClientSite, IServiceProvider and IWMPRemoteMediaServices. This is straightforward (sort of, your mileage may vary) using the ATL template CComObjectRootEx. The only methods needing (simple) code are IServiceProvider::QueryService and IWMPRemoteMediaServices::GetServiceType. All other methods may return E_NOTIMPL

2) Instantiate the "WMPlayer.OCX" COM object (in my case, via CoCreateInstance)

3) Retrieve from the object an IOleObject interface pointer via QueryInterface

4) Instanciate an object from the class seen in 1) (I use the CComObject<>::CreateInstance template)

5) Use the SetClientSite method from the interface you got at 3), passing a pointer to your OleClientSite implementation.

6) During the SetClientSite call, WMP will callback you: fisrt asking for an IServiceProvider interface pointer, second calling the QueryService method, asking for an IWMPRemoteMediaServices interface pointer. Return your implementation of IWMPRemoteMediaServices and, third, you will be called again via GetServiceType. You must then return "Remote". You are now connected to the WMP running instance

7) Query the COM object for an IWMPMedia interface pointer

8) If 7) didn't gave NULL, read the the IWMPMedia::name property.

9) DONE

All the above was tested with VS2010 / Windows Seven, and with WMP running (if there is no Media Player process running, just do nothing).

I don't know if yoy can/want to implement COM interface and object in Python. If you are interested by my C++ code, let me know. You could use that code in a C++ DLL, and then call it from python.

I know a little bit about the win32api.

At the first step, I really don't know what to do, googling IOleClientSite results in the msdn documentation, it's an interface. However, that's where I get stuck already. I can't find anything (might just be my horrendous googling skills) on working with these things in Python.

The second step:

WMP = win32com.client.Dispatch("WMPlayer.OCX")

Alright, that's doable.

On to the third step. QueryInterface -

"regardless of the object you have, you can always call its QueryInterface() method to obtain a new interface, such as IStream."

source

However, not for me. As I understand his explanation, I think it means that every com object sort of "inherits" three methods from IUnknown, one of which is QueryInterface, however this does not seem the case since calling QueryInterface on my WMP object fails miserably. (Object has no attribute 'QueryInterface')

I could ramble on, but I believe you got the point, I have no idea how to work with this. Can anyone help me out with this one? Preferably with code examples, but resources/documentation is welcome too.

Community
  • 1
  • 1
Azeirah
  • 6,176
  • 6
  • 25
  • 44
  • In Windows ecosystem, various tools/language talks to components via COM. So you can basically search for a similar solution in vbscript, and translate it into python+pywin32 – Anthony Kong Oct 27 '13 at 05:36
  • There definitly is a way to call QueryInterface. I found, with google, references to "_oleobj_", and/or "CastTo". I also read that it is possible to *implement* COM Interfaces in Python (an interface is nothing more than a VTBL, with some simple code). Alas, I don't use Python, and can't help more. Ping back if you need me to post the C++ code. – manuell Oct 27 '13 at 18:21
  • 1
    There's a nice tutorial on [PythonCOM here](http://starship.python.net/~skippy/conferences/tools99/html/index.htm). It talks about how to implement your own COM objects based on IUnknown (and therefore implementations for IOleClientSite and IServiceProvider). You don't need to register your classes, since you don't need other apps to create instances of your objects. – Eric Brown Oct 28 '13 at 01:22

1 Answers1

2

Almost final answser but CAN'T finish. I seems that pythoncom can't be used to implement custom Interface without the help of a C++ module. Here is an answser from Mark Hammon (Mon, 13 Jan 2003): How to create COM Servers with IID_IDTExtensibility2 interface

Sorry - you are SOL. To support arbitary interfaces, you need C++ support, in the form of an extension module. There is a new "Univgw" that may help you out, but I dont know much about this

I am not able to find anything about that "Univgw" thing...

The comtypes python module is intended to resolve the problem, and I found links saying it does, but I can't make it works with my fresh new Python 3.3. It's Python 2.x code. comtypes seems outdated and unmaintained.

Step 1 OK for IOleClientSite and IServiceProvider, KO for IWMPRemoteMediaServices

Step 2, 3, 4 and 5 OK

Step 6, 7 and 8 can't be implemented without IWMPRemoteMediaServices :-(

disclaimer: complete newbie in Python, please don't yell

import pythoncom
import win32com.client as wc
from win32com.axcontrol import axcontrol
import win32com.server as ws
from win32com.server import util
from win32com.server.exception import COMException
import winerror
import pywintypes

# Windows Media Player Custom Interface IWMPRemoteMediaServices
IWMPRemoteMediaServices = pywintypes.IID("{CBB92747-741F-44FE-AB5B-F1A48F3B2A59}")

class OleClientSite:
    _public_methods_ = [ 'SaveObject', 'GetMoniker', 'GetContainer', 'ShowObject', 'OnShowWindow', 'RequestNewObjectLayout', 'QueryService' ]
    _com_interfaces_ = [ axcontrol.IID_IOleClientSite, pythoncom.IID_IServiceProvider ]

    def SaveObject(self):
        print("SaveObject")
        raise COMException(hresult=winerror.E_NOTIMPL)

    def GetMoniker(self, dwAssign, dwWhichMoniker):
        print("GetMoniker ")
        raise COMException(hresult=winerror.E_NOTIMPL)

    def GetContainer(self):
        print("GetContainer")
        raise COMException(hresult=winerror.E_NOTIMPL)

    def ShowObject(self):
        print("ShowObject")
        raise COMException(hresult=winerror.E_NOTIMPL)

    def OnShowWindow(self, fShow):
        print("ShowObject" + str(fShow))
        raise COMException(hresult=winerror.E_NOTIMPL)

    def RequestNewObjectLayout(self):
        print("RequestNewObjectLayout")
        raise COMException(hresult=winerror.E_NOTIMPL)

    def QueryService(self, guidService, riid):
        print("QueryService",guidService,riid)
        if riid == IWMPRemoteMediaServices:
            print("Known Requested IID, but can't implement!")
            raise COMException(hresult=winerror.E_NOINTERFACE)
        else:
            print("Requested IID is not IWMPRemoteMediaServices" )
            raise COMException(hresult=winerror.E_NOINTERFACE)


if __name__=='__main__':
    wmp = wc.Dispatch("WMPlayer.OCX")
    IOO = wmp._oleobj_.QueryInterface(axcontrol.IID_IOleObject)
    pyOCS = OleClientSite()
    comOCS = ws.util.wrap(pyOCS, axcontrol.IID_IOleClientSite)
    IOO.SetClientSite(comOCS)
manuell
  • 7,528
  • 5
  • 31
  • 58
  • Unfortunate that it can't work without C++, I couldn't find anything either (although I believe your win32api google-fu is better than mine). I'll figure out how to do the current accessed file by WMP method, thanks again for all the help, even though it failed in the end (I'm not going to use C++ or a dll, unless the file method fails too, somehow). It has taught me a lot about win32api. Which is like voodoo. – Azeirah Oct 29 '13 at 23:52