7

I want to temporarily add a directory to the DLL search paths - is there a correct way to do this under Windows 7?

Scenario

I've got a C# application, let's call it WonderApp.

WonderApp needs to call a C++ DLL, located in C:\MyPath. So as part of WonderApp's Program.Main(), I added the following command:

Environment.SetEnvironmentVariable("PATH",
   "C:\\MyPath;" + Environment.GetEnvironmentVariable("PATH"));

According to this article, adding a directory to the PATH should also add it to the directories search for DLLs.

The solution works fine in Windows XP: if I add the directory to the PATH, the DLL loads and the program works just fine. If I don't add the directory, the DLL doesn't load, failing with a "not found" error.

However, this doesn't work for Windows 7.

So I figured, let's try using SetDllDirectory(). Like this:

[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);

And, later on:

 bool success = SetDllDirectory(Util.Paths.GetApplicationDataDir());

The value of success is true, but the DLL still fails to load.

Finally, if I set the PATH to include C:\MyPath manually, before running the application - it all works! The DLL loads, and runs just fine.

So, to re-iterate:

Is there a correct way to temporarily add a directory to the DLL search paths under Windows 7?

UPDATE: Using Process Explorer, I checked the application's run-time Environment, and "C:\MyPath" was indeed in the PATH! Furthermore, I saw that Helper.dll was in the list of open handles (as a DLL, not just a file) - and it still claimed not to find it.

Shalom Craimer
  • 20,659
  • 8
  • 70
  • 106
  • Is this the 64-bit version of Windows? What is the real path name? – Hans Passant Jun 08 '10 at 15:04
  • This is 32-bit Windows 7 Home. And the full path to the DLL is C:\MyPath\Helper.dll – Shalom Craimer Jun 09 '10 at 06:37
  • maybe other dlls are missing try loading helper.dll in program "depends.exe" and check for dependencies of other dlls. – OlimilOops Jun 09 '10 at 10:52
  • That doesn't explain why adding the directory to the PATH before running the application, lets the DLL load. – Shalom Craimer Jun 09 '10 at 21:25
  • Hmmm, ... I'm using Win 7 x64 and I'm doing the same (set the PATH for a DllImport in a specific directory) and it works well. – ulrichb Dec 11 '10 at 01:27
  • It's possible that because I'm letting C# handle the dllimport, it's happening before I can change the PATH of my program (which I'm doing from within the same program). Are you changing the PATH before running your program, or while the program is already running? – Shalom Craimer Dec 11 '10 at 17:32

3 Answers3

2

You can add custom DLL loading logic to a C# app using the 'AssemblyResolve' event.

This page has a good summary, with code samples: http://support.microsoft.com/kb/837908

Just as you did, I have found that changing the PATH environment variable of a running C# app does not affect the DLL search behaviour. Perhaps the AppDomain caches the PATH value at startup? You can use the AssemblyResolve event to work around this.

See also How to add folder to assembly search path at runtime in .NET?

Community
  • 1
  • 1
Rich
  • 15,048
  • 2
  • 66
  • 119
  • That's *awesome*!!! Thank you so much for letting me know! This sounds like it would work on any .NET assembly, but I need to test this also for non-.NET DLLs. – Shalom Craimer Aug 23 '11 at 04:53
  • 1
    OK, bad news: It works only for .NET assemblies. Which makes perfect sense, considering its name. It's a wonderful solution, but I can't use it, since the DLLs I need to load are not .NET assemblies. (And to make matters worse, those DLLs also need to load other DLLs, that are located in the same directory with them, so I'd need to find a way to get them to search in there, too). – Shalom Craimer Aug 23 '11 at 05:19
1

I am thinking it has to do with permission problems.

Try turning off UAC and running your code again. Check to see if updating the path worked.

If it did, at least you know where to start...

MQS
  • 413
  • 2
  • 6
  • 18
0

My solution is simple, but I feel ridiculous resorting to it.

I've written another assembly, "Shell", that modifies the Environment, runs WonderApp, and exits.

By modifying the PATH before running the main application (WonderApp), the main application's DLL search-path includes the directories added to the modified PATH.

It looks like this:

namespace shell
{
   static class program
   {
      [dllimport("kernel32.dll", charset = charset.auto, setlasterror = true)]
      public static extern bool setenvironmentvariable(string lpname, string lpvalue);

      private static string joinargstosinglestring(string[] args)
      {
         string s = string.empty;
         for (int i = 0; i < args.length; ++i)
         {
            if (!string.isnullorempty(s))
            {
               s += " ";
            }
            s += "\"" + args[i] + "\"";
         }
         return s;
      }

      [stathread]
      static void main(string[] args)
      {    
         string pathbefore = environment.getenvironmentvariable("path");
         string wewant = util.paths.getapplicationdatadir() + ";" + pathbefore;
         setenvironmentvariable("path", wewant);

         Process process = Process.Start(".\\WonderApp.exe", joinArgsToSingleString(args));
      }
   }
}

I wish I could find a better solution!

Shalom Craimer
  • 20,659
  • 8
  • 70
  • 106