2

I have an application that, due to its nature, needs to be run after business hours so as not to interrupt the workflow of the users. I have downloaded and added a reference to Microsoft.Win32.TaskScheduler.dll, as per recommendation on another question regarding the best way to schedule a task to run later that day.

In debugging, the program works as expected, however when deploying, I get the following error:

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Win32.TaskScheduler, Version=2.5.16.0, Culture=neutral, PublicKeyToken=0d013ddd5178a2ae' or one of its dependencies.

This leads me to believe that the dll is not being added to the executable correctly when it's being built.

Steps I've taken to resolve this:

  1. In the solution explorer, ensured Microsoft.Win32.TaskScheduler Copy Local property is True
  2. Project Properties, Publish, Application Files - Microsoft.Win32.TaskScheduler.dll Include in publish, Download Required, Include Hash
  3. Remove dependency and re-add
  4. Followed the suggestions in this answer

All have, at this point, failed. I can confirm that the .dll is in the /bin/debug folder as it should be. Additionally, I manually added System.Management.Automation the same way, and it appears to function as expected.

If anyone has any additional suggestions, it would be appreciated.

Community
  • 1
  • 1
Foxtrek_64
  • 357
  • 5
  • 14
  • Did you include the TaskScheduler dll with the deployment? IOW, not just in your bin folder, but also where the .exe is running? – B. Clay Shannon-B. Crow Raven Apr 01 '16 at 22:00
  • When placing the TaskScheduler.dll in the same directory as the deployed program and running it, the program kicks off correctly. However, because of how this deployment is set up, I am limited to a single file. Edit: Should I add the dll as a Resource and write the file to the file directory at runtime or is there an alternat/better solution? – Foxtrek_64 Apr 01 '16 at 22:10
  • 1
    Then it sounds like the deployment setup needs to change; if the DLL needs to be there for the app to run, it needs to be deployed. – B. Clay Shannon-B. Crow Raven Apr 01 '16 at 22:15
  • 1
    If you are restricted to a single file (for whatever reason) maybe the answer to [this question](http://stackoverflow.com/questions/8077570/how-to-merge-multiple-assemblies-into-one) may help you. System.Management.Automation may already be present in the Global Assembly Cache as this is part of PowerShell – derpirscher Apr 01 '16 at 22:23

1 Answers1

0

I had attempted to use derpirscher's solution, but the solution I ended up using was to deploy the dll to the running directory of the program. It's not the most elegant solution, but it satisfies the "single file deployment" requirement. It should be noted, however, that on all of the systems this is being deployed to, the user account is an administrator. This solution may not work if the user the program runs under is not an administrator, depending on where the executable is run from (e.g. another user's account folder).

class Program
{
    //Deploys the DLL to the location of the executable, as suggested by B. Clay Shannon
    //The Program() method runs before Main() and allows for regisration or placement
    //of files before the program "starts". Placing this line in Main()
    //causes a FileNotFound exception when it tries to register the assembly.
    static Program()
    {
        //The dll has been added as a resource, build action "Content".
        //Note the underscores in "Microsoft_Win32_TaskScheduler"
        //Adding the dll to the resource manager replaces '.' with '_' in the resource name
        File.WriteAllBytes(string.Format("{0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\Microsoft.Win32.TaskScheduler.dll"), FindResourceByName("Microsoft_Win32_TaskScheduler"));
    }

    static void Main(string[] args)
    {
        ...
    }

    /// <summary>
    ///   Returns a byte array of the object searched for
    /// </summary>
    /// <param name="objectName">Name of the resource object</param>
    /// <returns>Byte array of the specified resource object</returns>
    private static byte[] FindResourceByName(string objectName)
    {
        object obj = Properties.Resources.ResourceManager.GetObject(objectName);
        return ((byte[])(obj));
    }
}
Community
  • 1
  • 1
Foxtrek_64
  • 357
  • 5
  • 14