"The calling thread cannot access this object because a different thread owns it" 使用 System.Windows.Media.MediaPlayer

"The calling thread cannot access this object because a different thread owns it" using System.Windows.Media.MediaPlayer

我在使用 System.Windows.Media.MediaPlayer 播放声音时遇到问题,奇怪的是第一次它是如何工作的,但是第二次它抛出异常并显示调用线程无法访问的消息这个对象,因为另一个线程拥有它。

下面是我的代码,请看m_BackgroundWorker_DoWork 具体是它实际播放声音的地方,声音文件位置与上次存储的不同:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.Timers;
using System.Media;

namespace RPGInventor
{
    public partial class GameMapPanel : UserControl
    {
        protected Point m_Size;
        protected bool m_bGrid;
        protected CMap m_Map;
        protected System.Timers.Timer m_Timer;
        //protected SoundPlayer m_SoundPlayer;
        protected System.Windows.Media.MediaPlayer m_SoundPlayer;
        protected string m_szLastMedia;
        protected bool m_bRepeatBGS;

        public GameMapPanel()
        {
            InitializeComponent();
            this.Width = 0;
            this.Height = 0;
            m_Size = new Point(0, 0);
            m_Timer = new System.Timers.Timer(1000 / 60);
            m_Timer.Elapsed += m_Timer_Elapsed;
            //m_SoundPlayer = new SoundPlayer();
            m_SoundPlayer = new System.Windows.Media.MediaPlayer();
            m_szLastMedia = "";
        }

        void m_Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Invalidate();
        }

        public void setupGrid(int nCols, int nRows)
        {
            this.Width = nCols * 32;
            this.Height = nRows * 32;
            m_Size = new Point(nCols, nRows);
            m_Map.setGridSize(nCols, nRows);

            GameDialog parent = (GameDialog)this.Parent;
            parent.updateScrollBars();
        }

        public void setMap(CMap map)
        {
            this.m_Map = map;
            MainForm mf = (MainForm)CUtil.findForm("RPG Inventor");

            //m_BackgroundWorker.RunWorkerAsync();
            m_BackgroundWorker_DoWork(null, null);

            for (int i = 0; i < getMap().getEvents().Count; ++i)
            {
                CEvent cevent = (CEvent)getMap().getEvents()[i];
                if (cevent.getType() == (int)CEvent.EventType.TRANSFER)
                {
                    CTransferEvent transferEvent = (CTransferEvent)cevent;

                    if (CResources.getLoadedMap(transferEvent.getMapName()) == null)
                    {
                        CMap temp = new CMap("", 0, 0);
                        CMap Loadmap = temp.retrieve(mf.m_Database, transferEvent.getMapName());
                        CResources.addLoadedMap(Loadmap);
                    }

                    CLayer layer = (CLayer)getMap().getMapLayers()[1];

                    CTransferObject transferObj = new CTransferObject(transferEvent);
                    transferObj.setLocation(new Point(transferEvent.getGridLoc().X * 32,
                    transferEvent.getGridLoc().Y * 32));

                    layer.addObject(transferObj, 2);
                }
                if (cevent.getType() == (int)CEvent.EventType.DOOR)
                {
                    CDoorEvent doorEvent = (CDoorEvent)cevent;

                    if (CResources.getLoadedMap(doorEvent.getMapName()) == null)
                    {
                        CMap temp = new CMap("", 0, 0);
                        CMap Loadmap = temp.retrieve(mf.m_Database, doorEvent.getMapName());
                        CResources.addLoadedMap(Loadmap);
                    }

                    CLayer layer = (CLayer)getMap().getMapLayers()[1];
                    CDoorObject doorObj = new CDoorObject(doorEvent);
                    int nH = doorObj.getHeight();
                    int nDiff = nH - 32;
                    int nY = (doorEvent.getGridLoc().Y * 32) - nDiff;
                    doorObj.setLocation(new Point(doorEvent.getGridLoc().X * 32, nY));

                    layer.addObject(doorObj, 2);
                }
            }
        }

        public CMap getMap()
        {
            return this.m_Map;
        }

