Pull to refresh

Как я вставлял Java 7 в работающее приложение, и что пришлось изобрести для С runtime

Reading time 3 min
Views 2.6K
image

Однажды, в студеную зимнюю пору, в одно старое приложение на С++, успешно работавшее до этого годами, пришлось вставить новый 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'\0';

		// 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.

Сеанс телепатии


В ходе полуторачасовых разбирательств, выяснилось:

  1. Предыдущая версия основной программы и наш плагин на java к ней были собраны вижуалстудиями от 2003 до 2008 (MSVCR80.dll, MSVCR90.dll и т. д.), а Java 6 соответственно — раритетом с MSVCR71.dll.
  2. Новая Java собрана с использованием MSVCR100.dll (ага, ага). А версия программы у заказчика — использует MSVCR90.dll. Вот и не загружает.


Гугление вариантов не дало — прописывание и удаление манифестов дало ноль на массу с тем же результатом.

Камлание на бубне


image
И тогда был вытащен из кармана большой напильник, и родили очередное чудовище подпорок. И вместо одной SetDllDirectory получилось вот что:

		wcscat(strBuf, L"\\msvcr100.dll");
		test = LoadLibraryW(strBuf);
		strBuf[BufLen] = L'\0';

		wcscat(strBuf, L"\\client\\jvm.dll");
		test = LoadLibraryW(strBuf);
		if(test == 0) {
			strBuf[BufLen] = L'\0';
			wcscat(strBuf, L"\\server\\jvm.dll");
			test = LoadLibraryW(strBuf);
		}

		strBuf[BufLen] = L'\0';
		retnPWD = SetDllDirectoryW(strBuf);

То есть если руками подгрузить dependencies начиная от MSVCR100.dll — то находит и работает. А иначе — увы.

Disclaimer: вариант «а вы поставьте заказчику версию поновее» не подходит. Основное приложение — это Adobe InDesign Server CS5, за $7k за лицензию.

Подробности про колючую проволоку можно прочитать в предыдущих публикациях на тему

http://habrahabr.ru/post/122746/
http://habrahabr.ru/post/127574/
Tags:
Hubs:
+18
Comments 30
Comments Comments 30

Articles