Duckware
 You are here: Duckware » Technology » Java 6 and msvcr71.dll   Contact us  
Java 6 and msvcr71.dll
(Java 7/8 and msvcr100.dll)


Java 6 Missing msvcr71.dll problem: Duckware produces a Java EXE packager, JexePack, and after Java 6 was released I started to get complaints from customers that generated EXE's (that worked just fine under Java 5) were failing under Java 6 and reporting that msvcr71.dll was not found:
But my Java EXE packager does not use msvcr71.dll, java does! A quick search of Google turned up this relevant content:
  • Sun's Java 6 web notes which states "SE 6 applications that use custom launchers must be installed with msvcr71.dll in the same directory as the launcher executable"
  • Java Bug #6509291 - "Launching java using the jvm.dll no longer works without msvcr71.dll in the system path"
Sun's advice: Is Sun right? Must an EXE that launches the JVM include msvcr71.dll in the EXE directory? Sun could have statically linked to the C runtime and totally eliminated the requirement for msvcr71.dll, but Sun chose not to. Sun could have created a stub JVM loader DLL, which loads needed DLLs like msvcr71.dll and calls the real Java VM, but Sun chose not to. Instead of fixing the problem once for all Java developers world wide, Sun decided to push the problem out to be fixed by all Java developers. Typical Sun attitude that I have seen first hand for over 8 years.

Worse yet, Sun does not follow their own advice: Sun places a java.exe into the Windows system32 folder. So Sun must be following its own advice and place a msvcr71.dll next to that java.exe, right? No, they don't. So, when java.exe is run from there, it will fail, right? No, it works. So what is going on? Clearly Sun's java.exe is not following their own advice. Even stranger still is that when there IS a msvcr71.dll right next to java.exe in the system32 folder, it is not used. The msvcr71.dll in the Java installation bin folder is used instead. That seems to violate how LoadLibrary() works.

The bottom line: Sun's points to a Microsoft article for the reason why they are telling us to copy msvcr71.dll into the same directory as the EXE. That advice is not very good (and something Sun itself does not follow for their own java.exe), and certainly is not 'future proof'. EXE's are breaking now. Sun's fix is to copy msvcr71.dll next to the EXE. But in the future, when Sun moves on to another msvcr##.dll version, your EXE will still work, right? NO. Your 'fixed' app works just fine now, but when Java 7 ships and requires msvcr100.dll, your application will once again break. Sun has learned nothing from this experience because they are recommending a course of action where they know that EXE's will once again break in the future when a new Java VM is shipped.

The Issue: We want LoadLibrary() on the JVM to work on the Java VM without moving any msvcrXXX.dll next to the application EXE. Can that be done? That is what Sun is doing with their java.exe! After all, msvcrXXX.dll is needed by the Java VM, not the EXE. Under Windows, you can use LoadLibrary() to load the Java VM. But how do you force msvcr71.dll, located in the Java bin folder, to be found and used before any other msvcrXXX.dll on the system? Finding another msvcrXXX.dll on the computer (like windows system32) is very likely just fine, but we want to be 100% safe and use the one that Java ships in the Java installation folder (just like Sun's java.exe does).

Strange enough, on all of my XP and Vista machines, there in the Windows System32 folder is msvcr71.dll. How did it get there? On one of the XP machines, I renamed msvcr71.dll, restarted Windows, and upon startup, an Adobe component complained about msvcr71.dll not being found. I then tried to run Adobe Reader, and it reinstalled itself on the fly, recovering from the 'lost' DLL.
DLL directory search order: When Windows LoadLibary() is used, the directory search order (for dependant DLLs) is (reference):
  1. The application (EXE) directory or LoadLibraryEx() alternate order
  2. SetDllDirectory()¹ or if NULL and SafeDllSearchMode² is OFF the GetCurrentDirectory()
  3. GetSystemDirectory() folder (eg: C:\Windows\System32)
  4. The 16-bit system directory (eg: c:\Windows\System)
  5. GetWindowsDirectory() folder (eg: C:\Windows)
  6. If SafeDllSearchMode² is ON, GetCurrentDirectory()
  7. Directories in the PATH environment variable
¹SetDllDirectory is new to Windows XP SP1 and Windows Server 2003
²SafeDllSearchMode is ON by default on Windows XP SP2, Server 2003, Vista
One Possible Easy Solution: We can easily modify the PATH to include the Java bin folder via GetEnvironmentVariable() and SetEnvironmentVariable() before calling LoadLibrary(). It works! But, what if there is a msvcr71.dll in the Windows system32 folder? It would be found and used by the Java VM first. Technically, we need to force the msvcr71.dll located in the Java bin folder to be found first and used (like java.exe).

Strategy: As can be seen from the first search step above, if all of Sun's Java DLL's were in the Java bin folder, the best option would have been to simply use LoadLibraryEx() with the alternate search strategy. But this will not work since the JVM.DLL is in a 'client' or 'server' sub-folder of the Java 'bin' folder. That leaves the second search step above and SetDllDirectory(). But since SetDllDirectory() is a new Windows function, we also need the backup method of GetCurrentDirectory() to work on older Windows computers. So...

The Final Solution: The final solution, which will work work no matter what DLL dependencies Sun adds to the Java VM in the future, provided Sun does not change the 'bin' folder layout is:
  1. Call SetCurrentDirectory() and SetDllDirectory() to the Java bin folder
  2. Call LoadLibrary() on the full path to the jvm.dll
  3. Call SetCurrentDirectory(old-dir) and SetDllDirectory(NULL)
If there is a msvcr71.dll in the Java bin folder, it will be found and used first. Otherwise, if there is one in the windows system32 folder, it will be found and used. Note that SetDllDirectory() only exists on XP SP1 (and later), so dynamically bind to the function (and use only if found) for compatibility on older versions of Windows.
// Dynamic binding to SetDllDirectory()
typedef BOOL (WINAPI *LPFNSDD)(LPCTSTR lpPathname);
HINSTANCE hKernel32 = GetModuleHandle("kernel32");
LPFNSDD lpfn = (LPFNSDD)GetProcAddress(hKernel32, "SetDllDirectoryA");
Please note that this solution relies upon Sun properly placing any DLL's that their Java VM depends upon into the 'Java installation bin folder'. If Sun changes the folder layout, they will obviously break things once again. Since the 'bin folder' layout with client/server/hotspot sub folders applies to Java 1.3, 1.4, 1.5, 1.6, and now Java 1.7, hopefully common sense will prevail and Sun/Oracle will continue to keep the layout the same and all dependent DLL's in the 'bin' folder.

Sun, are you listening?

Oracle, are you listening?


Copyright © 2000-2024 Duckware