Page 1 of 1

OpenLibrary & windows\System32 directory

Posted: Wed Dec 05, 2007 10:06 pm
by Motu
I had a strange bug concerning Librarys and it was taking a long time until I found the problem.

Code: Select all

; Dll Function
ProcedureDLL XXL()
 MessageRequester("Old","",0)
EndProcedure
Do the following steps:

1. Compile the DLL procedure to a DLL called Message.dll
2. copy this file to the Windows\System32 directory
3. Replays the word "Old" with "New"
4. compile a second dll also called message.dll

Now write this program in the folder of the DLL (not the system32 directory of course!)

Code: Select all

; Program
Lib = OpenLibrary(#PB_Any,"Message.dll")
CallFunction(Lib,"XXL")
CloseLibrary(Lib)
compile the program and see: It will display "old". But it should not. Openlibrary should at first open the library from the program folder, not from the system32 folder.
Now create a .exe from the program code and run it. "new". Something seemse to be wrong here (either in PB or in Windows was to handle Dll calling).

Posted: Wed Dec 05, 2007 10:26 pm
by ABBKlaus
did you compile your test program to an exe or did you just hit comile from the ide :?:

Dynamic-Link Library Search Order
http://msdn2.microsoft.com/en-us/library/ms682586.aspx

Posted: Thu Dec 06, 2007 1:13 pm
by Motu
I did both (as written in the text) and both lead to different results.

your link simple says that this bahavior is wrong as well:

1. The directory from which the application loaded.

2. The current directory. <- Here even the simple compiled version should go to

3. The system directory. Use the GetSystemDirectory function to get the path of this directory.

Posted: Thu Dec 06, 2007 1:35 pm
by freak
Its all up to the OS. PB just calls LoadLibrary() and thats it.

Re: OpenLibrary & windows\System32 directory

Posted: Thu Dec 06, 2007 1:41 pm
by PB
> Lib = OpenLibrary(#PB_Any,"Message.dll")

Just avoid the problem totally by putting the DLL in your app's folder and
calling it from there. This is how I'd do the above:

Code: Select all

myappdir$="C:\Blah\Blah\Blah\"
Lib = OpenLibrary(#PB_Any,myappdir$+"Message.dll")
Problem solved. You're the coder. Tell your app where to find it, instead of
letting Windows do it. IMO.

Posted: Thu Dec 06, 2007 1:43 pm
by tinman
Motu wrote:I did both (as written in the text) and both lead to different results.
When you compile from the ide you usually get an executable created in the "Compilers" directory. So the directory for (1) would be e.g. "C:\Program Files\PureBasic\Compilers".

The compiler runs your executable (I think) so the current directory (2) would also be the "Compilers" directory. That is why when you run from the IDE your application does not find the "new" DLL.

I don't know if this has changed with PB 4.1, I guess it has because it is Vista compatible and you probably cannot write to the "Compilers" directory any more. In that case, it will be using your temporary directory. But the result is the same, the DLL will not be there.

(Maybe the DLL would appear there if you also run that source code from the IDE?)

Re: OpenLibrary & windows\System32 directory

Posted: Thu Dec 06, 2007 2:08 pm
by Trond
Try this to see where the exe is and what the current directory is:

Code: Select all

Debug GetCurrentDirectory()
Debug GetPathPart(ProgramFilename())
If the loaded dll actually IS from the windows directory it probably isn't in those directories.

Posted: Thu Dec 06, 2007 4:31 pm
by Motu
Thanks for all your replys :)

@Trond & tinman GetCurrentDirectory() returns the path that the application code is in, so that cannot be the problem.

@PB I know that is is a solution, anyway, for the problem I have this does not work. I call functions from the oggvorbisfile.dll. The dll it's self is calling the two dlls ogg.dll and vorbis.dll. But when they exist inside the system32 folder as well, these versions are loaded as well (and they may not be of the same version then the one that I need).

I guess I found the problem:

Windows XP and Windows 2000 SP4: Safe DLL search mode is disabled by default. To enable this feature, create the SafeDllSearchMode registry value and set it to 1.

If SafeDllSearchMode is enabled, the search order is as follows:

The directory from which the application loaded.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The current directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key.

If SafeDllSearchMode is disabled, the search order is as follows:

The directory from which the application loaded.
The current directory.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key.

So maybe the SafeDLLSearchMode is enabled somehow on my computer...

a workaround that works is to set the "Create temp exe in source dir" flag - but

PLEASE!!! PB TEAM move this option to the compiler option screen. You have to set it for all(!) include file and if you forget one you get strange errors when compiling from this include file...

Posted: Thu Dec 06, 2007 5:01 pm
by tinman
Motu wrote:So maybe the SafeDLLSearchMode is enabled somehow on my computer...
It's enabled by default in XP SP2, so that's perhaps why.

Posted: Tue Dec 11, 2007 1:29 pm
by Motu
Okay, thanks for the information @tinman.

there is a function call "SetDllDirectory" found in the Kernel32.dll
http://msdn2.microsoft.com/en-us/library/ms686203.aspx

with this little trick everyone else having this problem can solve it :)

Code: Select all

 Lib_Kernel32 = OpenLibrary(#PB_Any,"Kernel32.dll")
 *SetDllDirectoryA = GetFunction(Lib_Kernel32,"SetDllDirectoryA")
 If *SetDllDirectoryA <> 0
  String.s = GetCurrentDirectory()
  CallFunctionFast(*SetDllDirectoryA,@String)
 EndIf
CloseLibrary(Lib_Kernel32)

Uhh - no

Posted: Tue Dec 11, 2007 1:44 pm
by Motu
painfully it does not solve the problem. Anyone has a idea why?

Re: Uhh - no

Posted: Tue Dec 11, 2007 1:59 pm
by tinman
Motu wrote:painfully it does not solve the problem. Anyone has a idea why?
Seems like your code should work. Have you tried checking the value of GetLastError() after calling the function?

Edit: this code works for me. I used SetDllDirectoryW because I use unicode by default:

Code: Select all

Lib_Kernel32 = OpenLibrary(#PB_Any,"Kernel32.dll") 
*SetDllDirectory = GetFunction(Lib_Kernel32,"SetDllDirectoryW") 
 If *SetDllDirectory <> 0 
  String.s = GetCurrentDirectory() 
  Debug String
  result = CallFunctionFast(*SetDllDirectory,@String) 
  If result =0
    Debug Str(GetLastError_())
  EndIf
EndIf 
CloseLibrary(Lib_Kernel32)

If OpenLibrary(0, "dll_path.dll")
    CallFunction(0, "foo")
    CloseLibrary(0)
Else
    Debug "Could not find DLL"
EndIf

Posted: Tue Dec 11, 2007 7:31 pm
by ABBKlaus
you could have set to an invalid path ?

Code: Select all

Lib_Kernel32 = OpenLibrary(#PB_Any,"Kernel32.dll") 
If Lib_Kernel32
  CompilerIf #PB_Compiler_Unicode
    *GetDllDirectory = GetFunction(Lib_Kernel32,"GetDllDirectoryW")
    *SetDllDirectory = GetFunction(Lib_Kernel32,"SetDllDirectoryW")
  CompilerElse
    *GetDllDirectory = GetFunction(Lib_Kernel32,"GetDllDirectoryA")
    *SetDllDirectory = GetFunction(Lib_Kernel32,"SetDllDirectoryA")
  CompilerEndIf
  If *SetDllDirectory
    String.s = GetCurrentDirectory();+"makethepathinvalid"
    Debug String 
    result = CallFunctionFast(*SetDllDirectory,@String) 
    If result =0 
      Debug Str(GetLastError_()) ; note : an invalid path does not show up as error !
    EndIf 
  EndIf
  If *GetDllDirectory
    String.s=Space(#MAX_PATH)
    result = CallFunctionFast(*GetDllDirectory,#MAX_PATH,@String) 
    If result =0 
      Debug Str(GetLastError_()) 
    Else
      Debug String
    EndIf 
  EndIf
  CloseLibrary(Lib_Kernel32) 
EndIf

libname$="foo.dll"

Lib=OpenLibrary(#PB_Any,libname$)
If Lib
  String=Space(#MAX_PATH)
  hModule=GetModuleHandle_(libname$)
  If GetModuleFileName_(hModule,@String,#MAX_PATH)
    Debug String
  Else
    Debug Str(GetLastError_())
  EndIf
Else
  Debug "Lib not found"
EndIf