目的: 为了不干扰或者阻塞主线程
Unity中使用Process类来开启进程
Process类:
启动和停止进程, 获取或设置进程优先级, 是否已经退出, 以及获取系统正在运行的所有进行列表和各进程资源占用情况。
入参arguments: 控制台需要输入的命令行
文件名FileName : 传入exe绝对路径,关于unity路径问题最好规范点,统一使用'/'
private void CallCmd(string arguments)
{
Encoding GBK = Encoding.GetEncoding(936);
try
{
Process process = new Process();
process.StartInfo.FileName = m_StreamingAssertExePath;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
process.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
process.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
process.StartInfo.RedirectStandardError = true;//重定向标准错误输出
process.StartInfo.StandardOutputEncoding = GBK;
process.StartInfo.CreateNoWindow = true;//不显示程序窗口
process.Start();//启动程序
process.OutputDataReceived += (s, _e) =>
{
UnityEngine.Debug.Log(GBK2UTF8(_e.Data));
};
process.ErrorDataReceived += (s, _e) =>
{
UnityEngine.Debug.Log(GBK2UTF8(_e.Data));
};
process.EnableRaisingEvents = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
catch (Exception e)
{
UnityEngine.Debug.LogError(e.Message);
}
}
public static string GBK2UTF8(string value)
{
Encoding GBK = Encoding.GetEncoding(936);
byte[] bytes = Encoding.Convert(GBK, Encoding.UTF8, GBK.GetBytes(value));
return Encoding.UTF8.GetString(bytes);
}
直接调用CallCmd的话,会发现还是会阻塞主线程, 把process执行完才会执行主线程.
所以要把此函数放到子线程里去执行,*.exe没有做死循环或者阻碍线程运行的操作,所以在子线程调用结束会等待自动回收的.所以没必要Abort().
这里是自己写的保存一张照片命令行
public void Send_GetPicture()
{
if (!HasCmdExe) return;
Thread thread = new Thread(new ThreadStart(exe_Picture));
thread.Start();
}
private void exe_Picture()
{
string instructions = string.Format("{0},{1},{2}", (int)CmdSelect.Capture, 0, m_URL);
CallCmd(instructions);
}
控制台应用(.NET Framework)项目
这里注意控制台使用Console.ReadLine输入指令,unity调用是通过args输入
尽量不要去做阻碍程序运行的操作,比如文末注释掉的Console.ReadKey();
如果加了这行,unity是不会停止子线程的,需要手动把控制台进程杀掉(Process.kill) 以及中止子线程(Abort)
static void Main(string[] args)
{
Console.Write(@"
--------------------------模式选择--------------------------------
<0> - 上传设备信息 <!-- 0 -->,<!-- Custom -->,<!-- URL -->
<1> - 上传日志 <!-- 1 -->,<!-- Custom -->,<!-- URL -->,<!-- LogPath -->
<2> - 获取当前照片 <!-- 2 -->,<!-- Custom -->,<!-- URL -->
请输入指令: ");
string readLine = string.Empty;
#if DEBUG
readLine = Console.ReadLine().Trim();
#else
if (args.Length <=0)
{
return;
}
readLine = args[0].Trim();
#endif
string[] analysis = readLine.Split(',');
//上传设备信息
if (analysis.Length == 3 && Convert.ToInt32(analysis[0]) == 0)
{
//TODO: 获取系统配置并提交至服务器
}
//上传日志
else if (analysis.Length == 4 && Convert.ToInt32(analysis[0]) == 1)
{
//TODO: 获取unity的日志并提交至服务器
//我这里是由unity保存log日志,写入StreamingAssert/yyyy_MM_dd_HH_mm_ss.log,
//命令行传入路径,这里读取数据流上传服务器
}
//获取当前照片
else if (analysis.Length == 3 && Convert.ToInt32(analysis[0]) == 2)
{
//TODO: 拍摄一张照片并提交至服务器
}
else
{
Console.WriteLine("ErrorCode:{0} Message:{1}", (int)SystemCode.Error, "未知错误!");
}
//Console.WriteLine("--------------------------任意键退出--------------------------");
//Console.ReadKey();
}
关于生成有个小技巧:
一般我们的控制台项目会应用第三方库(FFmpeg,Aforge,LitJson,OpenCV等等), 生成exe的时候这些*.dll也会随之生成在同级目录下, 使用Costura.Fody将源代码合并到exe里面, 生成结束, 把exe拉取到任意目录都可以运行.
通过NuGet管理导入Costura.Fody