1

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?

Clint
  • 6,011
  • 1
  • 21
  • 28
user
  • 1,261
  • 2
  • 21
  • 43
  • 1
    Don't use `Directory.GetCurrentDirectory()`. You don't know what Directory will be the *current*. Depending on the type of project, you can use `AppDomain.CurrentDomain.BaseDirectory`, `Application.StartupPath`, `AppContext.BaseDirectory`, `Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)` etc. (`Environment.CurrentDirectory` is not good either) – Jimi Feb 06 '20 at 04:20
  • Hey User, I understand that all your pdfs get copied to output dir on build action right ? – Clint Feb 06 '20 at 05:35
  • @Clint - I've played with a few settings. In the current iteration they do. Maybe I don't fully understand where the output dir is? – user Feb 06 '20 at 14:08
  • the same directory where your exe is – Clint Feb 06 '20 at 15:00
  • Yeah, see my edit. On Build, they're being placed there; not on Publish, which is incredibly frustrating. – user Feb 06 '20 at 15:02
  • hey @user, have sent a chat invite – Clint Feb 06 '20 at 16:25

2 Answers2

1

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);
            }
        }
 }

Clint
  • 6,011
  • 1
  • 21
  • 28
  • Hey @Clint, appreciate the post. I don't believe paths are causing the issue. In the solution structure you posted, the resource is in the Startup project. The problem I'm facing is that when the resource is in another project in the solution, `AssistantLib` in this case, the resource doesn't ship once published and installed. See my second update to see comparison images. – user Feb 07 '20 at 22:15
  • @user, so basically you want to reference AssistantLib which contains the resource from your main AssistantProject. Which means AssistantLib project results in a DLL which you would consume from MainProject, correct ? and then you would publish the main project – Clint Feb 08 '20 at 08:19
  • hey, this worked. Making them Embedded Resources, `Assembly.Load` & `assembly.ManifestResourceStream` were the key. Appreciate the help. – user Feb 10 '20 at 02:27
  • One question I have - what's the best way to manage Resources in /Resources/Guidelines AND /Resources. Or is that bad practice? – user Feb 10 '20 at 02:33
  • What do you mean by manage, what is the intent ? you do not want to bulk up your resource by embedding too many things than what is necessary. Another way to to embed is to navigate to Visual Studio > Project > Properties > Resource . You can also add resources that way. – Clint Feb 10 '20 at 07:28
  • Should have been clearer - I mean organize. The library is going to contain a series of documents as resources. That's part of what it's designed for because the UI is for searching them. – user Feb 10 '20 at 14:41
  • ahh okie, I do not have a particular remedy for that :), I would do it, just as we organize anything else group the related ones into a folder and that pointing to the parent one – Clint Feb 10 '20 at 14:44
  • I figured as much. Thanks for the help! – user Feb 10 '20 at 14:57
0

i was facing same problem when i was building the project on exe format using the visual studio setup i simple add pdf file in project build output directory . you have to copy your pdf folder in bin or release folder and use ~ root directory to get file

Akash Gupta
  • 103
  • 8