Search code examples
c#.netclickoncevisual-studio-2019

Find resources of Class Library from Main Project


The Problem

My solution is made up of a handful of projects. The main one for the UI we'll call Assistant, and the backend we'll call AssistantLib.

Here is the structure:

enter image description here

In AssistantLib I have included PDFs as resources in a Resources folder with a Build Action of Content and Copy to Output Directory as Copy Always. I am able to locate them when debugging by using a combo of these:

    private string GetArtifactPath(string artifactName)
    {
        return Path.Combine(GetResourcePath(), artifactName);
    }

    public static string GetResourcePath()
    {
        return Path.Combine(Directory.GetCurrentDirectory(), "Resources");
    }

This works. Once I return the string from GetArtifactPath, I open the File with a Process object and AcroRd32.exe.

Note that I need to reference these files by their file paths. They're not simple txts to read or stream. I also need to be able to open them with certain flag provided by AcroRd32.exe. That means I must have the file path.

The problem I'm having as that once I publish the ClickOnce app, I receive an error that the file couldn't be found:

Error: Could not find a part of the path 'C:\Users\EL-C\AppData\Local\Apps\2.0\3JCPDD49.7G5\9122AMZE.NZL\azte..tion_edea8654ffceff97_0001.0000_447ed0da08290357\Resources\Guidelines\3.2'.. Stacktrace:    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

And of course, when I go to that location, the resources aren't there.

What I've Tried

  • I've changed the Build Action to Embedded Resource & None - same result
  • I've changed the Copy Output Directory to Do Not Copy
  • This doesn't apply because I can't stream these resources, they're not simple text files. I'm also not trying to read them, I'm trying to open them.
  • This also doesn't apply because referencing MyNamespace.Properties.Resources.My_Zip_File; doesn't work in my situation. I don't know the name of the resource ahead of time.

Update

I am looking into post-build events. In doing so, I found that these resources are already in the output directory following a build:

enter image description here

But for some reason they don't show up when I publish:

enter image description here

Update 2

To illustrate how the folder structure affects this, here are before and afters.

When a Resource is in AssistantLib (e.g. EVMSBP), this is the structure:

Resource in Lib

And here is what the ClickOnce publish folder looks like after installing with the resource in AssistantLib:

Missing

Alternatively, when a Resource is in Assistant (again EVMSBP), this is the structure:

Resource in Assistant

And here's what the ClickOnce publish folder looks like after installing with the resource in Assistant:

Not Missing

From what I can tell, the Resource MUST be a part of the Startup project. That sounds like insanity?

What am I missing?


Solution

  • With more details from the edit, this is how I replicated it

    • In AssistLib project

      • Files in Resources with Build Action as Embedded Resources.
      • Output Type: Class Library (AssistLib.dll)

      • See Solution Structure


    • In AzTech Main Project
      • I referenced the AssistLib.dll
      • Check if the resource (pdf) that I want to load exists in AssistLib.dll
      • Loads the resource
      • Launches the PDF
      • So far so good
      • I Published the App and installed
      • I launched the App from the Icon on the Start Menu
      • Which Launch the PDF that was embedded as resource in the DLL


    AzTech.cs

    var nameOfTheFile = "test.pdf";
    ResourceManager.GetResourceInfo(nameOfTheFile);
    if (ResourceManager.resourceExists == false)
    { Console.WriteLine("Specified PDF file not found"); return; }
    
    Console.WriteLine("Resouce found in DLL");
    ResourceManager.LoadResource(nameOfTheFile);//Will load the pdf in your main project
    
    Process.Start(nameOfTheFile);
    
    

     class ResourceManager
     {
            public static bool resourceExists { get; set; } = false;
            private static Stream resourceStream { get; set; }
            public static void GetResourceInfo(string fileNameWithExtension)
            {
                const string pathToResource = "AssistantLib.Resources.Guidelines";
    
                var assembly = Assembly.Load("AssistantLib");
                //var names = assembly.GetManifestResourceNames();
                var stream = assembly.GetManifestResourceStream($"{pathToResource}.{fileNameWithExtension}");
                if (stream == null)
                    return;
    
                resourceExists = true;
    
                resourceStream = stream;
    
            }
    
            public static void LoadResource(string newFileNameWithExtension)
            {
                if(File.Exists(newFileNameWithExtension))
                {
                    Console.WriteLine("File already exists");
                    return;
                }
                using (Stream s = File.Create(newFileNameWithExtension))
                {
                    Console.WriteLine("Loading file");
                    resourceStream.CopyTo(s);
                }
            }
     }