        public void addCharacterSet(CCharacterSet charSet)
        {
            CSpriteObject spriteObj = new CSpriteObject();
            spriteObj.loadImage(charSet.CharGraphic);
            spriteObj.setAnimations(charSet.CharSize.Y, charSet.CharSize.X);
            spriteObj.setCharSet(charSet);
            spriteObj.setVisible(true);
            //spriteObj.setListener(this);

            for (int i=0; i<m_Map.getEvents().Count; ++i)
            {
                CEvent cevent = (CEvent) m_Map.getEvents()[i];
                if (cevent.getType() == (int)CEvent.EventType.PLAYER_START)
                {
                    int nSpaceY = 32; int nDifference = spriteObj.getHeight() - nSpaceY;
                    spriteObj.setLocation(new Point(cevent.getGridLoc().X * 32, 
                            (cevent.getGridLoc().Y * 32) - nDifference));
                }
            }

            CLayer layer = (CLayer)m_Map.getMapLayers()[1];
            layer.addObject(spriteObj, 2);
        }

        public void addCharacterSet(CCharacterSet charSet, Point ptGrid, int nDir)
        {
            GameDialog gd = (GameDialog)this.Parent;
            MainForm mf = (MainForm)gd.Owner;
            CSpriteObject spriteObj = new CSpriteObject();
            spriteObj.loadImage(charSet.CharGraphic);
            spriteObj.setAnimations(charSet.CharSize.Y, charSet.CharSize.X);
            spriteObj.setCharSet(charSet);
            spriteObj.setVisible(true);
            spriteObj.setLocation(new Point(ptGrid.X * 32, ptGrid.Y * 32));
            spriteObj.setDirection(nDir);
            spriteObj.setCurrentAnim(nDir);
            //spriteObj.setListener(this);

            CLayer layer = (CLayer)m_Map.getMapLayers()[1];
            layer.addObject(spriteObj, 2);
        }

        public void Closed()
        {
            m_SoundPlayer.Stop();
            m_SoundPlayer.Close();
        }

        private void GameMapPanel_Load(object sender, EventArgs e)
        {
            //this.Height = 0;
            //this.Width = 0;
            GameDialog holder = (GameDialog)this.Parent;
            holder.updateScrollBars();
            m_Timer.Start();
        }

        private void GameMapPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            //create back buffer
            Bitmap backBuffer = new Bitmap(Width, Height);
            Graphics backG = Graphics.FromImage(backBuffer);

            if (m_Map != null)
            {
                ArrayList mapLayers = m_Map.getMapLayers();

                for (int i = 0; i < mapLayers.Count; ++i)
                {
                    CLayer layer = (CLayer)mapLayers[i];
                    layer.DrawLayer(backG);
                }
            }

