Однажды, в студеную зимнюю пору, в одно старое приложение на С++, успешно работавшее до этого годами, пришлось вставить новый Java runtime 7 вместо отлично работавшего Java 6. Ничто не предвещало, и вдруг.
Код в общем-то очевиден и прост — немного JNI, и вот он — больной зуб. Приведу код только для Windows, т.к. чудеса эквилибристики под маком не потребовались.
Как это было
Когда-то, много лет назад, мы создали простенькую DLL, слинкованную с jvm.dll стандартным способом, экспортировали в ней две с половиной функции, и «дергали» ее динамически. Сей финт ушами очевиден — нам ведь еще надо найти где сама Java расположилась. Вызывали эту простенькую proxy примерно так
bool LoadJavaEngine(HANDLE& engHandle)
{
bool loadResult = false;
do
{
... // here we have auto variables, nothing interesting
// first we need to find java
CRegKey rk;
if(rk.Open(HKEY_LOCAL_MACHINE,
L"SOFTWARE\JavaSoft\Java Runtime Environment\1.6",
KEY_QUERY_VALUE) !=
ERROR_SUCCESS || rk.QueryValue(strBuf, L"RuntimeLib", &strBufSz) != ERROR_SUCCESS) break;
WCHAR* backslashAddr = wcsrchr(strBuf, L'\');
if(backslashAddr) BufLen = backslashAddr - strBuf;
strBuf[BufLen] = L'';
// now C runtime knows where is jvm.dll located
retnPWD = SetDllDirectoryW(strBuf);
// ... nothing intersting - in the same manner we're looking for our proxy path in registry
if((engHandle = LoadLibrary(strBuf))==NULL) break;
// now try to initialize JVM by calling our proxy linked with jvm.dll
CreateJavaVMFunPtr CreateJavaVM =
(CreateJavaVMFunPtr)GetProcAddress(engHandle, "CreateJavaVM");
if(CreateJavaVM) loadResult = CreateJavaVM(needJNIProxy);
}
while(false);
return loadResult;
}
Неожиданные грабли на ровном месте
Как видно, код чрезвычайно прост и в общем-то занимает аж три строчки
SetDllDirectory
LoadLibrary
GetProcAddress
А теперь — усложняем. С Java 6 переходим на Java 7. Оптимистичный прогноз был таким — поменяем одну строчку, и все работает.
Меняем
if(rk.Open(HKEY_LOCAL_MACHINE,
L"SOFTWARE\JavaSoft\Java Runtime Environment\1.6",
на
if(rk.Open(HKEY_LOCAL_MACHINE,
L"SOFTWARE\JavaSoft\Java Runtime Environment\1.7",
и наступает знаменитый литовский праздник «обломайтис» Path not found. Не желает загружать нашу простенькую proxy.
Сеанс телепатии
В ходе полуторачасовых разбирательств, выяснилось:
- Предыдущая версия основной программы и наш плагин на java к ней были собраны вижуалстудиями от 2003 до 2008 (MSVCR80.dll, MSVCR90.dll и т. д.), а Java 6 соответственно — раритетом с MSVCR71.dll.
- Новая Java собрана с использованием MSVCR100.dll (ага, ага). А версия программы у заказчика — использует MSVCR90.dll. Вот и не загружает.
Гугление вариантов не дало — прописывание и удаление манифестов дало ноль на массу с тем же результатом.
Камлание на бубне
И тогда был вытащен из кармана большой напильник, и родили очередное чудовище подпорок. И вместо одной SetDllDirectory получилось вот что:
wcscat(strBuf, L"\msvcr100.dll");
test = LoadLibraryW(strBuf);
strBuf[BufLen] = L'';
wcscat(strBuf, L"\client\jvm.dll");
test = LoadLibraryW(strBuf);
if(test == 0) {
strBuf[BufLen] = L'';
wcscat(strBuf, L"\server\jvm.dll");
test = LoadLibraryW(strBuf);
}
strBuf[BufLen] = L'';
retnPWD = SetDllDirectoryW(strBuf);
То есть если руками подгрузить dependencies начиная от MSVCR100.dll — то находит и работает. А иначе — увы.
Disclaimer: вариант «а вы поставьте заказчику версию поновее» не подходит. Основное приложение — это Adobe InDesign Server CS5, за $7k за лицензию.
Подробности про колючую проволоку можно прочитать в предыдущих публикациях на тему
http://habrahabr.ru/post/122746/
http://habrahabr.ru/post/127574/
Автор: viklequick