【问题标题】:Function just stops running after killing process函数在杀死进程后停止运行
【发布时间】:2021-03-17 10:56:22
【问题描述】:

所以我用 C# 编写了一个程序来获取当前可用的音频输出设备。因此,当我运行该过程时,我会在 DataReceived 事件中获得设备的名称。当它收到“DONE”时,它会终止进程并将保存的名称添加到 TMP_Dropdown 选项中。但问题是,当它到达 dropdown.ClearOptions() 时,程序只是停止而没有任何错误消息。当我添加断点并继续单步执行该功能时,黄色条就会消失,并且该功能会停止。但是当我只是向设备添加一些随机字符串而不运行 GetDevices() 时,它就像一个魅力。

这是我上面引用的代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using TMPro;

    public class GetAudioOutputDevices : MonoBehaviour
    {
        private Process process = null;
    
        public List<string> devices = new List<string>();
    
        [SerializeField]
        private TMP_Dropdown dropdown;
    
        private string selected;
    
    
        private void Start()
        {
            GetDevices();
        }
    
        //start new process that gets returns the current audio devices
        private void GetDevices()
        {
            devices.Clear();
    
            try
            {
                process = new Process();
                process.EnableRaisingEvents = false;
                process.StartInfo.FileName = Application.streamingAssetsPath + "/GetAudioDevices/GetAllAudioDevices.exe";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.CreateNoWindow = false;
                process.OutputDataReceived += new DataReceivedEventHandler(DataReceived);
                process.Start();
                process.BeginOutputReadLine();
    
                UnityEngine.Debug.Log("successfully started app");
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError("unable to launch app:" + e.Message);
            }
        }
    
        //event that recieves the data from the process
        void DataReceived(object sender, DataReceivedEventArgs eventArgs)
        {
            // check if process is done
            if (eventArgs.Data == "DONE")
            {
                process.Kill();
                DoneReadingDevices();
            }
            else
            {
                if (!string.IsNullOrEmpty(eventArgs.Data))
                {
    
                    if (eventArgs.Data.Contains("SELECTED:"))
                    {
                        string dat = eventArgs.Data;
                        dat = dat.Replace("SELECTED:", "");
                        selected = dat;
                    }
                    else
                    {
                        UnityEngine.Debug.Log(eventArgs.Data);
                        devices.Add(eventArgs.Data);
                    }
                }
            }
    
        }
    
        //adds the devices to a textmesh pro dropdown and selects the one that was passed as selected by the process
        public void DoneReadingDevices()
        {
            dropdown.ClearOptions();
    
            TMP_Dropdown.OptionData selectedOpDat = null;
    
            dropdown.AddOptions(devices);
    
            UnityEngine.Debug.Log(dropdown.options.Count);
    
    
            foreach (TMP_Dropdown.OptionData d in dropdown.options)
            {
                if(d.text == selected)
                {
                    selectedOpDat = d;
                    break;
                }
            }
    
    
            if(selectedOpDat != null)
            {
                dropdown.value = dropdown.options.IndexOf(selectedOpDat);
            }
            else
            {
                UnityEngine.Debug.Log("didn't find matching data");
            }
            
        }
    
    }

这是我编写的用于获取音频设备的 c# 程序:

using System;
using NAudio.CoreAudioApi;

namespace GetAllAudioDevices
{
    class Program
    {
        static void Main(string[] args)
        {
            var enumerator = new MMDeviceEnumerator();
            MMDevice active = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
            foreach (var endpoint in enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active))
            {
                Console.WriteLine(endpoint.FriendlyName);
            }
            Console.WriteLine("SELECTED:" + active.FriendlyName);
            Console.WriteLine("DONE");
            Console.ReadLine();
        }
    }
}

