[英]Function just stops running after killing process
所以我在 C# 中編寫了一個程序,獲取當前可用的音頻 output 設備。 因此,當我運行該過程時,我會在 DataReceived 事件中獲得設備的名稱。 當它收到“DONE”時,它會終止進程並將保存的名稱添加到 TMP_Dropdown 選項中。 但問題是,當它到達 dropdown.ClearOptions() 時,程序只是停止而沒有任何錯誤消息。 當我添加一個斷點並繼續執行 function 時,黃色條就會消失,而 function 就會停止。 但是當我只是向設備添加一些隨機字符串而不運行 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();
}
}
}
您的問題很可能是多線程!
大多數 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");
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.