Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assembly.LoadFrom works on files inside the single-file bundle #40138

Closed
vitek-karas opened this issue Jul 30, 2020 · 9 comments
Closed

Assembly.LoadFrom works on files inside the single-file bundle #40138

vitek-karas opened this issue Jul 30, 2020 · 9 comments

Comments

@vitek-karas
Copy link
Member

For example if the single-file application is in C:\temp\MyApp.exe and it has MyApp.dll bundled in it, calling Assembly.LoadFrom(@"C:\temp\MyApp.dll") will actually work and return the MyApp assembly. Even though C:\temp\MyApp.dll doesn't exist on disk.

For single-file apps I think that Assembly.LoadFrom should still work on real files (ability to load a plugin for example). It should NOT work paths which don't exist on disk.
More so, if there actually is C:\temp\MyApp.dll file on disk, it's not the one which is returned from LoadFrom, that file is ignored, even though the code asked to load that explicitly.

@vitek-karas vitek-karas added this to the 5.0.0 milestone Jul 30, 2020
@ghost
Copy link

ghost commented Jul 30, 2020

Tagging subscribers to this area: @swaroop-sridhar, @agocke
See info in area-owners.md if you want to be subscribed.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Jul 30, 2020
@vitek-karas
Copy link
Member Author

/cc @MichalStrehovsky @jkotas for API behavioral compatibility

@jkotas
Copy link
Member

jkotas commented Jul 30, 2020

I agree with the behavior you are proposing.

@Symbai
Copy link

Symbai commented Jul 30, 2020

Curios about the following scenario:

  • I have a single application file which is bundled with a Bla.dll file
  • The single application file is located on my desktop
  • I also have a DLL file which is located on m desktop called Bla.dll
  • I want to force loading Bla.dll which is bundled with my single application file but NOT the one on my disk which is in the same existing folder.

How would I do that? Application.LoadFrom(".\\Bla.dll") ? And is the "whatsoever" expected behavior on this identical with all other APIs which accept a path as parameter, for example File.IO I'm asking because there might be scenarios like @vitek-karas said where we explicitly want to load an external file but there will also be scenarios where we would want to load files bundled with the single application file. Both should be possible and there should be a consistent behavior across all APIs on this.

@vitek-karas
Copy link
Member Author

This issue is about the fact that Assembly.LoadFrom does not behave like File.IO APIs in this case - we need to fix that. Any API which loads assembly from a path (Assembly.LoadFrom, Assembly.LoadFile, AssemblyDependencyResolver.LoadFromAssemblyPath) should only work on real files on disk. So if you want to load the file on disk next to your main .exe you would call one of these path loading APIs.

The overall principle is to make as many things work consistently across single-file and non-single-file deployments (no surprises and so on). It's not always possible (Assembly.Location is a good example), but in general if there's a way to do things the same in both cases, that is the recommended solution.

Accessing files in the bundle is currently not exposed in any public API - there's no API which lets you specifically open a file from the bundle. We intentionally made this choice to avoid confusion with File.IO and other APIs. To load assembly from the bundle, you just call Assembly.Load(simplename), the default ALC has access to the bundle and will resolve assemblies form there. That API will work both in single-file and non-single-file cases the same.

If there's a need to include additional "data" in the bundle, the recommended way to do that is to embed such file as a managed resource into one of the assemblies. Then it can be accessed via ResourceManager APIs and it will again work the same for single-file and non-single-file. Assemblies can also be embedded this way and loaded via the stream loading APIs.

@vitek-karas
Copy link
Member Author

Repro is part of the test app here: https://github.com/vitek-karas/single-file-feature-test

@jkotas
Copy link
Member

jkotas commented Jul 30, 2020

it can be accessed via ResourceManager APIs

Nit: The best API for this is Assembly.GetManifestResourceStream. It avoids overhead of the ResourceManager layer.

@Symbai
Copy link

Symbai commented Jul 30, 2020

Any API which loads assembly from a path should only work on real files on disk.

For this specific issue feel free to ignore the following feedback:

I'm pretty sure this leads to additional issues / requests in the future. I can fully understand that its for the .NET team easier to consider everything to be "real" / existing on the disk, rather than having to modify so many APIs to work with both... however. A single file application is meant to be that. And by allowing to store native files (e.g. non DOT NET files) its also to be meant to bundle NON RESOURCES files with it... otherwise, you would have told everyone to mark such a file as a resource file in the project. Which makes your decision to bundle native files obsolete... doesn't it?!?!?

This discussion is not part of this particular issue, I'm sorry for "hijacking" it and I'm sorry for bringing up these points but I definitely see a problem about this at all, even if there are no feedback about this already. This bundle "thing" has become more like a "virtual file system" and when it runs inside memory which is what you are working on, the more expectations will raise with this in the future, even though my opinion seems like it doesn't matter for you.

@vitek-karas
Copy link
Member Author

I do agree that the existing property which let's developers include additional files in the bundle should ideally be eventually deprecated. My reasoning is:

  • I don't want to go down the rabbit hole of inventing a way for all of the File IO APIs to work over the in-bundle virtual file system. Honestly it just sounds scary... - for one we would have to invent new syntax and support it pretty everywhere where file path is allowed, this alone feels really hard. Note that it would have to be new syntax - it would be easy to say that paths like "MyFile.txt" are relative to the bundle and thus should be searched within the bundle - but that would mean that files next to the bundle on disk would not be recognized via simple relative paths... that's a mess.
  • There already sort of is both a virtual file system and APIs to access it - embedded resources are almost like that. One can store really any binary blob as an embedded resource and access it by name and get a stream. Internally we don't use this mechanism for implementing the bundle (and even if we would it would be an implementation detail), but there's nothing wrong with using it in customer applications.
  • Another advantage of using embedded resources is that the same solution works for single-file and non-single-file, so one can write an app in such a way that it will work regardless of the deployment mode.
  • Auto-extracting files to disk like we did in 3.1 and like the inclusion of additional files in the bundle leads to all kinds of problems - I think you filed several of those (I might be wrong). Where is the right place to extract to, who/what owns the files, who/what cleans them up, ... - I would we learned this the hard way in 3.1.

On the topic of native libraries (native .dll on Windows and .so on Linux and so on): This is a hard problem. Ideally we would be able to bundle them and load them from the bundle directly, unfortunately there's no direct OS support for doing so (definitely not on Windows). So we would have to write our own native library loader - which is just asking for trouble (just the complexity of calling DllMain correctly on Windows is probably not worth it). Or extract to disk as mentioned above. There's additional details on this topic here: https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/staging.md#5-run-from-bundle-native-libraries.

I would like to not treat the bundle as a virtual file system - I think it's just too much potential complexity for relatively little gain. Ideally the actual implementation of how we store the assemblies in the bundle would not be visible anywhere (this specific issue is an example where the implementation leaks to the public API - so we should fix that).

I personally think that we should not try to support every application with single-file as is - there are certain thing we just can't emulate, so there will always be some amount of potential changes to the app to make it work as single-file. We're starting to work on better tooling to help with understanding what needs to change in the app: dotnet/roslyn-analyzers#3921.

@jeffschwMSFT jeffschwMSFT removed the untriaged New issue has not been triaged by the area owner label Aug 6, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants