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. Dismiss Notice

DirectoryNotFoundException - Works when Build and Run, doesn't work when launching exe manually

Discussion in 'Scripting' started by cpuRunningHot, Sep 18, 2020.

  1. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    I encountered a strange issue. I have file in the resources folder that I load at runtime. I have been testing with "Build And Run" for quite some time now and it all works as expected. However, I just tested running the exe manually and encountered a DirectoryNotFound exception when calling Directory.GetFiles(path), where the path is...

    "Assets/Resources/SubFolder"

    again, this works in both Editor mode and Build and Run.

    However, the exception thrown is showing a fully qualified path, which is like...

    "C:\devGamesExport\MyGame\Assets\Resources\SubFolder"

    which obviously does not exist.

    Is there any way of getting the final EXE to exhibit the same behavior as "Build and Run" such that it recognizes the resources path using .NET file/directory methods such as Directory.GetFiles()? I was under the impression that Build and Run would be identical to launching the exe manually.

    Thx in advance :D
     
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    The resources folder contents are built into the project and can be loaded via the unity Resources.Load method. If you want a folder to save and load files, you can create one at runtime in the Application.dataPath if you wish or you can also use the StreamingAssets folder. Any files placed in the special StreamingAssets folder will be included in the build as they are.
     
  3. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    I'm aware of this, and I am using Resources.Load() to load them dynamically at runtime. I am also considering refactoring to use the StreamingAssets folder instead if need be. But the questions remains... why the change of behavior in "Build and Run" vs. manually launching the executable, and is there a solution? So far, I've only identified the issue with Directory.GetFiles(), and I *could* refactor so that is not necessary ( i.e. I could cache the directory contents into a database ). I would first prefer to understand the root cause before making structural changes.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    Go look at the built target and you'll instantly see why. NONE of the files you put in (Except those in streaming assets) look anything like that once they make it into the build. They're all renamed to GUID files.

    Yes: always best to drag references into Scenes / Prefabs / other Assets.

    Second best is to load it with Resources.Load(). You gotta know the filenames in advance though so it's up to you to make a directory of what is in there with an editor script. Look online, there's tons of examples out there for pre-build step code to produce build-time file lists.

    Or else just do a Resources.LoadAll() and look for the name yourself in the array that comes back.
     
  5. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    Heya Kurt,

    Perhaps there is a miscommunication. "Build and Run" indeed builds the executable and launches it. It *does* work as expected when I do this. It only does not work when running the executable manually. I don't fully understand the mechanism, but I suspect that Unity maps a virtual file system or something like that. I suspect that there is a subtle difference in the environment when launching through "Build and Run" vs. directly from the exe.

    For my purposes, direct references is not an option since I am building dynamic content at runtime. When I'm not building dynamic content, I use direct references as the best practice.

    I'm fairly confident that if I move all of the dynamic data to the StreamingAssets folder, it will work as expected. However, I do find it more graceful to keep them contained in the Resources. The issue with Directory.GetFiles() has work arounds ( I could generate a list of all of the files ahead of time store them in a single file, or use StreamingAssets instead, or refactor so file searching is not necessary ), but I'd like to understand exactly what the difference between "Build and Run" vs. manually launching is. There may be other issues related to it that I have yet to identify.

    It could have been working "by accident" before when using Directory.GetFiles() when technically it shouldn't, which is a valid answer as well and may very well be the most probable. As it stands now, Directory.GetFiles() can locate files in the Resources folder in Editor mode AND in "Build and Run" in Unity Windows 2019.3.15f1

    Using the language of a QA department...

    Expected behavior:
    Build and Run should behave the same as running the executable manually with regards to file management in the Resources folder

    Actual behavior:
    Build and Run does not behave the same as running the executable manually with regards to file management in the Resources folder

    :D
     
  6. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    Pretty sure I just answered my question... it was a coincidence that the current working directory is set to the project root, so all file access calls were actually locating or loading the files directly, even when doing a "Build and Run". I was able to break it simply by calling Directory.SetCurrentDirectory("c:\\"); So, when calling "Build and Run", the current working directory was still set to the project root, while running it manually it was not.

    Since it was "working" for so long, I simply assumed that Unity was doing some file system magic, but alas that was an invalid assumption.

    Refacting to StreamingAssets it is. Thanks for listening :D
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    Yeah, that's a thing I've done too. Only way to be sure is to tar it up, put it on a new computer and test it on that computer, which will simulate what the customer receives from you.
     
    cpuRunningHot likes this.
  8. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    Indeed. One strategy that I am a big fan of is to never get hit by the same problem twice. So with that being said, I'm going to see if there are any reasons *not* to set the current working directory. If the game will run normally and the editor is not affected, I'll probably set the working directory to something other than the project root so in the future, I'll get a hard error right away if I do something as silly as this again.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    This is a goals-oriented approach.

    While laudable, a better approach is a systems-oriented approach.

    Develop systems to get good at recognizing when you do hit the same problem again.

    Because you will.

    (I mean unless you abandon your engineering career now.)

    You will hit the same problem again, trust me.

    Get fast at recognizing and fixing it.

    That's a system.
     
    cpuRunningHot likes this.
  10. cpuRunningHot

    cpuRunningHot

    Joined:
    May 5, 2018
    Posts:
    94
    Just in case anyone is interested, here is a quick fix to make sure that current working directory when running the game from "Build and Run" is consistent with manually running it. First thing is that we *need* !UNITY_EDITOR, or the editor will die a slow death ( try it, it's fun ). I added UNITY_STANDALONE_WIN for no reason other than I did not test it on other platforms, but I doubt that it's important. I call it in the Awake() method of a monobehavior that runs before most/all others.

    It's simple, and will ensure that current working directory from "Build and Run" is consistent with running the exe.

    Code (CSharp):
    1.  
    2. #if (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
    3.             // This is to ensure that calls to relative file/directories fail when launching
    4.             // from "Build and Run" so we can quickly identify a mistake, that would only
    5.             // be revealed if we ran the exe manually.
    6.             Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
    7. #endif
    8.