|
|
Java 6 and msvcr71.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 were reporting
that msvcr71.dll was not found:
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 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 places a java.exe into the Windows system32 folder. So Sun should
be following its own advice and place a msvcr71.dll next to 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.
There are many clues that point to the fact that just BEFORE a
LoadLibrary("<path>\jvm.dll") that Sun's java.exe is performing a
LoadLibrary("<path>\jvm.dll\..\..\msvcrt71.dll"). I can understand
why Sun does not publish this fact. It is pretty hard core to preload a DLL by specific
name because that will break in the future. Sun uses it because they assume they can keep
the java.exe in the system32 folder in sync with the version of Java that is installed
on the computer.
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 certainly is not 'future proof'. EXE's are breaking
now. Sun's fix is to copy msvcr71.dll next to the EXE (which Sun does not do for their java.exe).
But in the future, when Sun moves on to another msvcr##.dll version, your EXE will still work, right?
NO. 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.
The Issue: We want LoadLibrary() on the JVM to work on the Java 6 VM without
moving msvcr71.dll next to the application EXE. Can that be done? After all, msvcr71.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 msvcr71.dll on the system? Finding
another msvcr71.dll on the computer (like windows system32) is very likely just fine,
but we want to be 100% safe and use the one in the Java installation folder.
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 LoadLibary is used, the directory search order 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 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 first.
Technically, we need to force the msvcr71.dll located in the Java bin
folder to be found first and used.
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 they depend
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, and now Java 1.6 hopefully common sense will
prevail and Sun will continue to keep the layout the same and all dependent DLL's in the
'bin' folder.
Sun, are you listening?
|
|
Copyright © 2000-2009 Duckware
|
|