7

我是前阵子来这里询问通过 Java 控制 Windows Media Player 的人。 我已经取得了进展,但我遇到了一个令人烦恼的问题,所以我回来寻求帮助。

我按照上次得到的建议安装了 Jacob。我从测试脚本中执行这些行:

ActiveXComponent wmp = new ActiveXComponent("WMPlayer.OCX");
wmp.invoke("openPlayer", "http://somafm.com/wma128/groovesalad.asx");

...然后 WMP 弹出,播放 SomaFM。“哇!” 我认为。“我已经解决了这个问题!”

除了当我在创建该对象后与它进行交互时,它似乎与播放的 WMP 实例没有任何关系。当我执行此代码时:

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"));

...我得到以下输出:

音量:50
音量:0

这似乎无伤大雅,除了

  1. “50”的音量与实际设置的播放器音量无关,
  2. 在 setProperty 调用之后,播放器的音量实际上并没有改变。

我也尝试过其他属性,但都是一样的:属性的值似乎与玩家实际在做什么无关,而更改它们似乎会改变被操纵对象的状态,它对实际播放器没有影响。 (每次运行脚本时我都会得到完全相同的输出,所以无论我在旋转“音量”时更改的是什么,它在代码之外都没有任何持久性。)

显然我做错了什么,但我盲目地摸索着想弄清楚是什么。任何人都可以向我提供任何关于出了什么问题的见解,或者我接下来应该尝试什么?

(注意:我什至不确定“WMPlayer.OCX”是正确的输入参数。我在注册表中的 HKEY_CLASSES_ROOT 中尝试了看起来很可能的条目,直到找到这个。)

我提前感谢任何人可以提供的任何帮助。


编辑,2009 年 4 月 15 日: 我在一家名为 EZ JCom 的公司的产品中发现了一个特定于 WMP 的软件包。它的失败方式与我之前看到的完全相同,要么它只是 Jacob 的包装器,要么 WMP ActiveX/COM 接口完全损坏。(等等,我为什么说“要么”?)

我与客户服务聊天,他们最终展示了您如何在没有实际用处的情况下提供帮助。他们帮助我更正了他们提供的非编译示例代码,作为他们的 WMP 代码运行示例,但是当我纠缠他们以了解 get/set volume 方法应该如何工作时,我得到了这个:

“抱歉,这里没有 WMP 深入的专业知识 - EZ JCom 只是 Java 和 WMP 等其他程序之间的桥梁建设者。”

请记住,我正在评估的他们的软件包实际上称为“wmp.WindowsMediaPlayer”。如果我让它发挥作用,我将不得不说服我的老板花 600 美元购买许可证。有人想知道如果他们真的对自己的产品有一些专业知识,他们会收取什么费用。

所以,没有真正的进展。只是想我会分享。


编辑,2009 年 4 月 20 日: 是的,我还在纠结这个。我目前的操作理论是,为了获得音量设置,我需要远程访问 WMP。我见过提到 IWMPRemoteMediaServices 和 IServiceProvider 接口,后者的 QueryService 方法提供了指向前者的指针。不幸的是,我没有任何运气弄清楚如何获得 IServiceProvider。我已经看到提到它可以从 Windows“系统”对象访问,但我不知道如何获取该对象。(而且由于“系统”这个词在 Java 中占有很大比重,谷歌给了我一个可怕的噪音:信号比。)如果有人对我如何处理表示 System.dll 的 COM 对象有任何建议,我很乐意听到。


编辑,2009 年 4 月 21 日: 澄清:这是在 XP 系统上。

另外:我的研究表明仅与 WMP 对象交谈是不够的;你需要把它包得更紧,这样它才能回话。有一个包含很多 C++ 内容的 WMP SDK,但它似乎依赖于 Microsoft Visual C++ 对我没有的代码的扩展,而且它们并不是免费赠送的。(此外,我已经有 12 年没有使用过 C++了。)我知道使用 C# 是可能的,但是如果我不使用 Java,我需要解决方案是一个独立的可执行文件,并且没有安装 .NET相关机器。


编辑,2009 年 4 月 22 日: 根据 Mark 在下面的回答,我从 WinUser.h 中挖掘了 APPCOMMAND_MEDIA_* 常量并尝试了以下代码,该代码使用了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());

这给了我一个结果:

wmpHandle: 1640048
查找窗口错误?空值
播放结果:-1
播放错误?空值
暂停结果:-1
暂停错误?空值

...但实际上并不影响媒体播放器。

我也用 APPCOMMAND_MEDIA_PLAY_PAUSE (14) 进行了尝试,它给出了不同的返回值 (20),但也没有做任何事情。

FWIW,我真的需要让单独的 PLAY/PAUSE 命令来工作,这是一个可行的选择;只是盲目地切换状态对我没有帮助,因为我不知道当我开始时玩家处于什么状态。

有没有人对我做错了什么或我可以尝试什么有任何建议?

4

2 回答 2

2

那么这些不起作用吗?

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

(为不正确的代码道歉;我以前从未使用过 Jacob)


在这种情况下,创建/查找任何窗口并将其发送APPCOMMAND_MEDIA_PLAY_PAUSE。默认消息处理将使其影响 WMP。(发送静音不好,因为这会使整个系统静音。)

为了可移植性,我建议创建一个 C++ 命令行实用程序或使用 JNI,但现在NativeCall可能就足够了。


您的代码看起来不错,但我认为您只需将参数更改为 SendMessage。尝试:

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

APPCOMMAND_MEDIA_PLAY需要 XP SP1,但我假设您已经部署了它。

于 2009-04-21T15:21:15.080 回答
1

看起来当您将 wmpSettings 实例化为新的 ActiveXComponent 时,Jacob 实际上正在做一些有趣的事情来将组件包装在新的媒体播放器对象中,而不是检索您要求的设置属性。

您是否简单地尝试过:

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

我还回去阅读了让你开始使用 Jacob 的原始问题,我可以建议一种不同的方法,以防你在这个问题上旋转太久。

我已经设置了一个 Web 应用程序,该应用程序与当前登录到 Web 应用程序服务器的特定用户桌面上的 winamp 运行的直播服务器进行交互。我无法使用 COM 从 Web 应用程序的上下文中直接与用户的 winamp 实例通信,因此我设置了一个简单的 C# TCP/IP winamp 桥应用程序,该应用程序在shoutcast 用户的桌面上运行并允许 Web 应用程序创建一个来自本地主机的套接字连接。

对于 WMP,我相信您可以找到一些 C# 包装器,例如 WmpRemote.zip 的代码,如果您在http://d.hatena.ne.jp/punidama/20080227上进行文本搜索,您可以找到

如果您需要任何具体示例来进行设置,请告诉我。

于 2009-04-15T12:51:37.520 回答