Search Unity

Resolved Migrating from Windows Standalone to UWP

Discussion in 'Windows' started by marck_ozz, Jan 25, 2021.

  1. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Hello guys!!

    I'm trying to migrate my Windows PC app from "Standalone" to "UWP" and it result to be more challenging that I thougth, assume that both are windows bulds and sould works the same was my first mistake.

    I'm following the steps on this https://unity3d.com/partners/microsoft/porting-guides

    I'm able to build my App and tested in Windows 10, but some functions are letting off of my build, in specific the ones that uses dlls like "System.Windows.Forms" and "user32.dll" wich I uses to Open file and resize the windows for example.
    So, I'm trying to use this UWP specific APIs and methods to replace them like this:

    https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-using-file-and-folder-pickers

    But I'm unable to use the "Window.Storage" API that supouse to be already on my project (Am I right?). I'm getting the classic "Error: are you missing a using directive or an assembly reference?" error.

    In fact I'm unable to use any "Windows.Something" directive.

    My Questions are:

    1.- Is there an ACTUALIZED guide for UWP developing, build or recommended configuration?
    2.- How to use the Windows APIs in unity? or better, how to add those APIs?

    I have to add that my app for stand alone works pretty well, but now I have to take advantage of the Windows Store functionalities.

    I think that an actualized guide "step by step" on develop for UWP could be more helping, all the information out there is out of date.

    I'm using:
    Unity 2019.15 LTS
    Visual Studio 2019 community
    Windows 10


    My settings:

    upload_2021-1-25_14-39-39.png

    upload_2021-1-25_14-40-32.png
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    You need to wrap the access to those Windows.* APIs with #if ENABLE_WINMD_SUPPORT/#endif. It won't work in the editor, but the code path will become active when you build your game for UWP.
     
  3. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thank you!

    I have tried with something like this:

    Code (CSharp):
    1. public void OpenFile()
    2.     {
    3.         FilePicker();
    4.     }
    5.  
    6.     async void FilePicker()
    7.     {
    8.  
    9. #if ENABLE_WINMD_SUPPORT
    10.         var picker = new Windows.Storage.Pickers.FileOpenPicker();
    11.         picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    12.         picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    13.         picker.FileTypeFilter.Add(".jpg");
    14.         picker.FileTypeFilter.Add(".jpeg");
    15.         picker.FileTypeFilter.Add(".png");
    16.  
    17.         Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
    18.         if (file != null)
    19.         {
    20.             Debug.Log(file.Name);
    21.         }
    22.         else
    23.         {
    24.             Debug.Log("Operation cancelled");
    25.         }
    26. #endif
    27.  
    28.     }
    which remove the errors but as you say I can't test that in editor, I have to build (that is a long process in UWP) and hope that code works, if not, I have to repeat the whole process again.

    in some cases it gives me an error when I try to build due to some mistakes in that code. Plus, there some other more complex code like this (that also I try to implement):

    https://docs.microsoft.com/en-us/windows/uwp/monetize/enable-subscription-add-ons-for-your-app

    In that case, I need this "prerequisites":

    • You have created an app submission in Partner Center and this app is published in the Store. You can optionally configure the app so it is not discoverable in the Store while you test it. For more information, see the testing guidance.
    • You have created a subscription add-on for the app in Partner Center.

    But I can't even have app submission in the Parnet Center due to all those "bugs-erros". There's a long way from just an app builded in UWP and an app suibmited to Partnet Center (e.g I have to have a Windows Certified app via Certification kit) but firs I need a clean code.

    Maybe is more complex than Platfrom Dependent compilation. Is that the only way?
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    If you use "Script Only" build checkbox in the build settings, the iteration time should be fairly quick.

    You could also use "ExecutableOnly" build type, which eliminates the generated Visual Studio project and generates a built app directly (like the Windows Standalone player build). You can't ship a build like that, but iterating on it should be much faster.
     
  5. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thanks for the responses

    In order to use "Script Only" I have to check "Development Build" what I was already discarted because on the "Certification app" (https://docs.microsoft.com/en-us/previous-versions/windows/apps/hh694081(v=win.10)) step on the "Windows stuff" side it gives me an error message saying tha the "UnityPlayer" was precisely on develompent build.

    Now, when I select "script only" i get this error "Cached player destination differs from selected player destination. Please uncheck 'Scripts Only Build' and build again before attempting another scripts only build"

    Those are the kind of "tips" or "steps" than should be in a new "Guide for UWP", and I feel like I'm on the half on the way on this, on which I have arrived by taken information from here and there, even on the windows official pages.

    Is there something like an example project out there from Unity or the Windows side with "Good Practices" for UWP?
     
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Yes, you have to use development player for scripts only builds, and yes, it will fail certification. However, you're not submitting these builds that you iterate on to the Windows Store, so failing certification while iterating is fine. Once you finish development, you can make a single final build as "non-development". To use script only builds, you have to first make a full build with the same build settings and not modify your scenes. However, those builds will be way quicker than regular builds.

    We had a couple example projects for using specific UWP features but I'm looking at it and seems that they were taken down (I'm trying to figure out why). Our UWP development documentation can be found here:

    https://docs.unity3d.com/Manual/windowsstore-gettingstarted.html
     
  7. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Ok, I'll double check that info in case I missed something.
     
  8. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Hello again,

    This is what I got:

    First according with this: https://docs.unity3d.com/Manual/IL2CPP-WindowsRuntimeSupport.html, the WinRT suport suppuse to be already implemented in my project.

    And according to this: https://docs.unity3d.com/Manual/windowsstore-scripts.html, "Because the same script code is also used by Unity Editor (which always uses Mono), all code that uses WinRT API must be under ENABLE_WINMD_SUPPORT define" which I already have in the example given before.

    But still have this error when I try to build:

    "Assets\Scripts\FileManagerUWP.cs(39,54): error CS1929: 'IAsyncOperation<StorageFile>' does not contain a definition for 'GetAwaiter' and the best extension method overload 'AwaitExtensions.GetAwaiter(Process)' requires a receiver of type 'Process'"

    This Windows docs, https://docs.microsoft.com/en-us/wi...l-asynchronous-apis-in-csharp-or-visual-basic, says that the "FileOpenPicker.PickSingleFileAsync" has an "StorageFile" result type, so for me it seems like "await" operator it's not working as it should in an "UWP buid" but in an "Unity way".

    I also tryed something like this with the same result:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Threading;
    4. using System.Threading.Tasks;
    5. using UnityEngine;
    6.  
    7. public class FileManagerUWP : MonoBehaviour
    8. {
    9.     public GameObject button2;
    10.  
    11.     public void OpenFile()
    12.     {
    13.         FilePickerTask();
    14.     }
    15.  
    16.     async void FilePickerTask()
    17.     {
    18.         await FilePickerAsync();
    19.     }
    20.  
    21.     async Task FilePickerAsync()
    22.     {
    23.  
    24. #if ENABLE_WINMD_SUPPORT
    25.             var picker = new Windows.Storage.Pickers.FileOpenPicker();
    26.             picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    27.             picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    28.             picker.FileTypeFilter.Add(".jpg");
    29.             picker.FileTypeFilter.Add(".jpeg");
    30.             picker.FileTypeFilter.Add(".png");
    31.  
    32.             Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
    33.             if (file != null)
    34.             {
    35.                 Debug.Log(file.Name);
    36.                 //Do something
    37.             }
    38.             else
    39.             {
    40.                 Debug.Log("Operation cancelled");
    41.                 //do something
    42.             }
    43. #endif
    44.         await new WaitForSeconds(0.1f); // added to avoid the CS1998 warning since "await" is under platform dependant code
    45.     }
    46. }
    even try this silly code to see if it works only in UWP build (it obviously works):

    Code (CSharp):
    1. public GameObject button2;
    2.  
    3.     public void OpenFile()
    4.     {
    5.         //FilePickerTask();
    6. #if ENABLE_WINMD_SUPPORT
    7.         button2.SetActive(true);
    8. #endif
    9.  
    10.  
    11.     }

    I thing I missing something if not in the project settings-configuration maybe in the code.

    I will thank any help!!!!!!!!!.
     
  9. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    You're just missing "using System;" at the top of the file. GetAwaiter is an extension method for IAsyncOperation interface and it's defined in the System namespace. Compiler requires it to use the "await" operator.
     
    glingicom likes this.
  10. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thanks for your answers.

    1.-
    that works, I I thought that was the job of "using System.Threading;" and "using System.Threading.Tasks;".

    2.- The iteration time beetwen build and build is still too long when I creating the packages with the .sln file (almost 40min) is that normal? I creating only for x64.

    3.- I have to ask again if there's no way to include those WinRT APIs directly in the project like "using Windows.Storage" instead of using "#if ENABLE_WINMD_SUPPORT"? that grayed out the code and no error are shown until I try to build, its like coding on Notepad and hope everything works fine, I'm not that good haha.

    4.- After all that anoying things I still have errors in those functions that I try to use in the builded app (open file, folder).
    I test another method that I found in here: https://docs.microsoft.com/en-us/uw...eFolder_Windows_System_FolderLauncherOptions_

    which shows a file explorer on UWP.

    Code (CSharp):
    1. async void FolderLaunchAsync()
    2.     {
    3.         //C:\Users\Documents\UnityGames\Imagenes
    4.  
    5. #if ENABLE_WINMD_SUPPORT
    6.             string sPath = @"C:\Users\Documents\UnityGames\Imagenes";
    7.          
    8.  
    9.             Windows.Storage.StorageFolder folder = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(sPath);
    10.  
    11.             await Windows.System.Launcher.LaunchFolderAsync(folder);
    12.             if (folder != null)
    13.             {
    14.                 Debug.LogWarning("OK FOLDER");
    15.                 //Do something
    16.             }
    17.             else
    18.             {
    19.                 Debug.LogWarning("NO FOLDER");
    20.                 //do something
    21.             }
    22. #endif
    23.         await new WaitForSeconds(0.1f); // added to avoid the CS1998 warning since "await" is under platform dependant code
    24.     }
    The Unity player shows the next for the first code (openFile):
    Code (CSharp):
    1. (Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)
    2.  
    3. Exception: Exception of type 'System.Exception' was thrown.
    4.   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
    5.   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00000] in <00000000000000000000000000000000>:0
    6.   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00000] in <00000000000000000000000000000000>:0
    7.   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00000]...
    and the next for the second (show file explorer):
    Code (CSharp):
    1. (Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 35)
    2.  
    3. UnauthorizedAccessException: Attempted to perform an unauthorized operation.
    4.   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
    5.   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00000] in <00000000000000000000000000000000>:0
    6.   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00000] in <00000000000000000000000000000000>:0
    7.   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00000] in <00000000000000000000000000000000>:0 ...
    I'll attach the complete log file.

    Maybe depelop for UWP on Unity is not that easy (or posible) for those "more specific" functions.
     

    Attached Files:

  11. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    That is absolutely not normal. Can you share the build settings you use?

    You can switch to "player" context in Visual Studio:

    upload_2021-1-28_16-6-50.png

    Once you do that, #if ENABLE_WINMD_SUPPORT define becomes active and you get full intellisense.

    Those functions might have to be called on the Windows UI thread. You can use UnityEngine.WSA.Application.InvokeOnUIThread() to move the execution to the UI thread, and UnityEngine.WSA.Application.InvokeOnAppthread() to move the execution to the App thread. I also believe you cannot give arbitrary path to GetFolderFromPathAsync: UWP applications by default only have access to very fews paths on the filesystem. If you want to supply an arbitrary folder, you're going to have to pick it from a file/folder picker. In general, none of this is specific to Unity: it'd work the same way if you tried running this code in any UWP application (with the exception that Unity scripts don't execute on the UI thread by default).
     
    marck_ozz likes this.
  12. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    are the same tha I posted before, just added the Scrip only option:

    upload_2021-1-28_20-12-19.png
     
  13. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    How can I activate that:

    upload_2021-1-28_20-21-48.png
     
  14. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Ah, you might need to enable player project generation in Unity preferences: upload_2021-1-28_18-45-56.png

    As for the build times: which step takes a long time - building from Unity or building generated Visual Studio project?

    You should check the development build checkbox and switch build mode to "Executable Only" and uncheck scripts only builds. Then, build to an empty folder, and afterwards check the scripts only build (and have it checked for subsequent builds).

    I suspect scripts only build doesn't work for you because the development build checkbox isn't checked.
     
    gypark0 and marck_ozz like this.
  15. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    Thanks a lot!!! it's so much easier now.

    This works too, I can select a file from my disk like in Standalone. Here is the code (as an example):

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System;
    4. using UnityEngine.UI;
    5. using UnityEngine;
    6.  
    7. public class FileManagerUWP : MonoBehaviour
    8. {
    9.     public Text thisTxt;
    10.     private string path_;
    11.     private bool done_;
    12.  
    13.  
    14.     public void OpenFile()
    15.     {
    16.         FilePickerAsync();
    17.     }
    18.  
    19.  
    20.     async void FilePickerAsync()
    21.     {
    22.  
    23. #if ENABLE_WINMD_SUPPORT
    24.             UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
    25.         {
    26.             var picker = new Windows.Storage.Pickers.FileOpenPicker();
    27.             picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    28.             picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    29.             picker.FileTypeFilter.Add(".jpg");
    30.             picker.FileTypeFilter.Add(".jpeg");
    31.             picker.FileTypeFilter.Add(".png");
    32.  
    33.             Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
    34.             if (file != null)
    35.             {
    36.                 Debug.Log(file.Name);
    37.                 path_ = file.Name;
    38.                 done_ = true;
    39.             }
    40.             else
    41.             {
    42.                 Debug.Log("Operation cancelled");
    43.                 path_ = "No file selected";
    44.                 done_ = true;
    45.             }
    46.         }, true);
    47. #endif
    48.         await new WaitForSeconds(0.0f); // added to avoid the CS1998 warning since "await" is under platform dependant code
    49.  
    50.     }
    51.  
    52.     void Update()
    53.     {
    54.         if (done_)
    55.         {
    56.             thisTxt.text = path_;
    57.             done_ = false;
    58.         }
    59.     }
    60. }

    It was building the Visual Studio project because I was generating the packages every time, I didn't explore the "Executable Only" option before, it just "make the work for me" and for developing and testing is OK and faster (like 5-8 min every buld).

    Thanks a lot man!!!, I think (and hope) that with this I can replace the rest of standalone functions for the UWP ones.
     
  16. marck_ozz

    marck_ozz

    Joined:
    Nov 30, 2018
    Posts:
    107
    anyways it would be great to have a look to those examples!

    thanks again