Search Unity

UWP command line arguments

Discussion in 'VR' started by MerkSim, Jul 11, 2018.

  1. MerkSim

    MerkSim

    Joined:
    Mar 1, 2018
    Posts:
    12
    Hello,

    I am wondering if there is a way to modify the generated project when building a UWP app to:
    -call it easily through command line
    -pass custom arguments that can be used in the default loaded scene (i.e. set the text of a UI element to be the first argument)

    I 've managed to call my app through the CMD with an Alias, but I cannot parse any extra arguments after that and pass then to Unity. I 'm using Environment.GetCommandLineArgs(); in my scene, but I don't think that is the right way, especially when trying to pass the arguments through the UWP project from CMD.

    Any suggestions?
    Thanks
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    UWP apps don't support "traditional" command line arguments like regular win32 apps do as they don't get invoked directly, but rather "Activated" through special Windows activation APIs.

    In Unity, we "fake" command line arguments by allowing the app to pass them to "AppCallbacks" constructor. What you could do is modify your app to read these arguments from a file before constructing AppCallbacks, and then also write a script that writes them out to that file next to the executable before launching the app. That's how most of our tests internally are setup.
     
  3. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17
    But how can you use the values set via the AppCallbacks? How can the developer access them in a Unity Script?

    Thanks
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    seb_krueger likes this.
  5. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17
  6. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17
    Though, might there be an issue with quoted strings in the arguments string? Something like this arrives with backslashes in front of the double quotes in the Environemen.GetCommandLineArgs:

    Code (CSharp):
    1. Platform::String^ cmdLineArgumentents = LR"(-File "E:\projects\Unity\deploy\build\bin\x64\Debug\AppX\Data\file.txt")";
     
  7. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    I don't think there are any issues like that with it. AppCallbacks takes an array of arguments, and Environment.GetCommandLineArgs gives you back exactly what you passed to AppCallbacks constructor. It never procecsses or converts them to a single command line.
     
    seb_krueger likes this.
  8. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17
    I tested it with a single argument string:

    Code (CSharp):
    1. Platform::String^ cmdLineArgumentents = LR"(-File "E:\projects\Unity\deploy\build\bin\x64\Debug\AppX\Data\file.txt")";
    Environment.GetCommandLineArgs contained this:

    Code (CSharp):
    1. -File \"E:\projects\Unity\deploy\build\bin\x64\Debug\AppX\Data\file.txt\"
    And I wonder wether the leading back slashes in front of the double quotes are correct and if not, what the reason is, that we see it here.
     
  9. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    How did you pass the arguments to AppCallbacks constructor?
     
  10. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17
  11. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    It works for me:

    Code (csharp):
    1. m_AppCallbacks = ref new AppCallbacks(ref new Platform::Array<Platform::String^>
    2. {
    3.     L"-File",
    4.     LR"(F:\projects\UnityProjects\New Unity Project (157)\bin\build\bin\x64\Debug\AppX\Data\file.txt)"
    5. });
    The resulting Environment.GetCommandLineArgs contained an array of 2 elements and I was able to read the file successfully using the path from the second argument.
     
    Agredek and seb_krueger like this.
  12. seb_krueger

    seb_krueger

    Joined:
    Jan 4, 2019
    Posts:
    17

    I'll try that! Thanks a lot!
     
  13. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    So, I must be missing something.
    • Isn't the App.cpp file auto generated and therefore going to get overwritten when the player is built from within the Editor? If I customize it, aren't I going to lose those changes.
    • This looks like the arguments are hard coded into the AppCallbacks constructor and can't be changed at run time anyway. So, the solution here is to hard code a path to a file and read the arguments from there?
      • Why would I use this approach over just processing a setup file in a monobehaviour? (Assuming I'm not using any default player parameters that are processed by the unity engine.)
    Mike
     
    Last edited: Oct 9, 2019
  14. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Unity doesn't overwrite your custom changes of App.cpp, the manifest file or the project files. So if you make changes to it, commit it to your source control and always build the project from Unity on top of it, you should be fine.

    Generally, command line args are only useful at development time because you can't set them when launching UWP app from the start menu. There's no reason to pass arguments to AppCallbacks constructor if you can control how they are consumed. They are only useful when you want to change engine behaviour or use a 3rd party library that uses Environment.GetCommandLineArgs().
     
  15. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    Thank you. I agree with your statements in part. I'm working on a general purpose application that can be configured at runtime to plot user specific data in 3D. I use command line parameters to...
    1. Load a configuration file (specified on the command line) that contains JSON settings to data sources and plot configurations.
    2. Force the engine to use a particular vrmode (or none) from the standard engine cmd line parameters.
    I'm not very familiar with the UWP development, so if you have suggestion, I'd appreciate them.

    My best ideas are
    1. Create a new file type for my plot configuration files and make a file association so that the app will launch automatically. Haven't figured out how to get the file selected. (Is this a protocol launch?)
    2. Create an application setup file that will have to be modified manual to indicate the VR mode, and will reside in a known location. This, I think, is what this thread is suggesting.
    Thanks for the tips.
     
  16. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    How do you envision your app getting launched? Why not build a UI that let your specify those parameters or select a configuration file on disk?
     
  17. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    The same app is going to be used on a variety of data sets. For "canned" visualizations where an analyst has already pre-configured what he wants to see, it's usually launched from a batch file or button on menu that has been configured to launch the application with a particular configuration file.

    During development, the analyst will run a data set against a variety of configurations (in desktop, a HMD, and in a CAVE (stereo VR), mode). It's just easier to pass cmd line parameters or create batch files to do that.

    I'm not against an interactive UI. It just takes longer for the user to load the visualization and is more difficult to automate. And, I know it's changing, but Unity's UI support just doesn't work very well in VR yet.

    I think the best compromise I'm finding is to associate my app with a new file type for the visualization configuration files and given them a unique extension. I can configure Unity to launch the app on that type, but I don't know how to get the file that caused the launch from within a Monobehavior yet.
     
  18. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Try this: https://forum.unity.com/threads/how...-xaml-file-in-uwp-il2cpp.558445/#post-3702604
     
  19. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    Thanks. Probably won't get to it until next week, but I'll take a look.
     
  20. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    Finally got a chance to take a look at the code you posted. Didn't get it working, but did realize that on file activation, the file is contained WSA.Applicaiton.argument member.

    https://docs.unity3d.com/ScriptReference/WSA.Application-arguments.html

    However, I'm getting a directory not found exception on a path that is perfectly valid.

    DirectoryNotFoundException: Could not find a part of the path "C:\Users\mew9\Desktop\Visualization\Config.kpl".
    at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x00000] in <00000000000000000000000000000000>:0
    at System.IO.StreamReader..ctor (System.String path, System.Text.Encoding encoding, System.Boolean detectEncodingFromByteOrderMarks, System.Int32 bufferSize, System.Boolean checkHost) [0x00000] in <00000000000000000000000000000000>:0
    at System.IO.StreamReader..ctor (System.String path) [0x00000] in <00000000000000000000000000000000>:0
    at System.IO.File.ReadAllText (System.String path) [0x00000] in <00000000000000000000000000000000>:0

    Thought it might be this issue.
    https://issuetracker.unity3d.com/issues/uwp-il2cpp-file-dot-exists-method-returns-false

    So, I enabled the app's file system privledge, and I added broadFileSystemAccess to the manifest per this article
    https://docs.microsoft.com/en-us/windows/uwp/files/file-access-permissions
    (The editor is complaining about Capability being an invalid element in the restricted capabilities namespace.)

    If it's a security problem, I'm surprised by the DirectoryNotFound exception. Can I use the FileStream class in UWP if the app has the proper permissions?
     
  21. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    It gets DirectoryNotFoundException because it doesn't know if directory exists or not.

    Generally, you should solve this problem by adding the supplied StorageFile/StorageFolder to FutureAccessList. Once you do that, you can then access it using System.IO classes.

    broadFileSystemAccess file access is complicated because not only it is a 'restricted' capability, it also doesn't work out of the box: the user needs to go to system settings and specifically grant your app permissions to access their file system.
     
  22. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    I'd rather do it the right way, so thank you for pointing me to the FutureAccessList. It seems strange to me that an application would be able to give itself access to a file/folder this way. And, when I tried to add access to the Application.arguments file, I got an access violation anyway. (Maybe I need to add all the folders in the path as well?)

    The examples I see all use a picker, but I'd really want the user to be able to just double click on a file and go.


    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;

    using UnityEngine;

    #if ENABLE_WINMD_SUPPORT && UNITY_WSA
    using Windows.Storage;
    using Windows.Storage.AccessCache;
    #endif

    using Kraken;

    public class UPWLoadTest : MonoBehaviour
    {
    // Start is called before the first frame update
    void Start()
    {
    }

    private void Update()
    {
    if (Input.GetKeyDown(KeyCode.Space))
    Load();
    }

    // Start is called before the first frame update
    async void Load()
    {
    #if ENABLE_WINMD_SUPPORT && UNITY_WSA
    Debug.Log( UnityEngine.WSA.Application.arguments );

    string prefix = "File=";
    string wsaArgs = UnityEngine.WSA.Application.arguments.Trim();
    if ( wsaArgs.Length > 0 && wsaArgs.StartsWith(prefix) )
    {
    string path = wsaArgs.Substring(prefix.Length);
    StorageFile file = await StorageFile.GetFileFromPathAsync(path);
    StorageApplicationPermissions.FutureAccessList.Add( file );

    Debug.Log("Loading: " + path );
    string text = File.ReadAllText(path);

    Debug.Log("text: " + text );
    }
    #endif
    }
    }

     
  23. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
  24. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    Ok. But, I don't have access to the StorageFile object from the ActivationArgs on the C# side, right?
    It doesn't exactly solve my problem anyway. The config file from the file activation is going to contain paths to data files for loading. I'm trying hard to avoid the file/folder pickers as the app will be run in VR CAVEs and HMDs. And, the data and config files are auto generated for visualization (as often as daily). If I add a StorageFolder to the FutureAccessList, will the contents and subfolders also be authorized? And, future additions to the folder? It would be acceptable to authorize a folder once using the picker strategy.

    The other option I stumbled upon here is to add the ALL_APPLICATIONS_PACKAGES group to the folder(s) where data is stored. It's more work to set up, but looks like it might be more convenient long term.

    https://www.mr-kg.com/uwp-file-access/

    Appreciate the advice. My entire UWP experience is with Unity, so I'm out of my element.
     
  25. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    I *think* you might be able to subscribe to that event inside Awake() function. If that doesn't work, you may need to modify generated VS project.

    I think that would work.
     
  26. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    I hate to ask, but is there a Unity/UWP sample around that shows how to access local files (with or without the FutureAccessList)? I get a COM exception just trying to open the FileOpenPicker. (Wrong thread, maybe?)

    Update:
    https://forum.unity.com/threads/how-to-show-native-file-picker.381528/

    Update:
    Nope. Still getting a COM exception.


    async void Start()
    {
    #if WINDOWS_UWP
    UnityEngine.WSA.Application.InvokeOnUIThread( OpenPicker, true);
    #endif
    }

    #if WINDOWS_UWP
    async static private void OpenPicker()
    {
    Debug.Log("creating file picker");
    var filePicker = new FileOpenPicker();
    Debug.Log("picking single file");
    StorageFile file = await filePicker.PickSingleFileAsync();
    Debug.Log("file pick done");
    }
    #endif
     
    Last edited: Nov 19, 2019
  27. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
  28. mikewarren

    mikewarren

    Joined:
    Apr 21, 2014
    Posts:
    109
    (Sigh) You're right.
    I'd already been using the MS sample you highlighted as a guideline, but wanted to open it to all file types. It would appear that the FileTypeFilter is less optional than I'd understood. I still need to figure out how to use a picker to give access to a specific (pre-known) file, but at least I'm on the right track.

    Appreciate the advice. Including a working example here for others. (Note: I know I've got a concurrency issue related to accessing the text member on both the UI and Applicaiton thread. This was just proof of concept and I'll need to invoke a method to read the file on the application thread.)

    Mike

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6.  
    7. #if WINDOWS_UWP
    8. using Windows.Storage;
    9. using Windows.Storage.AccessCache;
    10. using Windows.Storage.Pickers;
    11. #endif
    12.  
    13.  
    14. public class ShowFile : MonoBehaviour
    15. {
    16.     private string text = null;
    17.  
    18.     // Start is called before the first frame update
    19.     async void Start()
    20.     {
    21. #if WINDOWS_UWP
    22.         UnityEngine.WSA.Application.InvokeOnUIThread( OpenPicker, false);
    23. #endif
    24.     }
    25.  
    26. #if WINDOWS_UWP
    27.     async private void OpenPicker()
    28.     {
    29.         var picker = new Windows.Storage.Pickers.FileOpenPicker();
    30.         //picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    31.         //picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    32.         picker.FileTypeFilter.Add(".kpl");
    33.         //picker.FileTypeFilter.Add(".jpeg");
    34.         //picker.FileTypeFilter.Add(".png");
    35.  
    36.         Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
    37.         if (file != null)
    38.         {
    39.             // Application now has read/write access to the picked file
    40.             Debug.Log( "Picked photo: " + file.Name );
    41.  
    42.             text = await Windows.Storage.FileIO.ReadTextAsync(file);
    43.         }
    44.         else
    45.         {
    46.             Debug.Log( "Operation cancelled." );
    47.         }
    48.     }
    49. #endif
    50.  
    51.     void OnGUI()
    52.     {
    53.         GUILayout.Label( "Hello World!" );
    54.  
    55.             if ( text != null )
    56.                 GUILayout.Label(text);
    57.  
    58.         GUILayout.Label("Bye");
    59.     }
    60. }
    61.  
     
  29. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    You should be able to use "*" (or "*.*", can't remember) as file type to allow picking any file.
     
  30. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    233
    Hi! So, how to make "Open As" working with UWP + Unity? Is it even possible?