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):
- The application (EXE) directory or LoadLibraryEx() alternate order
- SetDllDirectory()¹ or if NULL and SafeDllSearchMode² is OFF the GetCurrentDirectory()
- GetSystemDirectory() folder (eg: C:\Windows\System32)
- The 16-bit system directory (eg: c:\Windows\System)
- GetWindowsDirectory() folder (eg: C:\Windows)
- If SafeDllSearchMode² is ON, GetCurrentDirectory()
- 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:
- Call SetCurrentDirectory() and SetDllDirectory() to the Java bin folder
- Call LoadLibrary() on the full path to the jvm.dll
- 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?
|
|