using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Security.Cryptography;
using System.Text.Json.Nodes;
using System.Windows.Forms;
using Sim.Host;
namespace System.Windows.Forms
{
///
/// ActiveX 控件的基类。VS 把 COM 控件拖到窗体上时生成 OcxState = resources.GetObject("…OcxState"),
/// 平台编译时给每个窗体嵌了空的资源,这里取到的是 null,不影响使用。
///
public abstract class AxHost : Control, ISupportInitialize
{
protected AxHost(string clsid) { }
public State? OcxState { get; set; }
public void BeginInit() { }
public void EndInit() { }
public class State { }
}
}
namespace WMPLib
{
public enum WMPPlayState
{
wmppsUndefined = 0, wmppsStopped = 1, wmppsPaused = 2, wmppsPlaying = 3, wmppsScanForward = 4, wmppsScanReverse = 5,
wmppsBuffering = 6, wmppsWaiting = 7, wmppsMediaEnded = 8, wmppsTransitioning = 9, wmppsReady = 10, wmppsReconnecting = 11,
}
/// 播放控制:axWindowsMediaPlayer1.Ctlcontrols.play()
public interface IWMPControls
{
void play();
void pause();
void stop();
double currentPosition { get; set; }
}
/// 播放设置:axWindowsMediaPlayer1.settings.volume
public interface IWMPSettings
{
bool autoStart { get; set; }
int volume { get; set; }
bool mute { get; set; }
void setMode(string bstrMode, bool varfMode);
}
}
namespace AxWMPLib
{
using WMPLib;
///
/// Windows Media Player 控件(教材项目 7 的"广播"播放 ring.mp3)。和真的一样:设置 URL 后自动开始播放(settings.autoStart),
/// 也可以调用 Ctlcontrols.play()。声音在浏览器里播放;实训台上的广播喇叭随之亮起。只支持本机文件。
///
[ClientType("AxWindowsMediaPlayer")]
public class AxWindowsMediaPlayer : AxHost
{
private string _url = "";
private WMPPlayState _playState = WMPPlayState.wmppsUndefined;
public AxWindowsMediaPlayer() : base("6bf52a52-394a-11d3-b153-00c04f79faa6")
{
Ctlcontrols = new Controls(this);
settings = new Settings();
}
protected override Size DefaultSize => new(245, 240);
public string URL
{
get => _url;
set
{
_url = value ?? "";
if (settings.autoStart && _url.Length > 0) Play();
}
}
public IWMPControls Ctlcontrols { get; }
// 属性名和 COM 接口一样是小写
public IWMPSettings settings { get; }
public WMPPlayState playState => _playState;
public string uiMode { get; set; } = "full";
public bool stretchToFit { get; set; }
public bool enableContextMenu { get; set; } = true;
public string status => _playState == WMPPlayState.wmppsPlaying ? "正在播放" : "已停止";
public event EventHandler? PlayStateChange;
private void Play()
{
LegacyThreads.CheckPoint();
// 教材写 BaseDirectory + "//Resources//ring.mp3":路径里多余的斜杠在 Linux 上也能用
var path = _url.StartsWith("file:///", StringComparison.OrdinalIgnoreCase) ? new Uri(_url).LocalPath : _url;
if (!File.Exists(path))
{
SimHost.SendConsole($"Windows Media Player:找不到要播放的文件 {_url}\n");
SetState(WMPPlayState.wmppsStopped);
return;
}
var data = File.ReadAllBytes(path);
var mime = Path.GetExtension(path).ToLowerInvariant() switch
{
".wav" => "audio/wav",
".ogg" => "audio/ogg",
".m4a" or ".aac" => "audio/aac",
_ => "audio/mpeg",
};
var key = SimHost.DataKey(Convert.ToHexString(SHA1.HashData(data)).ToLowerInvariant(), data, mime);
SimHost.Send(new JsonObject { ["type"] = "media", ["id"] = SimId, ["action"] = "play", ["key"] = key, ["name"] = Path.GetFileName(path) });
SetState(WMPPlayState.wmppsPlaying);
}
private void Stop()
{
if (_playState != WMPPlayState.wmppsPlaying) return;
SimHost.Send(new JsonObject { ["type"] = "media", ["id"] = SimId, ["action"] = "stop" });
SetState(WMPPlayState.wmppsStopped);
}
private void SetState(WMPPlayState state)
{
if (_playState == state) return;
_playState = state;
PlayStateChange?.Invoke(this, EventArgs.Empty);
}
protected override void WriteProps(JsonObject props)
{
base.WriteProps(props);
props["URL"] = _url;
}
private sealed class Controls(AxWindowsMediaPlayer owner) : IWMPControls
{
public void play()
{
if (owner._playState != WMPPlayState.wmppsPlaying && owner._url.Length > 0) owner.Play();
}
public void pause() => owner.Stop();
public void stop() => owner.Stop();
public double currentPosition { get; set; }
}
private sealed class Settings : IWMPSettings
{
public bool autoStart { get; set; } = true;
public int volume { get; set; } = 50;
public bool mute { get; set; }
public void setMode(string bstrMode, bool varfMode) { }
}
}
}