0

I would like to create a dotnet tool with the purpose of holding external binaries my project depends on, but do not want to reference the binaries directly. In C# code I would start one of the .exe files as a standalone process. My idea is to package the binaries as content so they are not referenced in the project. However I need to keep the binaries from being copied to the bin folder of the parent projects, but still find a way to call the .exe file.

I put this within the .csproj used for creating the dotnet tool and run dotnet pack. The binaries are packaged as content in the tool.

    <ItemGroup>
        <!-- 
            Include the binaries as content in the nuget package.
            Content files are immutable & arbitrary files that will not be referenced in the project.
        -->
        <Content Include="binaries/*.*" Pack="true">
            <CopyToOutputDirectory>Never</CopyToOutputDirectory>
            <PackageCopyToOutput>true</PackageCopyToOutput>
            <Pack>true</Pack>
        </Content>
    </ItemGroup>

So, I have the nuget packed with 3rd party files and want to use it from a project that consumes it. Is there a way I can access the .exe without copying the binaries to the bin folder of the consuming project?

ejones
  • 1
  • 1
    Using processes? I don’t understand why you don’t want to reference the binaries, but processes are usually the best way to run external exes. – SupaMaggie70 b Dec 02 '22 at 15:42
  • _"without copying the binaries to the bin folder"_ - something like "embedding" the exe? I am not quite sure, but you may want to investigate ["ILMerge"](https://github.com/dotnet/ILMerge) ... (I am not affiliated with the project. I merely know it exists.) – Fildor Dec 02 '22 at 15:52
  • @SupaMaggie70b yes, I want to call it using System.Diagnostics Process class, but I am having trouble finding the path to the executable within the Nuget package created – ejones Dec 02 '22 at 16:12
  • @ejones I don’t know how Nuget packages work with exes. You can always just download the exe yourself, and put it in the right folder so you can always find it. However, this is probably not what you want. I am still completely confused as to why you refuse to load the dll. It only takes up memory once you call a function from it, thanks to JIT compilation. – SupaMaggie70 b Dec 02 '22 at 16:16
  • This might help. https://stackoverflow.com/questions/891958/embedding-a-binary-file-inside-a-class-library – Vic F Dec 03 '22 at 02:14
  • > "I am having trouble finding the path to the executable within the Nuget package created": `GeneratePathProperty="true"` on your consuming project's `PackageReference` will give you an MSBuild variable that contains the path to the nuget package root directory. You can then use that variable to declare the path to the exe file within the package. See https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty – svenhuebner Dec 03 '22 at 18:54
  • @svenhuebner thank you! Finally, is there a way to retrieve this variable (or the path to the exe file) from within my C sharp code? – ejones Dec 05 '22 at 17:26

1 Answers1

0

Although the question is vague about what you want and don't want, together with the comments I think I have an idea now:

  1. You want to distribute an application (exe and accompanying files) via a nuget package.
  2. You want to consume that nuget package in a project.
  3. You want to execute the application from that package at runtime from the project's code.
  4. You do not want to have the application files lying around in the project's output folder.

You already did 1. by adding the application to the package as content. You also already did 2.

To accomplish 3. you have to be able to access the application file at runtime from a known permanent location. The "package" is a build-time concept. It does not exist at runtime.

During the build (package restore), nuget will download the package and then store it and its contents in a local package cache folder. From your question in the comments above I understand your plan was to have your project execute the exe in that folder. That is possible, but highly inadvisable. You cannot rely on the local package cache folder. It is a cache. It can be deleted at any time.

What you can do instead is what Fidor hinted at: add the application files as embedded resource to your project assembly. This will bloat your project's assembly but makes all application files available at runtime without them lying around somewhere. You do not need ILMerge for that though, because you control the project assembly build.

Creating and using embedded resources from a nuget package

To do that you have to add the application files to the EmbeddedResource item in your project. For that you need the build-time location of the nuget package contents. You get the latter by adding GeneratePathProperty="true" to your PackageReference. This gives you an MSBuild property called Pkg<your_package_name_sanitized containing the root path of the nuget package contents (see the docs). You can then use that variable to declare a globbed path to the application files within the package, like so:

<PackageReference Include="My.Package" Version="1.0.0" GeneratePathProperty="true" />
<EmbeddedResource Include="$(PkgMy_Package)/contentFiles/any/netstandard2.0/**/*" LinkBase="App" />

This will make all application files available at runtime as embedded resources with the name prefix <project_assembly_root_namespace>.App.. You then get a stream for an embedded file at runtime with

typeof(SomeProjectType).Assembly.GetManifestResourceStream("Project.Root.Namespace.App.MyApplication.exe")

and can e. g. save the files to a location from which they can be executed as their own process.

svenhuebner
  • 342
  • 1
  • 2
  • 10