I'm the guy who was here a while back asking about controlling Windows Media Player via Java. I've made progress, but I've hit a vexing problem, so I'm back looking for help.
I followed the advice I got last time 'round and installed Jacob. I execute these lines out of a test script:
ActiveXComponent wmp = new ActiveXComponent("WMPlayer.OCX");
wmp.invoke("openPlayer", "http://somafm.com/wma128/groovesalad.asx");
... and WMP pops up, playing SomaFM. "W00t!" I think. "I've got this one solved!"
Except that when I interact with this object after its created, it doesn't seem to have anything to do with the WMP instance playing. When I execute this code:
ActiveXComponent wmpSettings = new
ActiveXComponent(wmp.getProperty("settings").toDispatch());
System.out.println("VOLUME: " + wmpSettings.getProperty("volume"));
wmpSettings.setProperty("volume", 0);
System.out.println("VOLUME: " + wmpSettings.getProperty("volume"));
... I get an output of:
VOLUME: 50 VOLUME: 0
This seems innocuous enough, except that
- the volume of "50" has nothing to do with where the player volume is actually set and
- the volume of the player doesn't actually change after the setProperty call.
I've tried other properties as well, but it's the same thing: the value of the properties doesn't seem to be related to what the player is actually doing, and while changing them seems to alter the state of the object being manipulated, it has no affect on the actual player. (I get the exact same output each time I run the script, so whatever it is I'm altering when I twiddle "volume", it doesn't have any persistence outside of the code.)
Obviously I'm doing something wrong, but I'm fumbling around blindly trying to figure out what. Can anybody offer me any insight into what's going awry, or what I ought to be trying next?
(Note: I'm not even certain "WMPlayer.OCX" is the right input parameter. I experimented with likely-looking entries in HKEY_CLASSES_ROOT in the registry until I found this one.)
My thanks in advance for any help anybody can offer.
Edit, 4/15/2009: I found a WMP-specific package in the offerings from a company called EZ JCom. It failed in ways so identical to what I was seeing before that either it's just a wrapper for Jacob, or the WMP ActiveX/COM interface is plain broken. (Wait, why did I say "either"?)
I chatted with customer service, and they wound up demonstrating how you can be helpful without actually being useful. They helped me correct the non-compiling sample code they provided as an example of their WMP code in action, but when I pestered them for some insight into how the get/set volume methods are supposed to work, I got this:
"Sorry, but there is no WMP in-depth expertise available here - EZ JCom is just a bridge builder between Java and other programs like WMP."
Bear in mind the package of theirs I was evaluating is actually called "wmp.WindowsMediaPlayer". Had I gotten it to work, I would have had to talk my boss into shelling-out $600 for the license. One wonders what they'd charge if they actually had some expertise on their own product.
So, no real progress. Just thought I'd share.
Edit, 4/20/2009: Yeah, I'm still poking at this. My current operating theory is that in order to get at the volume settings, I'll need to access WMP remotely. I've seen mention of the IWMPRemoteMediaServices and IServiceProvider interfaces, with the QueryService method of the latter providing a pointer to the former. Unfortunately, I'm not having any luck figuring out how to get a hold of IServiceProvider. I've seen mention that it's accessible from the windows "System" object, but I can't figure out how to get a hold of that object. (And since the word "System" figures pretty heavily into Java, Google is giving me a hellacious noise:signal ratio.) If anybody has any advice on how I lay hands on the COM object representing System.dll, I'd love to hear it.
Edit, 4/21/2009: Clarification: this is on an XP system.
Also: my research is suggesting that merely talking to the WMP object is insufficient; you need to wrap it more tightly than that so it can talk back. There's a WMP SDK with a lot of C++ stuff, but it appears to rely on Microsoft Visual C++ extensions to the code that I don't have and they're not giving away for free. (Besides, I haven't done C++ in twelve years.) I know it's possible with C#, but if I'm going outside of Java, I'd need the solution to be a standalone executable and .NET isn't installed on the relevant machines.
Edit, 4/22/2009: Per Mark's answer below, I dug the APPCOMMAND_MEDIA_* constants out of WinUser.h and tried the following code, which makes use of the NativeCall api:
final int APPCOMMAND_MEDIA_PLAY = 46;
final int APPCOMMAND_MEDIA_PAUSE = 47;
NativeCall.init();
IntCall findWindow = new IntCall("user32", "FindWindowA");
int wmpHandle = findWindow.executeCall(new Object[] { null, "Windows Media Player" });
System.out.println("wmpHandle: " + wmpHandle);
System.out.println("Find Window Error? " + findWindow.getLastError());
IntCall sendMessage = new IntCall("user32", "SendMessageA");
int playResult = sendMessage.executeCall(new Object[] { wmpHandle, APPCOMMAND_MEDIA_PLAY, 0, 0 });
System.out.println("Play Result: " + playResult);
System.out.println("Play Error? " + sendMessage.getLastError());
try { Thread.sleep(5000); } catch (Exception e) {}
int pauseResult = sendMessage.executeCall(new Object[] { wmpHandle, APPCOMMAND_MEDIA_PAUSE, 0, 0 });
System.out.println("Pause Result: " + pauseResult);
System.out.println("PauseError? " + sendMessage.getLastError());
This gives me a result of:
wmpHandle: 1640048 Find Window Error? null Play Result: -1 Play Error? null Pause Result: -1 PauseError? null
... but doesn't actually affect the media player.
I've also tried it with APPCOMMAND_MEDIA_PLAY_PAUSE (14), which gives different return values (20) but doesn't do anything either.
FWIW, I really need to get the individual PLAY/PAUSE commands to work for this to be a viable option; simply toggling the state blindly doesn't help me since I don't know what state the player is in when I start.
Does anybody have any advice on what I'm doing wrong or what else I might try?