"调用线程无法访问此对象,因为另一个线程拥有它"使用System.Windows.Media.MediaPlayer

时间:2015-03-25 21:42:02

标签: c# winforms visual-studio-2012

我在使用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();
                }
            }
        }
    }
}

3 个答案:

答案 0 :(得分:0)

在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语句中包含导致错误的任何代码行。

答案 1 :(得分:0)

非常感谢您的投入,这就是我提出的结果,并且它可以正常工作

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();
                    }

答案 2 :(得分:-2)

如果你想要一个同步调用,你需要使用Dispatcher.Invoke来调用与UI线程不同的线程内的WPF元素,但是如果可以,则优先使用Dispatcher.BeginInvoke。

使用以下代码更改您的代码:

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