COMException accessing WMP from Plex Plugin (using Python for .NET)

3

My Goal

I'm trying to create a plugin for Plex Media Server (PMS) that will interface with WMP (Windows Media Player) to get metadata about Windows media library items.

The Setup

  • PMS uses Python 2.7 as its primary script host. Plex Plugins are written in Python, though they operate in a sandboxed capacity. Unfortunately there's sad little documentation on what exactly the boundaries are on this sandboxed functionality.
  • I decided to use Python for .NET to access the Windows SDK to interface with WMPLib. Python for .NET (http://pythonnet.github.io/) is a Python library used to access functionality from .NET assemblies from within the Python runtime.
  • I created a .NET assembly to access WMPLib, which is a part of the Windows SDK designed to programmatically access the functionality of WMP. WMPLib is basically a COM Interop wrapper for .NET targeting wmp.dll.

What's Working?

The whole chain from Python on through the COM-based WMP access is working. If I fire up the embedded Plex Script Host that comes with Plex Media Server (a version of Python 2.7), I can readily access data from WMP. That means that the following links in the chain are all working:

  • Python is loading Python for .NET
  • Python for .NET is loading my .NET assembly
  • My .NET assembly is loading WMPLib (Interop.WMPLib.dll, a .NET assembly for COM Interop)
  • WMPLib is successfully opening and utilizing wmp.dll (accessed from C:\Windows\System32)

What's Not Working?

Activating the COM Interop part of this chain is not working from within the sandboxed Plex plugin. Again, this plugin is written in standard Python, but something is subtly different about the Python execution environment once the sandboxing code has run. I get the following exception when running the WMP access code from within the plugin:

COMException: Exception from HRESULT: 0xC00D1327
   at WMPLib.IWMPPlayer4.get_mediaCollection()
  • In this scenario I know that Python for .NET is working, because I've already loaded and accessed other things from my .NET assembly at this point.
  • C:\Windows\System32 is at the front of PATH variable. I'm assuming that COM dlls should be located via the PATH environment variable (This seems to say so), but I'm not entirely certain of that. How a COM assembly should be located in this unique scenario (Python accessing .NET accessing COM) is one of the biggest unknowns for me.

The Questions

  1. How might the Plex plugin sandbox be changing the Python execution environment such that accessing the COM assembly is no longer working?
  2. How should the COM assembly be located and accessed by the environment in this case?
  3. Does it require specific permissions that the Plex sandbox may have locked down?

Maybe I should at least win some kind of prize for arriving at a question that intersects so many different technologies in a uniquely confusing way...

Edit 1

I've ruled out any .NET related issues entirely, thanks to @Paulo's suggestion below. I'm now doing all interop with WMPLib through the comtypes Python library. Now I'm getting the following error:

COMError: (-1072884953, None, (None, None, None, 0, None))

Though -1072884953 is a different error code, a little digging around makes it appear that this error is associated with (maybe equivalent to?) the same error that I was getting through .NET interop (this post makes it appear so).

So now the facts that I'm stuck with are these:

  1. wmp.dll is loading in all cases (which @Paulo helped me to figure out below).
  2. When the code accessing WMP is run outside the Plex sandbox environment, library items can be accessed from WMP just fine.
  3. When the code accessing WMP is run inside the Plex sandbox environment, library items cannot be accessed from WMP.
  4. The error code that I get (whether from .NET or Python based COM interop is NS_E_CURL_INVALIDPATH: The URL contains a path that is not valid. This error appears to be involved with attempted playback in most cases.
  5. This is odd because I've never gotten as far as playback in my scenario... I'm only attempting to call wmp.mediaCollection

So the Plex sandbox truly seems to be key in this scenario. Any further ideas?

Edit 2

Minimally, this is the code that it takes to fail:

from comtypes.client import CreateObject

wmp = CreateObject("{6BF52A52-394A-11d3-B153-00C04F79FAA6}")
collection = wmp.mediaCollection

That collection = wmp.mediaCollection is where the error happens.

So there really aren't any parameters being passed in that could be causing the failure. To reiterate, this code runs fine in the general Python 2.7 context. It only fails within the Plex plugin sandbox. I don't know how to get details on how the Plex sandbox may be changing the execution environment. I'd imagine my answer lies in that direction.

python
.net
com
wmp
plex
asked on Stack Overflow Jul 14, 2014 by jdmcnair • edited Feb 8, 2016 by denfromufa

1 Answer

0

Let me get this straight, and correct me if I'm wrong:

  • You have a running instance of Python 2.7, Plex Media Server

  • You're using a library, Python for .NET, to load a .NET into your process

  • You're loading WMPLib, an imported COM interop assembly, in .NET to use the Windows Media Player library through Python for .NET

Let's clear this out:

  • 0xC00D1327 is NS_E_CURL_INVALIDPATH:

    The URL contains a path that is not valid.

    It seems like a legitimate object error, not a COM error.

  • The DLL search order has little to do with it, since wmp.dll is registered with a full path under InProcServer32 registry keys for each provided class, and that's what ultimately matters.

    In fact and as you stated, if you've reached this point, it's clearly not a problem about loading .NET assemblies or COM not finding a DLL.

Now, to the questions:

  1. Since the error seems legit, you might not have access to the media collection (Internet zone?), or WMP is not correctly registered/installed, or some codec is missing or not correctly registered/installed, etc.

    What are you loading into WMP? Try with basic things, such as local .WAV, .MP3, .AVI and .MPG files, then try more advanced formats e.g. MPEG4-encoded videos, or maybe remote locations.

  2. You should attempt a more direct approach, although I can't really vouch which is better: win32com (part of pywin32) or comtypes.

    It was a long while ago since I've looked at them, so take this with a grain of salt: with comtypes, you're able to use your COM objects much like regular Python objects with properties and methods, while win32com seems more inclined to do things by runtime name dispatching.

    At least you'll be punching something you really have to put up with (Python) instead of loading .NET for something that doesn't even require it (WMP).

  3. I don't know what that sandboxing is about, but my guess is that it's a Python-only sandbox, not something that restricts usage of the operating system.


EDIT: Are you providing a filename (e.g. C:\path\to\file.mp4) where a URL is expected (file:///C:/path/to/file.mp4), or vice-versa? I guess you must show the failing code and what values are being provided.

answered on Stack Overflow Jul 17, 2014 by acelent • edited Jul 23, 2014 by acelent

User contributions licensed under CC BY-SA 3.0