Solution
Are you expecting main.vi to load from inside the application or from its original files on disk?
From inside application:
If you've set main.vi as Always Included in your build, then you can load it from there, but the path passed to the open VI reference should be the executable location, not the source location. My guess is you are doing this, and the 8.x layout works because there is a special case for open VI reference from inside this type of app that looks for the file name rather than the entire path. Thus, even though you are providing the path to the source, LabVIEW loads it from the executable. This is a safety net but is not the intended usage. Instead, you should build a path inside the app like <path>\exename.exe\main.vi. You can determine that dynamically with something like below. That path under the executable may be different for the non-8.x layout.

This will not work when running from source. You can solve this by checking the type of application and using a different path when running from source. However, if the relative path between the two VIs is consistent, you can compute the path to main.vi using the path of Launcher.vi. Just place the code below inside Launcher.vi. As long as neither VI is part of a library, this should work both in build layouts and when running from source.

From its original files on disk:
This just doesn't work. VI source can be source only and tends to reference files from vi.lib. The LabVIEW Run-Time Engine (RTE) can't load source only VIs and has no vi.lib. Anything you load in a built app needs to have gone through a build to prepare it and everything it depends on to be used in an RTE. Easiest answer is to include them in the executable. A more complicated approach is to use a separate build to create a Packed Project Library (PPL) with the dynamic VI. In this case, you'll need to provide the path inside that PPL to the open VI reference.