none
What else in order to make SetDllDirectory work in a .NET 4.0 app? RRS feed

  • Question

  • Hi all,

    I'm calling SetDllDirectory in order to ensure that the Firebird 32/64 bit DLL (plain, old DLL, not an assembly) is on the search path for my WinForms application at runtime.  Note that I can't hardcode the path containing the DLL and will only know it at runtime, as it depends on whether I'm currently a 32 or 64 bit application.  I do not want to install different versions of the app for 32 or 64 bit platforms.

    At app startup (in Main(..)) I'm calling SetDllDirectory via P/Invoke with the directory containing the DLL.  Everything is returning ok, but when it comes time to load the DLL, I'm getting errors that the DLL can't be found:  "Unable to load DLL 'fbembed': The specified module could not be found. (Exception from HRESULT: 0x8007007E)"

    How do I see what the plain old DLL search path is in .NET?  Is it the same as the assembly probing path?  What else do I have to do to ensure that SetDllDirectory actually works as advertised (http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx)?

    Thanks,
    Matthew

    Monday, January 17, 2011 9:07 PM

Answers

  • I also use P/Invoke with following code snippet, dlls used below are all .NET assembly:

        class Program

        {

            [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

            [return: MarshalAs(UnmanagedType.Bool)]

            static extern bool SetDllDirectory(string lpPathName);

     

            [DllImport("kernel32", SetLastError = true)]

            static extern IntPtr LoadLibrary(string lpFileName);

     

            static void Main(string[] args)

            {

                try

                {

                    string dll2Path = @"D:\Projects\VS10\ConsoleApplication7\ClassLibrary2\bin\Debug";

                    string dll3Path = @"D:\Projects\VS10\ConsoleApplication7\ClassLibrary3\bin\Debug";

     

                    SetDllDirectory(dll2Path);

                    //SetDllDirectory(dll3Path);

     

                    Console.WriteLine("-------ClassLibrary1.dll-------");

                    IntPtr ptr = LoadLibrary("ClassLibrary1.dll");

                    if (ptr == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary1 loaded");

                    }

     

                    Console.WriteLine("-------ClassLibrary2.dll-------");

                    IntPtr ptr2 = LoadLibrary("ClassLibrary2.dll");

                    if (ptr2 == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary2 loaded");

                    }

     

                    Console.WriteLine("-------ClassLibrary3.dll-------");

                    IntPtr ptr3 = LoadLibrary("ClassLibrary3.dll");

                    if (ptr3 == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary3 loaded");

                    }

                }

                catch (Exception e)

                {

                    Console.WriteLine(e.Message);

                }

                finally

                {

                    Console.ReadLine();

                }

            }

        }

     


    Eric Yang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by eryang Monday, February 7, 2011 2:47 AM
    Friday, January 21, 2011 4:26 AM

All replies

  • The behavior that I'm seeing is that for each directory you want to search, you have to invoke LoadLibrary immediately after calling SetDllDirectory.  This is the only way that I can make it work.  So, if you have multiple directories on the path you want to search, you have to loop through each directory and call SetDllDirectory(dir) then LoadLibrary(dll) until it's loaded or you've exhausted your path.

    I can't find documentation on this consecutive call requirement.  Is this the expected behavior?

    Thanks,
    Matthew

    Monday, January 17, 2011 9:56 PM
  • I think the SetDllDirectory call is unnecessary. You can just call LoadLibrary with the full path of the dll you want to load.
    Mattias, C# MVP
    Tuesday, January 18, 2011 10:01 AM
    Moderator
  • That much I know.

    My use case was that I'm depending on a .NET assembly that loads a DLL, and I have to make sure that the directory containing the DLL is on the path at the time the assembly calls LoadLibrary.  Because the assembly can't know at build time the full path of the DLL, I must ensure that the directory containing the DLL that the assembly depends on is on the path.  I wanted to just add the path at my application's startup, calling SetDllDirectory then, but SetDllDirectory doesn't seem to support that kind of use.

    Thanks anyway,
    Matthew

    Tuesday, January 18, 2011 10:42 PM
  •  

    My test result shows that the SetDllDirectory function will take effect until I invoke this function again with a different path string; and new path string will overwrite old path string, in another words, the later call to SetDllDirectory will invalid previous call.

     

    Agree with Mattias, if we know the path string when invoke SetDllDirectory function, we can also compose a full file path to the target dll at runtime, and pass the full path to LoadLibrary function.


    Eric Yang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, January 19, 2011 4:01 AM
  • I wonder if you're testing under the same conditions as I am.  I am invoking SetDllDirectory via P/Invoke in one assembly, then allowing the assembly that I depend on to call LoadLibrary some time later.

    Also, I can't pass the full path to LoadLibrary, because first, I'm not the one calling LoadLibrary, and second, I don't want my assembly to have to know which libraries are needed by the assembly that mine depends on.

    -matthew

    Wednesday, January 19, 2011 5:55 PM
  • I also use P/Invoke with following code snippet, dlls used below are all .NET assembly:

        class Program

        {

            [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

            [return: MarshalAs(UnmanagedType.Bool)]

            static extern bool SetDllDirectory(string lpPathName);

     

            [DllImport("kernel32", SetLastError = true)]

            static extern IntPtr LoadLibrary(string lpFileName);

     

            static void Main(string[] args)

            {

                try

                {

                    string dll2Path = @"D:\Projects\VS10\ConsoleApplication7\ClassLibrary2\bin\Debug";

                    string dll3Path = @"D:\Projects\VS10\ConsoleApplication7\ClassLibrary3\bin\Debug";

     

                    SetDllDirectory(dll2Path);

                    //SetDllDirectory(dll3Path);

     

                    Console.WriteLine("-------ClassLibrary1.dll-------");

                    IntPtr ptr = LoadLibrary("ClassLibrary1.dll");

                    if (ptr == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary1 loaded");

                    }

     

                    Console.WriteLine("-------ClassLibrary2.dll-------");

                    IntPtr ptr2 = LoadLibrary("ClassLibrary2.dll");

                    if (ptr2 == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary2 loaded");

                    }

     

                    Console.WriteLine("-------ClassLibrary3.dll-------");

                    IntPtr ptr3 = LoadLibrary("ClassLibrary3.dll");

                    if (ptr3 == IntPtr.Zero)

                    {

                        Console.WriteLine(Marshal.GetLastWin32Error());

                    }

                    else

                    {

                        Console.WriteLine("ClassLibrary3 loaded");

                    }

                }

                catch (Exception e)

                {

                    Console.WriteLine(e.Message);

                }

                finally

                {

                    Console.ReadLine();

                }

            }

        }

     


    Eric Yang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by eryang Monday, February 7, 2011 2:47 AM
    Friday, January 21, 2011 4:26 AM