Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

[Solved] IL2CPP and Process.Start

Discussion in 'Scripting' started by sebas77, May 31, 2018.

  1. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    Is

    Process.Start

    supposed to work with IL2CPP windows standalone 64bit? Using it right now crashes the application hard.

    upload_2018-5-31_16-39-38.png

    Solutions or alternatives?
     
    moo1210, _TheFuture_ and JLopezTeleco like this.
  2. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    We've not implement the .NET Process class in IL2CPP yet. Is was not needed for mobiles and consoles in most cases. It is on our list of things to implement though.
     
    _TheFuture_ likes this.
  3. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    Oh no that's a showstopper for us! So I don't have any alternative?
     
  4. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    am I forced to write something in c++ maybe?
     
  5. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    Unfortunately no, not from C#. You would need to p/invoke into a native library to start the process.

    If you can discuss it, I'm curious to know what your game or project is using the Process API to do.
     
  6. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    Our game is server, client based. The Single player mode still runs a local server in background, it's how it has been designed. When the client starts, if the single player mode is selected, the client starts a local server in background.
     
    _TheFuture_ likes this.
  7. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    That makes sense, thanks! We'll look into implementing this.
     
  8. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    Done, it wasn't too hard, but it was annoying because the main applications also manages the life of the spawned process.

    Of course this is going to work only on windows, but for the time being is all right for us.
     
  9. BBO_Lagoon

    BBO_Lagoon

    Joined:
    Mar 2, 2017
    Posts:
    191
    Hi, Is there any alternative for launching process with IL2CPP windows ?
     
  10. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    At the moment, the only alternative is to p/invoke into a native library which accesses the Windows C API directly.
     
  11. fsfs0455

    fsfs0455

    Joined:
    Jan 24, 2015
    Posts:
    1
    I tried to use p/invoke in 2018.2.7f1, but still not work. QQ截图20180922182459.png QQ图片20180922182710.png
     
  12. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    Getting p/invoke signatures correct can be difficult, especially for functions like CreateProcess. First, I would p/invoke into GetLastError to find the specific error message.

    Another option is to write your own native library that calls CreateProcess. It might have a simpler API, so the p/invoke signature will be simpler.
     
  13. June1111

    June1111

    Joined:
    Aug 11, 2017
    Posts:
    33
    Whats the eta on the process class getting supported by il2cpp?
     
  14. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    We don't have an ETA right now.
     
  15. June1111

    June1111

    Joined:
    Aug 11, 2017
    Posts:
    33
    Alright no worries, did it in c with no problems deploying. Have a good day.
     
  16. Sheiyla

    Sheiyla

    Joined:
    Jul 19, 2015
    Posts:
    1
    Hello, do you have news about are integration because me too I need it ?
     
  17. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    No, we don't have any update about completing this work.
     
  18. SKNKanimation

    SKNKanimation

    Joined:
    Feb 24, 2017
    Posts:
    12
    Getting Process.Start to work seems less overwhelming than getting all of System.Diagnostics to work in Il2cpp. I'm willing to pay for a plugin that does that sort of thing, given that it's been almost a year where this is a planned-but-not-implemented feature. Somebody help, please ^_^
     
  19. June1111

    June1111

    Joined:
    Aug 11, 2017
    Posts:
    33
    _TheFuture_, Mattstg, Sam777 and 2 others like this.
  20. crawler

    crawler

    Joined:
    Aug 9, 2012
    Posts:
    27
    Hey Josh4364, thanks for your solution! :cool:
     
    June1111 likes this.
  21. TheSheyper

    TheSheyper

    Joined:
    Jul 3, 2013
    Posts:
    21
    Hi !
    I found a simple workaround for a simple case like opening another exe file :

    Code (CSharp):
    1. Application.OpenURL(localPath);
    2.  
    3. Application.Quit();
    instead of :

    Code (CSharp):
    1. Process setup = new Process();
    2. setup.StartInfo.FileName = localPath;
    3. setup.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
    4. setup.Start();
    5.  
    6. Application.Quit();
    :)
     
    Mattstg, RobbyZ, Sam777 and 2 others like this.
  22. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,630
    hmmm I am even surprised this works, are you saying that instead of saving the exe, it actually opens it and runs it?
     
  23. kenfalco

    kenfalco

    Joined:
    Jul 18, 2012
    Posts:
    27
    Any solutions for MacOS? OpenUrl not work. I have an app installer made with Unity
     
  24. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    1,084
    Doesn't work with special characters ex. Chinese, Korean etc.

    My workaround so far is to build separate external NET Framework Console Application open it and send args using C/C++ library in Unity game.

    So ye, I ship my project with:
    - Unity game with il2cpp with C/C++ library
    - NET Framework Console Application

    That way I can use Process.Start to for ex. open .txt file in user default editor - this is how "Edit Raw" works in my game for player's made maps.
     
  25. Zorkind

    Zorkind

    Joined:
    May 4, 2015
    Posts:
    55
    Any updates on this? i have a project with the same requirements as the others, MP game with SP mode running shadow server in background :-\

    Since the game is in Alpha, i can live with publishing it as Mono, but i really think it would make a lot of sense to publish it as CPP.

    My luck is that the code that really matters to me is in the server and the server i can push as CPP while the client is in Mono, so i guess i am fine :)
     
    Last edited: May 25, 2020
  26. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    No, we don't have any update on this.
     
  27. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    372
    This would really be helpful in my case, also with a client-server multiplayer game.
    I was trying to use MongoDB C# drivers (DLLs) to connect to my database server, and it was failing in IL2CPP builds.
    Instead, I figured I'd make a separate Visual Studio C# .NET Core project that could do it, and I'd start the .exe for that in a separate process.

    Now, Process.Start() is failing because it's not supported in IL2CPP Unity builds.
    This is a shame, I'd really love some insight on some kind of timeline @JoshPeterson if possible. Anything we can do to help?
     
  28. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    We don't have a timeline for Process support in IL2CPP. I'm not sure if or when it will end up being supported.
     
  29. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
    Please add this, we are using it heavily and are now noticing that it will not work with IL2CPP (please add a compile error...). Do you have a list which stuff is currently not implemented like Process?
     
  30. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
    This works nicely, Thank you!!! Can someone help with adding the following events and methods from the Net version of the Process to the solution provided by June1111?:

    Code (CSharp):
    1. //events we need:
    2. m_process.OutputDataReceived += ProcessOutputDataReceived;
    3. m_process.ErrorDataReceived += ProcessErrorDataReceived;
    4. m_process.Exited += ProcessExited;
    5.  
    6. //methods we need
    7. m_process.HasExited
     
  31. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,603
    Well, you generally do not get any kind of "event" from other applications unless they specifically raise them. What you seem to be looking for is redirecting the standard I/O of the child process. The docs have an example how this can be done. I was never in need of something like that. Note that the example only shows how to redirect the standard input and output streams to pipes. However if you look at the startupinfo struct you can see there's also a seperate handle for the standard error stream which should work the same way as the stdout stream.

    In order to detect if a child process ended you should be able to use "WaitForSingleObject" with the process handle. Of course to emulate everything that the Process class does for you, you most likely need a couple of threads.

    So while dealing with the native WinAPI isn't that complicated, it still takes some efford. That's why C# is more attractive to many programmers since the .NET framework wraps / hides a lot of that nasty low level OS stuff. You can only appreciate the luxury once it's gone :)
     
    Last edited: Jul 12, 2020
  32. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,603
    Note that since Mono is essentially open source you can look up the Process class source code yourself and maybe extract the required parts. Not sure which parts actually do not work with IL2CPP. Though I would try to keep the things as simple as possible. The issue might be the "AsyncStreamReader" used but that's just a wild guess.
     
  33. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    Unfortunately we don't have a list of unimplemented parts of the .NET Framework base class libraries documented. The biggest ones are probably Process, Configuration Manager, and System.Reflection.Emit.
     
  34. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
    Thank you all for your feedback! :)
     
  35. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    1,084
  36. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,153
    How did you get it to work? I keep running into a win32exception error. Any insight into what you did would be appreciated.
     
  37. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
    Can you give some hints what exactly happens, e.g. where do you get the exception (function call etc.)?
     
  38. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,153
    Ah...hmm...after a night of sleep while I was typing up my reply and looking at my code, I decided to give something else a try. I was getting an access denied error from the editor, but I was trying to open a Unity project in another folder. When I moved it into the same folder, I was able to get it to launch. That should still work for what we're needing. Thanks for the help.

    The standalone test was throwing a mono-io-layer-error (5) which I couldn't find a reference to.
     
  39. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
  40. TheDiscoRainbow

    TheDiscoRainbow

    Joined:
    Jan 20, 2016
    Posts:
    2
    I tried June1111's code but had a permission error (it might be fixed by moving the executable to Application.dataPath, but that's not convenient in my case. The Application.OpenURL() method helped but didn't allow me to pass input parameters. In the end, I wrote a wrapper method to call Process.Start() from an external Dot Net Framework console app:

    Unity:

    string analysisCommand = "";

    if (Application.isEditor)
    {
    System.Diagnostics.Process.Start("CMD.exe", analysisCommand); // doesn't work in builds
    }
    else // workaround - writing command arguments to file and calling OpenURL on a wrapper function
    {
    string commandTxtPath = Path.GetFullPath(Path.Combine(@Application.dataPath, @"..\..\..\external_dependencies\StartExternalProcess\bin\Debug\command.txt"));
    string startExternalProcessPath = Path.GetFullPath(Path.Combine(@Application.dataPath, @"..\..\..\external_dependencies\StartExternalProcess\bin\Debug\StartExternalProcess.exe"));

    Debug.LogFormat("Application.dataPath:{0}", Application.dataPath);
    Debug.LogFormat("commandTxtPath:{0}", commandTxtPath);
    Debug.LogFormat("startExternalProcessPath:{0}", startExternalProcessPath);

    SaveDataNew.WriteTextFile(commandTxtPath, analysisCommand);
    Application.OpenURL(startExternalProcessPath);
    }

    Console app:

    using System;
    using System.Diagnostics;
    using System.IO;

    namespace StartExternalProcess
    {
    class Program
    {
    static void Main(string[] args)
    {
    string currentDirectory = Directory.GetCurrentDirectory();
    string entryAssembleyLocation = System.Reflection.Assembly.GetEntryAssembly().Location.Replace(@"\StartExternalProcess.exe", "");

    Console.WriteLine("currentDirectory:{0}", currentDirectory);
    Console.WriteLine("entryAssembleyLocation:{0}", entryAssembleyLocation);

    if (currentDirectory != entryAssembleyLocation)
    {
    Console.WriteLine("set current directory to entryAssembleyLocation");
    Directory.SetCurrentDirectory(entryAssembleyLocation);
    }
    else
    {
    Console.WriteLine("current path is already at the entryAssembleyLocation");
    }

    string[] lines = File.ReadAllLines(@"command.txt");

    string command = lines[0];
    Console.WriteLine(command);
    Process.Start("CMD.exe", command);
    Console.ReadLine();
    }
    }
    }









     
  41. OneManArmy3D

    OneManArmy3D

    Joined:
    Jun 2, 2011
    Posts:
    189
    any update on this 3 years later?
     
    pym2020 and ModLunar like this.
  42. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    No - I would expect that we will not implement this. It is possible from a technical perspective, but it is not high on the list of priorities for our team.
     
  43. spvn

    spvn

    Joined:
    Dec 10, 2013
    Posts:
    80
    Can anybody get this to work? I keep getting a Win32Exception:

    Win32Exception: %1 is not a valid Win32 application.
     
  44. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    179
    This works for us, enjoy!

    Based on https://stackoverflow.com/questions/1591342/c-how-to-determine-if-a-windows-process-is-running

    Code (CSharp):
    1. #if UNITY_STANDALONE_WIN
    2. // created: 2020-07-13
    3. using Microsoft.Win32.SafeHandles;
    4. using System;
    5. using System.Runtime.ConstrainedExecution;
    6. using System.Runtime.InteropServices;
    7. using System.Security;
    8. using System.Security.Permissions;
    9.  
    10. namespace Virtofy.IO
    11. {
    12.     /// <summary>
    13.     /// Helper system for process (to allow working with IL2CPP generated code)
    14.     /// </summary>
    15.     public static class ProcessHelper
    16.     {
    17.         #region Variables
    18.  
    19.         [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    20.         [return: MarshalAs(UnmanagedType.Bool)]
    21.         private static extern bool CreateProcessW(
    22.             string lpApplicationName,
    23.             [In] string lpCommandLine,
    24.             IntPtr procSecAttrs,
    25.             IntPtr threadSecAttrs,
    26.             bool bInheritHandles,
    27.             ProcessCreationFlags dwCreationFlags,
    28.             IntPtr lpEnvironment,
    29.             string lpCurrentDirectory,
    30.             ref STARTUPINFO lpStartupInfo,
    31.             ref PROCESS_INFORMATION lpProcessInformation
    32.         );
    33.  
    34.         [DllImport("kernel32.dll", SetLastError = true)]
    35.         [return: MarshalAs(UnmanagedType.Bool)]
    36.         private static extern bool CloseHandle(IntPtr hObject);
    37.  
    38.         [DllImport("kernel32.dll", SetLastError = true)]
    39.         [return: MarshalAs(UnmanagedType.Bool)]
    40.         private static extern bool TerminateProcess(IntPtr processHandle, uint exitCode);
    41.  
    42.         [DllImport("kernel32.dll", SetLastError = true)]
    43.         private static extern IntPtr OpenProcess(ProcessAccessRights access, bool inherit, uint processId);
    44.  
    45.         [DllImport("kernel32.dll", SetLastError = true)]
    46.         [return: MarshalAs(UnmanagedType.Bool)]
    47.         private static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
    48.  
    49.         [DllImport("kernel32.dll", SetLastError = true)]
    50.         static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
    51.  
    52.         [DllImport("kernel32.dll")]
    53.         private static extern uint GetCurrentProcessId();
    54.  
    55.         [Flags]
    56.         private enum ProcessAccessRights : uint
    57.         {
    58.             PROCESS_CREATE_PROCESS = 0x0080, //  Required to create a process.
    59.             PROCESS_CREATE_THREAD = 0x0002, //  Required to create a thread.
    60.             PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
    61.             PROCESS_QUERY_INFORMATION = 0x0400, //  Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
    62.             PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, //  Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000:  This access right is not supported.
    63.             PROCESS_SET_INFORMATION = 0x0200, //    Required to set certain information about a process, such as its priority class (see SetPriorityClass).
    64.             PROCESS_SET_QUOTA = 0x0100, //  Required to set memory limits using SetProcessWorkingSetSize.
    65.             PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
    66.             PROCESS_TERMINATE = 0x0001, //  Required to terminate a process using TerminateProcess.
    67.             PROCESS_VM_OPERATION = 0x0008, //   Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
    68.             PROCESS_VM_READ = 0x0010, //    Required to read memory in a process using ReadProcessMemory.
    69.             PROCESS_VM_WRITE = 0x0020, //   Required to write to memory in a process using WriteProcessMemory.
    70.             DELETE = 0x00010000, // Required to delete the object.
    71.             READ_CONTROL = 0x00020000, //   Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
    72.             SYNCHRONIZE = 0x00100000, //    The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
    73.             WRITE_DAC = 0x00040000, //  Required to modify the DACL in the security descriptor for the object.
    74.             WRITE_OWNER = 0x00080000, //    Required to change the owner in the security descriptor for the object.
    75.             STANDARD_RIGHTS_REQUIRED = 0x000f0000,
    76.             PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF //    All possible access rights for a process object.
    77.         }
    78.  
    79.         [StructLayout(LayoutKind.Sequential)]
    80.         private struct PROCESS_INFORMATION
    81.         {
    82.             internal IntPtr hProcess;
    83.             internal IntPtr hThread;
    84.             internal uint dwProcessId;
    85.             internal uint dwThreadId;
    86.         }
    87.  
    88.         [StructLayout(LayoutKind.Sequential)]
    89.         private struct STARTUPINFO
    90.         {
    91.             internal uint cb;
    92.             internal IntPtr lpReserved;
    93.             internal IntPtr lpDesktop;
    94.             internal IntPtr lpTitle;
    95.             internal uint dwX;
    96.             internal uint dwY;
    97.             internal uint dwXSize;
    98.             internal uint dwYSize;
    99.             internal uint dwXCountChars;
    100.             internal uint dwYCountChars;
    101.             internal uint dwFillAttribute;
    102.             internal uint dwFlags;
    103.             internal ushort wShowWindow;
    104.             internal ushort cbReserved2;
    105.             internal IntPtr lpReserved2;
    106.             internal IntPtr hStdInput;
    107.             internal IntPtr hStdOutput;
    108.             internal IntPtr hStdError;
    109.         }
    110.  
    111.         [Flags]
    112.         private enum ProcessCreationFlags : uint
    113.         {
    114.             NONE = 0,
    115.             CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
    116.             CREATE_DEFAULT_ERROR_MODE = 0x04000000,
    117.             CREATE_NEW_CONSOLE = 0x00000010,
    118.             CREATE_NEW_PROCESS_GROUP = 0x00000200,
    119.             CREATE_NO_WINDOW = 0x08000000,
    120.             CREATE_PROTECTED_PROCESS = 0x00040000,
    121.             CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
    122.             CREATE_SECURE_PROCESS = 0x00400000,
    123.             CREATE_SEPARATE_WOW_VDM = 0x00000800,
    124.             CREATE_SHARED_WOW_VDM = 0x00001000,
    125.             CREATE_SUSPENDED = 0x00000004,
    126.             CREATE_UNICODE_ENVIRONMENT = 0x00000400,
    127.             DEBUG_ONLY_THIS_PROCESS = 0x00000002,
    128.             DEBUG_PROCESS = 0x00000001,
    129.             DETACHED_PROCESS = 0x00000008,
    130.             EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
    131.             INHERIT_PARENT_AFFINITY = 0x00010000
    132.         }
    133.  
    134.         private const UInt32 INFINITE = 0xFFFFFFFF;
    135.         private const UInt32 WAIT_ABANDONED = 0x00000080;
    136.         private const UInt32 WAIT_OBJECT_0 = 0x00000000;
    137.         private const UInt32 WAIT_TIMEOUT = 0x00000102;
    138.  
    139.         private const int ERROR_NO_MORE_FILES = 0x12;
    140.         [DllImport("kernel32.dll", SetLastError = true)]
    141.         private static extern SafeSnapshotHandle CreateToolhelp32Snapshot(SnapshotFlags flags, uint id);
    142.         [DllImport("kernel32.dll", SetLastError = true)]
    143.         private static extern bool Process32First(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
    144.         [DllImport("kernel32.dll", SetLastError = true)]
    145.         private static extern bool Process32Next(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
    146.  
    147.         [Flags]
    148.         private enum SnapshotFlags : uint
    149.         {
    150.             HeapList = 0x00000001,
    151.             Process = 0x00000002,
    152.             Thread = 0x00000004,
    153.             Module = 0x00000008,
    154.             Module32 = 0x00000010,
    155.             All = (HeapList | Process | Thread | Module),
    156.             Inherit = 0x80000000,
    157.             NoHeaps = 0x40000000
    158.         }
    159.         [StructLayout(LayoutKind.Sequential)]
    160.         private struct PROCESSENTRY32
    161.         {
    162.             public uint dwSize;
    163.             public uint cntUsage;
    164.             public uint th32ProcessID;
    165.             public IntPtr th32DefaultHeapID;
    166.             public uint th32ModuleID;
    167.             public uint cntThreads;
    168.             public uint th32ParentProcessID;
    169.             public int pcPriClassBase;
    170.             public uint dwFlags;
    171.             [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
    172.         };
    173.  
    174.         [SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
    175.         internal sealed class SafeSnapshotHandle : SafeHandleMinusOneIsInvalid
    176.         {
    177.             internal SafeSnapshotHandle() : base(true)
    178.             {
    179.             }
    180.  
    181.             [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    182.             internal SafeSnapshotHandle(IntPtr handle) : base(true)
    183.             {
    184.                 base.SetHandle(handle);
    185.             }
    186.  
    187.             protected override bool ReleaseHandle()
    188.             {
    189.                 return CloseHandle(base.handle);
    190.             }
    191.  
    192.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    193.             private static extern bool CloseHandle(IntPtr handle);
    194.         }
    195.  
    196.         #endregion
    197.  
    198.         #region Core
    199.         /// <summary>
    200.         /// Starts the given file
    201.         /// </summary>
    202.         /// <param name="path"></param>
    203.         /// <param name="arguments"></param>
    204.         /// <param name="dir"></param>
    205.         /// <param name="hidden"></param>
    206.         /// <param name="processID"></param>
    207.         /// <returns></returns>
    208.         public static bool Start(string path,
    209.             string arguments,
    210.             string dir,
    211.             bool hidden,
    212.             out uint processID)
    213.         {
    214.             processID = 0;
    215.             ProcessCreationFlags flags = hidden ? ProcessCreationFlags.CREATE_NO_WINDOW : ProcessCreationFlags.NONE;
    216.             STARTUPINFO startupinfo = new STARTUPINFO {
    217.                 cb = (uint)Marshal.SizeOf<STARTUPINFO>()
    218.             };
    219.             PROCESS_INFORMATION processinfo = new PROCESS_INFORMATION();
    220.             if (!CreateProcessW(null,
    221.                 path + " " + arguments,
    222.                 IntPtr.Zero,
    223.                 IntPtr.Zero,
    224.                 false,
    225.                 flags,
    226.                 IntPtr.Zero,
    227.                 dir,// + " " + arguments,
    228.                 ref startupinfo,
    229.                 ref processinfo))
    230.             {
    231.                 return (false);
    232.             }
    233.             processID = processinfo.dwProcessId;
    234.             //return processinfo.dwProcessId;
    235.             return (true);
    236.         }
    237.  
    238.         private static IntPtr GetProcessHandle(uint processID)
    239.         {
    240.             return (OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, processID));
    241.         }
    242.  
    243.         /// <summary>
    244.         /// Checks if the given process has ended
    245.         /// </summary>
    246.         /// <param name="processID"></param>
    247.         /// <returns></returns>
    248.         public static bool IsProcessEnded(uint processID)
    249.         {
    250.             IntPtr handle = GetProcessHandle(processID);
    251.             if (GetExitCodeProcess(handle, out uint lpExitCode)) {
    252.                 return (lpExitCode != 259);
    253.             } else {
    254.                 return (true);
    255.             }
    256.         }
    257.  
    258.         /// <summary>
    259.         /// Kills the given process
    260.         /// </summary>
    261.         /// <param name="processID"></param>
    262.         /// <returns></returns>
    263.         public static bool KillProcess(uint processID)
    264.         {
    265.             IntPtr handle = GetProcessHandle(processID);
    266.             if (handle == IntPtr.Zero) {
    267.                 return(false);
    268.             }
    269.             if (!TerminateProcess(handle, 0)) {
    270.                 return (false);
    271.             }
    272.             if (!CloseHandle(handle)) {
    273.                 return (false);
    274.             }
    275.             return (true);
    276.         }
    277.  
    278.         /// <summary>
    279.         /// Waits till the given process has exited.
    280.         /// This will stall the main thread, e.g. will freezes the app!
    281.         /// </summary>
    282.         /// <param name="processID"></param>
    283.         public static void WaitForExit(uint processID)
    284.         {
    285.             IntPtr handle = GetProcessHandle(processID);
    286.             if (handle == IntPtr.Zero) {
    287.                 return;
    288.             }
    289.             UInt32 result = WaitForSingleObject(handle, INFINITE);
    290.             //todo: check result...
    291.         }
    292.  
    293.         /// <summary>
    294.         /// Get my current process ID
    295.         /// </summary>
    296.         /// <returns></returns>
    297.         public static int GetCurrentProcessID()
    298.         {
    299.             uint id = GetCurrentProcessId();
    300.             return(Convert.ToInt32(id));
    301.         }
    302.  
    303.         /// <summary>
    304.         /// Get the parent process ID
    305.         /// </summary>
    306.         /// <param name="Id"></param>
    307.         /// <returns></returns>
    308.         public static int GetParentProcessID(int Id)
    309.         {
    310.             PROCESSENTRY32 pe32 = new PROCESSENTRY32 { };
    311.             pe32.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
    312.             SafeSnapshotHandle hSnapshot = CreateToolhelp32Snapshot(SnapshotFlags.Process, (uint)Id);
    313.  
    314.             if (hSnapshot.IsInvalid) {
    315.                 return (-1);
    316.             }
    317.  
    318.             if (!Process32First(hSnapshot, ref pe32)) {
    319.                 int errno = Marshal.GetLastWin32Error();
    320.                 if (errno == ERROR_NO_MORE_FILES) {
    321.                     return -1;
    322.                 }
    323.                 return (-1);
    324.             }
    325.             do {
    326.                 if (pe32.th32ProcessID == (uint)Id)
    327.                     return (int)pe32.th32ParentProcessID;
    328.             } while (Process32Next(hSnapshot, ref pe32));
    329.        
    330.             return (-1);
    331.         }
    332.  
    333.         #endregion
    334.  
    335.         #region Mutex
    336.  
    337.         /*
    338.         [DllImport("kernel32.dll")]
    339.         private static extern IntPtr CreateMutex(IntPtr lpMutexAttributes, bool bInitialOwner, string lpName);
    340.         [DllImport("kernel32.dll")]
    341.         private static extern bool ReleaseMutex(IntPtr hMutex);
    342.  
    343.         public static IntPtr CreateMutex(bool initialOwner,
    344.             string name)
    345.         {
    346.             // create IntPtrs for use with CreateMutex()
    347.             IntPtr ipMutexAttr = new IntPtr(0);
    348.             IntPtr ipHMutex = new IntPtr(0);
    349.             ipHMutex = CreateMutex(ipMutexAttr, initialOwner, name);
    350.             if (ipHMutex != IntPtr.Zero) {
    351.                 int iGLE = Marshal.GetLastWin32Error();
    352.                 if (iGLE == 183) {// Win32Calls.ERROR_ALREADY_EXISTS)
    353.                     //allready exists
    354.                 }
    355.             }
    356.             return (ipHMutex);
    357.         }
    358.         */
    359.         /*
    360.         public static bool ReleaseMutex(IntPtr intPtr)
    361.         {
    362.             if (intPtr != IntPtr.Zero) {
    363.                 return (ReleaseMutex(intPtr));
    364.             } else {
    365.                 return (false);
    366.             }
    367.         }
    368.         */
    369.  
    370.         #endregion
    371.     }
    372. }
    373. #endif
    374.  
     
    Last edited: Sep 20, 2021
  45. AlfEspinosaN

    AlfEspinosaN

    Joined:
    Jan 14, 2019
    Posts:
    9
    Hello! Hey, so, this is only intended for opening exe files on a local path? For opening internet urls I'm still having issues :/
     
    x1alphaz1 likes this.
  46. PelleSimon

    PelleSimon

    Joined:
    Jul 20, 2021
    Posts:
    2
    Any updates on this? For my team this is becoming more and more of an issue.
     
    ModLunar and ZO5KmUG6R like this.
  47. PieterAlbers

    PieterAlbers

    Joined:
    Dec 2, 2014
    Posts:
    226
    Would love to see this implemented as well
     
  48. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    372
    This doesn't help people who really need C# System.Process for their use cases, but in my case, I needed to run a separate C# command line program (.NET 5, using Nuget packages for MongoDB that Unity wouldn't play nice with).

    Since it's for my backend game server(s), I decided (and was able) to refactor the command line calls into HTTP requests/responses, which are faring way better for me than fighting with Unity (as we all know! Haha).

    It means I no longer need to walk on ice when setting up my AWS EC2 instance in the cloud that runs my Unity build (game server) and this .NET 5 command line program, and any other separate command line programs I needed to run. (For example, the programs might need different .NET versions and clash, and are more complex to setup on 1 machine/environment).

    Instead, using HTTP requests, I have new flexibility in where I can run these little microservices/endpoints -- same machine, different machines/EC2 instances, or even better -- in a containerized environment such as Docker or Docker Compose to run the entire stack of applications at once.

    I hope this helps some people out there :).
     
  49. Erveon

    Erveon

    Joined:
    Sep 15, 2019
    Posts:
    13
    Any change on this stance? It's important for us at Oddshot Games and has been a major headache in moving production products to IL2CPP.
     
  50. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,801
    No, we don't have plans to implement this soon. We simple don't have the resources to work on it at the moment, sorry.