views:

848

answers:

2

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

  1. the volume of "50" has nothing to do with where the player volume is actually set and
  2. 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?

+1  A: 

Hi, it looks like when you instantiate wmpSettings as a new ActiveXComponent, Jacob is actually doing something funny to wrap the component in a new media player object instead of retrieving the settings property you're asking for.

Have you tried simply:

Dispatch wmpSettings = wmp.getProperty("settings").toDispatch();
wmpSettings.setProperty("volume", 0);

I also went back and read the original question that got you into using Jacob and I can suggest a different approach in case you wind up spinning your wheels on this for too long.

I had setup a web application which interacted with a shoutcast server running from winamp on the desktop of a specific user that was currently logged in to the web app server. I couldn't use COM to communicate directly with the user's instance of winamp from within the context of the web application so I setup a simple C# TCP/IP winamp bridge application that ran on the shoutcast user's desktop and allowed the web application to make a socket connection from localhost.

For WMP I'm sure you can find some C# wrappers like the code for WmpRemote.zip you can find if you do a text search on http://d.hatena.ne.jp/punidama/20080227

Let me know if you need any specific examples for setting this up.

nvuono
Hey, thanks for the feedback. The code you suggest doesn't compile (no such thing as Dispatch.setProperty()), but this does: "Dispatch.put(wmpSettings, "volume", 0);" Produces exactly the same behavior as before, unfortunately
BlairHippo
Do you know of any such C# wrappers that have been compiled to run as standalone apps? Deploying .NET to all the machines that would need to run this isn't an option. (I concede I don't know a lot about C#/.NET, so my apologies if that's a stupid question. :-) )
BlairHippo
+1  A: 

So do these not work?

wmp.getProperty("settings").toDispatch().setProperty("mute", 1);
wmp.getProperty("controls").toDispatch().invoke("pause");

(Apologies for incorrect code; I've never used Jacob before)


In that case, create/find any window and send it APPCOMMAND_MEDIA_PLAY_PAUSE. The default message processing will make it affect WMP. (Sending mute is no good, as that will mute the whole system.)

For portability, I'd recommend creating a C++ command line utility or using JNI, but NativeCall might suffice for now.


Your code looks good, but I think you just need to change the parameters to SendMessage. Try:

final int WM_APPCOMMAND = 0x0319;
int playResult = sendMessage.executeCall(new Object[] {
        wmpHandle,
        WM_APPCOMMAND,
        wmpHandle,
        APPCOMMAND_MEDIA_PLAY << 16});

APPCOMMAND_MEDIA_PLAY requires XP SP1, but I'm assuming you have that deployed.

Mark
I very sincerely wish. :-) Unfortunately, no. Further research has hinted that, basically, when you try to talk to WMP this way, it's going to ask questions back -- and if it doesn't like what you have to say, it will ignore you. (Which is consistent with my experience.) Executing those commands is like playing with a toy steering wheel attached to a sandbox; you can spin it all you like, but nothing's actually happening.
BlairHippo
Yes, you probably need to implement IWMPRemoteMediaServices, which I'm guessing Jacob can't do.
Mark
Thanks! This is an approach I hadn't considered before, but unfortunately, it's not quite working yet. Please see the edited post above and let me know if you have any insight into how I'm screwing it up.
BlairHippo
Oh, HELL yes. You, sir, win a cookie. And my deepest thanks; I get to look like a genius and our clients will be happier; both Very Good Things. One last pair of bonus questions: do you know if this trick is liable to work on a media player running inside a browser? And do you know how to use FindWindow to grab in instance of Internet Explorer when I won't know the window's exact name? As long as I have this solution in my pocket, may as well see what else it can do. :-)
BlairHippo
And BTW, is there a particular resource you used to come up with that code, or is that mainly your experience talking?
BlairHippo
Sorry, I don't think this will work on an embedded player, but in that case you could use Javascript. The idea came from when I wanted to mute my system on a certain event (I naively sent it to HWND_BROADCAST, putting my computer into a muting/demuting frenzy). The mechanism is the bit about the shell hook in http://msdn.microsoft.com/en-us/library/ms646275.aspx.Thanks for the cookie; if I think of a better solution, I'll post back here.
Mark
Of course, in the long run, you're better off using something completely scriptable, like Flash or VLC (http://www.autoitscript.com/forum/index.php?showtopic=76230). Best of luck!
Mark
Heh. Tell me about it. I'm definitely pushing for us to quit encouraging clients to use WMP and push them something we can manage, but with several hundred units already in the field, I had a lot of motivation to find a legacy-friendly solution.
BlairHippo

related questions