AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
ExternalProcess.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using System;
5 using System.Diagnostics;
6 using System.IO;
7 using System.Runtime.InteropServices;
8 using System.Threading;
9 using UnityEngine;
10 
11 namespace HoloToolkit.Unity
12 {
16  public class ExternalProcess : IDisposable
17  {
18  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
19  private static extern IntPtr ExternalProcessAPI_CreateProcess([MarshalAs(UnmanagedType.LPStr)] string cmdline);
20  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
21  private static extern bool ExternalProcessAPI_IsRunning(IntPtr handle);
22  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
23  private static extern void ExternalProcessAPI_SendLine(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string line);
24  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
25  private static extern IntPtr ExternalProcessAPI_GetLine(IntPtr handle);
26  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
27  private static extern void ExternalProcessAPI_DestroyProcess(IntPtr handle);
28  [DllImport("ExternalProcessAPI", CallingConvention = CallingConvention.Cdecl)]
29  public static extern void ExternalProcessAPI_ConfirmOrBeginProcess([MarshalAs(UnmanagedType.LPStr)] string processName);
30 
31  private IntPtr mHandle;
32 
37  private static string sAppDataPath;
38 
39  public static void Launch(string appName)
40  {
41  // Full or relative paths only. Currently unused.
42 
43  if (!appName.StartsWith(@"\"))
44  {
45  appName += @"\";
46  }
47 
48  string appPath = AppDataPath + appName;
49  string appDir = Path.GetDirectoryName(appPath);
50 
51  Process pr = new Process();
52  pr.StartInfo.FileName = appPath;
53  pr.StartInfo.WorkingDirectory = appDir;
54  pr.Start();
55  }
56 
57  private static string AppDataPath
58  {
59  get
60  {
61  if (string.IsNullOrEmpty(sAppDataPath))
62  {
63  sAppDataPath = Application.dataPath.Replace("/", @"\");
64  }
65 
66  return sAppDataPath;
67  }
68  }
69 
70  public static bool FindAndLaunch(string appName)
71  {
72  return FindAndLaunch(appName, null);
73  }
74 
75  public static bool FindAndLaunch(string appName, string args)
76  {
77  // Start at working directory, append appName (should read "appRelativePath"), see if it exists.
78  // If not go up to parent and try again till drive level reached.
79 
80  string appPath = FindPathToExecutable(appName);
81  if (appPath == null)
82  {
83  return false;
84  }
85 
86  string appDir = Path.GetDirectoryName(appPath);
87 
88  Process pr = new Process();
89  pr.StartInfo.FileName = appPath;
90  pr.StartInfo.WorkingDirectory = appDir;
91  pr.StartInfo.Arguments = args;
92 
93  return pr.Start();
94  }
95 
96  public static string FindPathToExecutable(string appName)
97  {
98  // Start at working directory, append appName (should read "appRelativePath"), see if it exists.
99  // If not go up to parent and try again till drive level reached.
100 
101  if (!appName.StartsWith(@"\"))
102  {
103  appName = @"\" + appName;
104  }
105 
106  string searchDir = AppDataPath;
107 
108  while (searchDir.Length > 3)
109  {
110  string appPath = searchDir + appName;
111 
112  if (File.Exists(appPath))
113  {
114  return appPath;
115  }
116 
117  searchDir = Path.GetDirectoryName(searchDir);
118  }
119 
120  return null;
121  }
122 
123  public static string MakeRelativePath(string path1, string path2)
124  {
125  // TBD- doesn't really belong in ExternalProcess.
126 
127  path1 = path1.Replace('\\', '/');
128  path2 = path2.Replace('\\', '/');
129  path1 = path1.Replace("\"", "");
130  path2 = path2.Replace("\"", "");
131 
132  Uri uri1 = new Uri(path1);
133  Uri uri2 = new Uri(path2);
134  Uri relativePath = uri1.MakeRelativeUri(uri2);
135  return relativePath.OriginalString;
136  }
137 
143  public static ExternalProcess CreateExternalProcess(string appName)
144  {
145  return CreateExternalProcess(appName, null);
146  }
147 
148  public static ExternalProcess CreateExternalProcess(string appName, string args)
149  {
150  // Seems like it would be safer and more informative to call this static method and test for null after.
151  try
152  {
153  return new ExternalProcess(appName, args);
154  }
155  catch (Exception ex)
156  {
157  UnityEngine.Debug.LogError("Unable to start process " + appName + ", " + ex.Message + ".");
158  }
159  return null;
160  }
161 
162  private ExternalProcess(string appName, string args)
163  {
164  appName = appName.Replace("/", @"\");
165  string appPath = appName;
166  if (!File.Exists(appPath))
167  {
168  appPath = FindPathToExecutable(appName);
169  }
170 
171  if (appPath == null)
172  {
173  throw new ArgumentException("Unable to find app " + appPath);
174  }
175 
176  // This may throw, calling code should catch the exception.
177  string launchString = args == null ? appPath : appPath + " " + args;
178  mHandle = ExternalProcessAPI_CreateProcess(launchString);
179  }
180 
181  ~ExternalProcess()
182  {
183  Dispose(false);
184  }
185 
186  public bool IsRunning()
187  {
188  try
189  {
190  if (mHandle != IntPtr.Zero)
191  {
192  return ExternalProcessAPI_IsRunning(mHandle);
193  }
194  }
195  catch
196  {
197  Terminate();
198  }
199 
200  return false;
201  }
202 
203  public bool WaitForStart(float seconds)
204  {
205  return WaitFor(seconds, () => { return ExternalProcessAPI_IsRunning(mHandle); });
206  }
207 
208  public bool WaitForShutdown(float seconds)
209  {
210  return WaitFor(seconds, () => { return !ExternalProcessAPI_IsRunning(mHandle); });
211  }
212 
213  public bool WaitFor(float seconds, Func<bool> func)
214  {
215  if (seconds <= 0.0f)
216  seconds = 5.0f;
217  float end = Time.realtimeSinceStartup + seconds;
218 
219  bool hasHappened = false;
220  while (Time.realtimeSinceStartup < end)
221  {
222  hasHappened = func();
223  if (hasHappened)
224  {
225  break;
226  }
227  Thread.Sleep(Math.Min(500, (int)(seconds * 1000)));
228  }
229  return hasHappened;
230  }
231 
232  public void SendLine(string line)
233  {
234  try
235  {
236  if (mHandle != IntPtr.Zero)
237  {
238  ExternalProcessAPI_SendLine(mHandle, line);
239  }
240  }
241  catch
242  {
243  Terminate();
244  }
245  }
246 
247  public string GetLine()
248  {
249  try
250  {
251  if (mHandle != IntPtr.Zero)
252  {
253  return Marshal.PtrToStringAnsi(ExternalProcessAPI_GetLine(mHandle));
254  }
255  }
256  catch
257  {
258  Terminate();
259  }
260 
261  return null;
262  }
263 
264  public void Terminate()
265  {
266  try
267  {
268  if (mHandle != IntPtr.Zero)
269  {
270  ExternalProcessAPI_DestroyProcess(mHandle);
271  }
272  }
273  catch
274  {
275  // TODO: Should we be catching something here?
276  }
277 
278  mHandle = IntPtr.Zero;
279  }
280 
281  // IDisposable
282 
283  public void Dispose()
284  {
285  Dispose(true);
286  GC.SuppressFinalize(this);
287  }
288 
289  protected virtual void Dispose(bool disposing)
290  {
291  Terminate();
292  }
293  }
294 }
Helper class for launching external processes inside of the unity editor.
static bool FindAndLaunch(string appName, string args)
static ExternalProcess CreateExternalProcess(string appName)
The actual ExternalProcess class.
static bool FindAndLaunch(string appName)
static string FindPathToExecutable(string appName)
static string MakeRelativePath(string path1, string path2)
bool WaitFor(float seconds, Func< bool > func)
virtual void Dispose(bool disposing)
static ExternalProcess CreateExternalProcess(string appName, string args)
static void Launch(string appName)