            g.DrawImage(backBuffer, new Point(0, 0));
        }

        private void GameMapPanel_MouseEnter(object sender, EventArgs e)
        {
            this.Focus();
        }

        private void GameMapPanel_KeyDown(object sender, KeyEventArgs e)
        {
            //get CharSet
            GameDialog gd = (GameDialog)this.Parent;
            MainForm mf = (MainForm)gd.Owner;
            CSpriteObject player = new CSpriteObject();
            CCharacterSet dbCharSet = (CCharacterSet)mf.m_Database.m_Characters[0];

            ArrayList list = m_Map.getMapLayers();
            CLayer layer = (CLayer)list[1];

            for (int i = 0; i < layer.getObjects().Count; i++)
            {
                CMapObject mapObj = layer.getObject(i);
                if (mapObj.getType() == (int)CMapObject.MapObjectType.SPRITE)
                {
                    CSpriteObject sprite = (CSpriteObject)mapObj;
                    if (sprite.getCharSet().Name.Equals(dbCharSet.Name))
                    {
                        player = sprite;
                    }
                }
            }

            if (e.KeyCode == Keys.Down)
            {
                player.setDirection((int)CSpriteObject.Direction.DOWN);
                player.startTimer();
            }
            if (e.KeyCode == Keys.Up)
            {
                player.setDirection((int)CSpriteObject.Direction.UP);
                player.startTimer();
            }
            if (e.KeyCode == Keys.Left)
            {
                player.setDirection((int)CSpriteObject.Direction.LEFT);
                player.startTimer();
            }
            if (e.KeyCode == Keys.Right)
            {
                player.setDirection((int)CSpriteObject.Direction.RIGHT);
                player.startTimer();
            }
        }

        private void GameMapPanel_KeyUp(object sender, KeyEventArgs e)
        {
            //get CharSet
            GameDialog gd = (GameDialog)this.Parent;
            MainForm mf = (MainForm)gd.Owner;
            CSpriteObject player = new CSpriteObject();
            CCharacterSet dbCharSet = (CCharacterSet)mf.m_Database.m_Characters[0];

            ArrayList list = m_Map.getMapLayers();
            CLayer layer = (CLayer)list[1];

            for (int i = 0; i < layer.getObjects().Count; i++)
            {
                CMapObject mapObj = layer.getObject(i);
                if (mapObj.getType() == (int)CMapObject.MapObjectType.SPRITE)
                {
                    CSpriteObject sprite = (CSpriteObject)mapObj;
                    if (sprite.getCharSet().Name.Equals(dbCharSet.Name))
                    {
                        player = sprite;
                    }
                }
            }

            if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
            {
                player.stopTimer();
            }
        }

        private void GameMapPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Down:
                case Keys.Up:
                case Keys.Left:
                case Keys.Right:
                    e.IsInputKey = true;
                    break;
            }
        }

        private void m_BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            MainForm mf = (MainForm)CUtil.findForm("RPG Inventor");

            if (this.getMap().getSoundDir() != null && this.getMap().getSoundDir() != ""
                && this.getMap().getSoundName() != null && this.getMap().getSoundName() != "")
            {
                String szPath = ".\Projects\" + mf.m_Database.m_Name + "\Audio\" +
                    this.getMap().getSoundDir() + "\" + this.getMap().getSoundName();

                string szSoundPlayerPath = m_szLastMedia;

                if (!szSoundPlayerPath.Equals(szPath))
                {
                    try
                    {
                        m_SoundPlayer.Stop();
                        m_SoundPlayer.Open(new Uri(szPath, UriKind.Relative));
                        m_SoundPlayer.Play();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    //m_SoundPlayer.Stop();
                    //m_SoundPlayer.SoundLocation = szPath;
                    //m_SoundPlayer.PlayLooping();
                }
            }
        }
    }
}

在与 UI 线程不同的线程中使用 WPF 元素时,如果需要同步调用,则需要使用 Dispatcher.Invoke 调用,但如果可以,最好使用 Dispatcher.BeginInvoke.

更改您的代码:

System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
    m_SoundPlayer.Stop();
    m_SoundPlayer.Open(new Uri(szPath, UriKind.Relative));
    m_SoundPlayer.Play();
}));

在 WinForms 中,无法从任何其他线程访问在一个线程中创建的控件。所以你需要运行主线程上的代码使用Invoke

试试这个:

if (!szSoundPlayerPath.Equals(szPath))
{
    try
    {
        this.Invoke((MethodInvoker)delegate 
        {
            m_SoundPlayer.Stop();
            m_SoundPlayer.Open(new Uri(szPath, UriKind.Relative));
            m_SoundPlayer.Play();
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

我不是 100% 确定以上三行是否是导致异常的原因,但您只需要将导致错误的代码行包装在 Invoke 语句中。

非常感谢您的意见,这就是我想出的结果并且有效

if (InvokeRequired)
                    {
                        this.BeginInvoke(new Action(() =>
                        {
                            m_SoundPlayer.Stop();
                            m_SoundPlayer.Open(new Uri(szPath, UriKind.Relative));
                            m_SoundPlayer.Play();
                        }));
                    }
                    else
                    {
                        m_SoundPlayer.Stop();
                        m_SoundPlayer.Open(new Uri(szPath, UriKind.Relative));
                        m_SoundPlayer.Play();
                    }