Heavily confused by win32api + COM and an answer f

2019-06-08 00:55发布

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.

1条回答
我只想做你的唯一
2楼-- · 2019-06-08 01:21

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)
查看更多
登录 后发表回答