From 7de50b2e252f92e53bc95be39b2ccbf0d8de2aae Mon Sep 17 00:00:00 2001 From: Emiliana <38952746+emilianavt@users.noreply.github.com> Date: Mon, 17 Feb 2020 20:16:06 +0100 Subject: [PATCH] When using OpenSeeLauncher with pinvoke, it will now log process output to log files. --- Unity/OpenSeeLauncher.cs | 38 +++--- Unity/OpenSeeProcessInterface.cs | 201 ++++++++++++++++++++++++++++++- 2 files changed, 217 insertions(+), 22 deletions(-) diff --git a/Unity/OpenSeeLauncher.cs b/Unity/OpenSeeLauncher.cs index 8f11f8b..8100a56 100644 --- a/Unity/OpenSeeLauncher.cs +++ b/Unity/OpenSeeLauncher.cs @@ -45,10 +45,14 @@ public class OpenSeeLauncher : MonoBehaviour { public string modelPath = "models\\"; [Tooltip("Additional options that should be passed to the face tracker.")] public List commandlineOptions = new List(new string[] { "--silent", "1", "--max-threads", "4" }); - [Tooltip("IL2CPP doesn't support Proocess.Start. When this is enabled, OpenSeeLauncher will create the tracking process by calling the necessary API functions directly through the DLLs. In this case, reading the standard output from the process will not be supported. It will also retrieve the camera list directly through the escapi DLLs, so make sure it is part of your Unity project.")] + [Tooltip("IL2CPP doesn't support Proocess.Start. When this is enabled, OpenSeeLauncher will create the tracking process by calling the necessary API functions directly through the DLLs. In this case, reading the standard output from the process will not be supported and it will instead be send to the logfiles set in pinvokeStdOut and pinvokeStdErr in the persistent data directory. It will also retrieve the camera list directly through the escapi DLLs, so make sure it is part of your Unity project.")] public bool usePinvoke = false; [Tooltip("When this is enabled, even if usePinvoke is disabled, the camera list will be retrieved through escapi DLLs directly, which can be faster. Make sure the DLLs are in your Unity project.")] public bool usePinvokeListCameras = false; + [Tooltip("This is the standard output log file's name in the persistent data directory.")] + public string pinvokeStdOut = "output.txt"; + [Tooltip("This is the standard error log file's name in the persistent data directory.")] + public string pinvokeStdErr = "error.txt"; [Header("Runtime information")] [Tooltip("This field shows if the tracker is currently alive.")] public bool trackerAlive = false; @@ -58,6 +62,8 @@ public class OpenSeeLauncher : MonoBehaviour { private StringBuilder trackerSB = null; private bool dontPrintNow = false; private System.IntPtr processHandle = System.IntPtr.Zero; + private System.IntPtr processStdOut = System.IntPtr.Zero; + private System.IntPtr processStdErr = System.IntPtr.Zero; private Job job = null; private bool CheckSetup(bool requireTarget) { @@ -216,23 +222,7 @@ public class OpenSeeLauncher : MonoBehaviour { } string argumentString = EscapeArguments(arguments.ToArray()); - if (processHandle != System.IntPtr.Zero) { - if (OpenSeeProcessInterface.Alive(processHandle)) - OpenSeeProcessInterface.TerminateProcess(processHandle, 0); - OpenSeeProcessInterface.CloseHandle(processHandle); - processHandle = System.IntPtr.Zero; - } - - if (trackerProcess != null && !trackerProcess.HasExited) { - trackerProcess.CloseMainWindow(); - trackerProcess.Close(); - if (!dontPrintNow) { - try { - trackerProcess.CancelOutputRead(); - trackerProcess.CancelErrorRead(); - } catch {} - } - } + StopTracker(); if (!usePinvoke) { ProcessStartInfo processStartInfo; @@ -270,7 +260,9 @@ public class OpenSeeLauncher : MonoBehaviour { return !trackerProcess.HasExited; } else { string dir = Path.GetDirectoryName(exePath); - OpenSeeProcessInterface.Start(exePath, "facetracker " + argumentString, dir, true, out processHandle); + string outputLog = Application.persistentDataPath + "/" + pinvokeStdOut; + string errorLog = Application.persistentDataPath + "/" + pinvokeStdErr; + OpenSeeProcessInterface.Start(exePath, "facetracker " + argumentString, dir, true, outputLog, errorLog, out processHandle, out processStdOut, out processStdErr); if (processHandle != System.IntPtr.Zero) { job.AddProcess(processHandle); return OpenSeeProcessInterface.Alive(processHandle); @@ -286,6 +278,14 @@ public class OpenSeeLauncher : MonoBehaviour { OpenSeeProcessInterface.CloseHandle(processHandle); processHandle = System.IntPtr.Zero; } + if (processStdOut != System.IntPtr.Zero) { + OpenSeeProcessInterface.CloseHandle(processStdOut); + processStdOut = System.IntPtr.Zero; + } + if (processStdErr != System.IntPtr.Zero) { + OpenSeeProcessInterface.CloseHandle(processStdErr); + processStdErr = System.IntPtr.Zero; + } if (trackerProcess != null && !trackerProcess.HasExited) { trackerProcess.CloseMainWindow(); trackerProcess.Close(); diff --git a/Unity/OpenSeeProcessInterface.cs b/Unity/OpenSeeProcessInterface.cs index 2027b12..7964971 100644 --- a/Unity/OpenSeeProcessInterface.cs +++ b/Unity/OpenSeeProcessInterface.cs @@ -9,6 +9,185 @@ namespace OpenSee { public class OpenSeeProcessInterface { #region DllImport + [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] + private static extern IntPtr CreateFile( + string lpFileName, + EFileAccess dwDesiredAccess, + EFileShare dwShareMode, + SECURITY_ATTRIBUTES lpSecurityAttributes, + ECreationDisposition dwCreationDisposition, + EFileAttributes dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [StructLayout(LayoutKind.Sequential)] + private struct SECURITY_ATTRIBUTES + { + public int nLength; + public IntPtr lpSecurityDescriptor; + public int bInheritHandle; + } + + [Flags] + enum EFileAccess : uint { + // + // Standart Section + // + + AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type + MaximumAllowed = 0x2000000, // MaximumAllowed access type + + Delete = 0x10000, + ReadControl = 0x20000, + WriteDAC = 0x40000, + WriteOwner = 0x80000, + Synchronize = 0x100000, + + StandardRightsRequired = 0xF0000, + StandardRightsRead = ReadControl, + StandardRightsWrite = ReadControl, + StandardRightsExecute = ReadControl, + StandardRightsAll = 0x1F0000, + SpecificRightsAll = 0xFFFF, + + FILE_READ_DATA = 0x0001, // file & pipe + FILE_LIST_DIRECTORY = 0x0001, // directory + FILE_WRITE_DATA = 0x0002, // file & pipe + FILE_ADD_FILE = 0x0002, // directory + FILE_APPEND_DATA = 0x0004, // file + FILE_ADD_SUBDIRECTORY = 0x0004, // directory + FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe + FILE_READ_EA = 0x0008, // file & directory + FILE_WRITE_EA = 0x0010, // file & directory + FILE_EXECUTE = 0x0020, // file + FILE_TRAVERSE = 0x0020, // directory + FILE_DELETE_CHILD = 0x0040, // directory + FILE_READ_ATTRIBUTES = 0x0080, // all + FILE_WRITE_ATTRIBUTES = 0x0100, // all + + // + // Generic Section + // + + GenericRead = 0x80000000, + GenericWrite = 0x40000000, + GenericExecute = 0x20000000, + GenericAll = 0x10000000, + + SPECIFIC_RIGHTS_ALL = 0x00FFFF, + FILE_ALL_ACCESS = + StandardRightsRequired | + Synchronize | + 0x1FF, + + FILE_GENERIC_READ = + StandardRightsRead | + FILE_READ_DATA | + FILE_READ_ATTRIBUTES | + FILE_READ_EA | + Synchronize, + + FILE_GENERIC_WRITE = + StandardRightsWrite | + FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES | + FILE_WRITE_EA | + FILE_APPEND_DATA | + Synchronize, + + FILE_GENERIC_EXECUTE = + StandardRightsExecute | + FILE_READ_ATTRIBUTES | + FILE_EXECUTE | + Synchronize + } + + [Flags] + public enum EFileShare : uint + { + /// + /// + /// + None = 0x00000000, + /// + /// Enables subsequent open operations on an object to request read access. + /// Otherwise, other processes cannot open the object if they request read access. + /// If this flag is not specified, but the object has been opened for read access, the function fails. + /// + Read = 0x00000001, + /// + /// Enables subsequent open operations on an object to request write access. + /// Otherwise, other processes cannot open the object if they request write access. + /// If this flag is not specified, but the object has been opened for write access, the function fails. + /// + Write = 0x00000002, + /// + /// Enables subsequent open operations on an object to request delete access. + /// Otherwise, other processes cannot open the object if they request delete access. + /// If this flag is not specified, but the object has been opened for delete access, the function fails. + /// + Delete = 0x00000004 + } + + public enum ECreationDisposition : uint + { + /// + /// Creates a new file. The function fails if a specified file exists. + /// + New = 1, + /// + /// Creates a new file, always. + /// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes, + /// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies. + /// + CreateAlways = 2, + /// + /// Opens a file. The function fails if the file does not exist. + /// + OpenExisting = 3, + /// + /// Opens a file, always. + /// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW. + /// + OpenAlways = 4, + /// + /// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist. + /// The calling process must open the file with the GENERIC_WRITE access right. + /// + TruncateExisting = 5 + } + + [Flags] + public enum EFileAttributes : uint + { + Readonly = 0x00000001, + Hidden = 0x00000002, + System = 0x00000004, + Directory = 0x00000010, + Archive = 0x00000020, + Device = 0x00000040, + Normal = 0x00000080, + Temporary = 0x00000100, + SparseFile = 0x00000200, + ReparsePoint = 0x00000400, + Compressed = 0x00000800, + Offline = 0x00001000, + NotContentIndexed= 0x00002000, + Encrypted = 0x00004000, + Write_Through = 0x80000000, + Overlapped = 0x40000000, + NoBuffering = 0x20000000, + RandomAccess = 0x10000000, + SequentialScan = 0x08000000, + DeleteOnClose = 0x04000000, + BackupSemantics = 0x02000000, + PosixSemantics = 0x01000000, + OpenReparsePoint = 0x00200000, + OpenNoRecall = 0x00100000, + FirstPipeInstance= 0x00080000 + } + + [DllImport("kernel32.dll", SetLastError=true)] private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); const UInt32 INFINITE = 0xFFFFFFFF; @@ -122,16 +301,32 @@ public class OpenSeeProcessInterface { } #endregion - public static uint Start(string exePath, string commandline, string dir, bool hidden, out IntPtr handle) + public static uint Start(string exePath, string commandline, string dir, bool hidden, string outputLog, string errorLog, out IntPtr handle, out IntPtr handleStdOut, out IntPtr handleStdErr) { + SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); + sa.nLength = Marshal.SizeOf(); + sa.lpSecurityDescriptor = IntPtr.Zero; + sa.bInheritHandle = 1; + handle = IntPtr.Zero; + handleStdOut = IntPtr.Zero; + handleStdErr = IntPtr.Zero; + if (outputLog != null) + handleStdOut = CreateFile(outputLog, EFileAccess.FILE_WRITE_DATA, EFileShare.Read | EFileShare.Write, sa, ECreationDisposition.CreateAlways, EFileAttributes.Normal, IntPtr.Zero); + if (errorLog != null) + handleStdErr = CreateFile(errorLog, EFileAccess.FILE_WRITE_DATA, EFileShare.Read | EFileShare.Write, sa, ECreationDisposition.CreateAlways, EFileAttributes.Normal, IntPtr.Zero); + ProcessCreationFlags flags = hidden ? ProcessCreationFlags.CREATE_NO_WINDOW : ProcessCreationFlags.NONE; STARTUPINFO startupinfo = new STARTUPINFO { - cb = (uint)Marshal.SizeOf() + cb = (uint)Marshal.SizeOf(), + dwFlags = 0x00000100, + hStdInput = IntPtr.Zero, + hStdOutput = handleStdOut, + hStdError = handleStdErr }; PROCESS_INFORMATION processinfo = new PROCESS_INFORMATION(); - if (!CreateProcessW(exePath, commandline, IntPtr.Zero, IntPtr.Zero, false, flags, IntPtr.Zero, dir, ref startupinfo, ref processinfo)) + if (!CreateProcessW(exePath, commandline, IntPtr.Zero, IntPtr.Zero, true, flags, IntPtr.Zero, dir, ref startupinfo, ref processinfo)) { throw new Win32Exception(); }