【问题讨论】:

    标签: c# unity3d process


    【解决方案1】:

    您的问题很可能是多线程!

    大部分 Unity API(任何直接依赖或影响场景的东西)只能在 Unity 主线程中使用,不能在任何后台线程/任务中使用。

    您对process.OutputDataReceived 的回调很可能发生在单独的线程上。


    您宁愿需要将接收到的数据“分派”回 Unity 主线程。

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using UnityEngine;
    using TMPro;
    
    using System.Linq;
    
    public class GetAudioOutputDevices : MonoBehaviour
    {
        [SerializeField]
        private TMP_Dropdown dropdown;
    
    
        private Process process = null;
    
        // Thread-safe public read-only access to the _devices list
        public IReadOnlyList<string> devices
        {
           get
           {
               lock(_lock)
               {
                   return _devices;
               }
           }
        }
    
        // Here we actually let the thread/process write devices to
        // -> only access via the lock
        private readonly List<string> _devices = new List<string>();
    
        // Our lock object for thread-safe read-write
        // see https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/lock-statement
        private readonly object _lock = new object();
    
        // This will be set by the process handler
        // -> only access via the lock
        private string selected;
    
        // This will be set by the process handler
        // -> only access via the lock
        private bool done;
    
    
        // If you change the return type of Start to IEnumerator
        // Unity automatically runs it as a Coroutine
        // See https://docs.unity3d.com/Manual/Coroutines.html
        private IEnumerator Start()
        {
            GetDevices();
    
            // wait until we finished receiving the devices
            // see https://docs.unity3d.com/ScriptReference/WaitUntil.html
            yield return new WaitUntil(CheckIfDone);
    
            // will now be executed in the Unity main thread
            DoneReadingDevices();
        }
    
        private bool CheckIfDone()
        {
            lock(_lock)
            {
                return done;
            }
        }
    
        //start new process that gets returns the current audio devices
        private void GetDevices()
        {
            lock(_lock)
            {
                devices.Clear();
            }
    
            try
            {
                process = new Process();
                process.EnableRaisingEvents = false;
                process.StartInfo.FileName = Application.streamingAssetsPath + "/GetAudioDevices/GetAllAudioDevices.exe";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.CreateNoWindow = false;
                process.OutputDataReceived += new DataReceivedEventHandler(DataReceived);
                process.Start();
                process.BeginOutputReadLine();
    
                UnityEngine.Debug.Log("successfully started app");
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError("unable to launch app:" + e.Message);
            }
        }
    
        //event that recieves the data from the process
        void DataReceived(object sender, DataReceivedEventArgs eventArgs)
        {
            // check if process is done
            if (eventArgs.Data.Equals("DONE"))
            {
                process.Kill();
                
                lock(_lock)
                {
                    done = true;
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(eventArgs.Data))
                {
                    if (eventArgs.Data.Contains("SELECTED:"))
                    {
                        var dat = eventArgs.Data;
                        dat = dat.Replace("SELECTED:", "");
                        lock(_lock)
                        {
                            selected = dat;
                        }
                    }
                    else
                    {
                        UnityEngine.Debug.Log(eventArgs.Data);
                        lock(_lock)
                        {
                            _devices.Add(eventArgs.Data);
                        }
                    }
                }
            }
    
        }
    
        //adds the devices to a textmesh pro dropdown and selects the one that was passed as selected by the process
        public void DoneReadingDevices()
        {
            dropdown.ClearOptions();
    
            TMP_Dropdown.OptionData selectedOpDat = null;
    
            lock(_lock)
            {
                dropdown.AddOptions(devices);
                // Using Linq instead of the foreach loop
                // see https://docs.microsoft.com/dotnet/api/system.linq.enumerable.firstordefault
                selectedOpDat = dropdown.options.FirstOrDefault(d => d.text.Equals(currentSelected));
            }
    
            UnityEngine.Debug.Log(dropdown.options.Count);
    
            if(selectedOpDat != null)
            {
                dropdown.value = dropdown.options.IndexOf(selectedOpDat);
            }
            else
            {
                UnityEngine.Debug.Log("didn't find matching data");
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多