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

    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/
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 30

      0
      А почему под маком чудеса не потребовались? Не стали трогать, или произошло невероятное и все само «срослось»?
      • UFO just landed and posted this here
          0
          Про мак будет в следующей.
          +6
          > Новая Java собрана с использованием MSVCR100.dll
          Очевидно, под мак Java не собирается с использованием MS C++ Runtime.
            0
            dll, so и иже с ними — костыль под малые объёмы памяти. Такой же, каким в своё время были оверлеи на 16-битной адресации. Уже лет 7 как пора вернуться к статической линковке.
              0
              О да, я еще помню парад апдейтов, когда в libz вдруг vulnerability нашли. Лучше б экономили память ;-)
                0
                Угу… в либе на 400kb (где 95% — комментарии) одни «вдруг» накосячили, а другие через несколько лет «вдруг» нашли :) Благотворительным студентам из opensource, которые пишут ради искусства, а не ради скучной серой математической выверенности, ничего не поможет. Ни dll, ни so, ни даже jvm.
            0
            Там dlopen() сюрпризов не преподнес.

            Вообще, в деле запуска Java, для мака разницы немного, основных отличий — вот они

            #ifdef WINDOWS
            	#define CORRECT_JNI_VER JNI_VERSION_1_6
            #else //MACINTOSH
            	#define CORRECT_JNI_VER JNI_VERSION_1_4
            #endif
            

            и вот это
            		JavaVMInitArgs vm_args;
            		vm_args.version = CORRECT_JNI_VER;
            		JavaVMOption* options = new JavaVMOption[vmParamCount];
            
            		options[0].optionString = memoryOpt; // -Xmx...
            		options[1].optionString = classpath;
            #ifdef MACINTOSH
            		options[2].optionString = "-Djava.awt.headless=true";
            		options[3].optionString = "-XstartOnFirstThread";
            #endif
            

            –2
            А установить Microsoft Visual C++ 2010 x86 Runtime на компьютер заказчика никак нельзя?
              0
              Уже. Java принесла.
              0
              do  {
               ...
              } while (false);
              


              Какова цель такого оборачивания?
                +6
                break в середине позволяет в любой момент выйти из блока кода.
                  0
                  return.
                    0
                    return не совсем аналогичная замена. После этого блока могут идти еще инструкции
                      +1
                      В ДАННОМ случае замена.

                      В любом случае эмуляция goto в языке, где есть goto, выглядит очень глупо
                      Как будто человек думает, что неприятие наложено на само слово, а не данную управляющую конструкцию.
                        +1
                        Я и не утверждал что это тру вей. Как раз наоборот. Вот тут поподробнее мое мнение о goto и break.
                        Каким бы неправильным не был подход с выходом из блока через break, тем не менее он не эквивалентен return-у. И несколько return-ов это такой-же хак как и то что сделал тс.
                        Просто скорее всего человек уже набил руку на таком подходе, и плюс даже если сейчас у него после этого блока нет инструкций, завтра они могут там появится. Так что не надо спорить, это не ЗАМЕНА, ни в данном случае ни в каком другом случае.
                        +1
                        В данном случае это эквивалент.
                        А лепить цикл вместо GOTO — неудобочитаемо.
                        В данном случае вопрос решается легко ступенчатыми if
                        ИМХО, это наглядней отображает логику алгоритма, а ширина современных мониторов не даст коду «не влезть» в экран.

                        if (...) {
                        
                          if (..) {
                            
                            if (..) {
                        
                            }
                        
                          }
                        
                        }
                        


                          0
                          Мои современные мониторы используются в портретном режиме.
                            0
                            Глаза не болят? Частота же другая, когда поворачиваешь.

                            Очень хотел сделать, как на фото:
                            один 30" посредине (2560x1600)
                            и по бокам два 20" (1200x1600)

                            Быстро отказался от этой идеи, глаза чуть не вылезли. Может настройки надо было где-то подкрутить :)
                              0
                              С частотой все нормально. Проблемы с субпиксельным сглаживанием и углами обзора.
                                0
                                Думаю, что все-таки дело в частоте. Вот ради прикола повернул ноутбук на 90 градусов, а впечатление, будто ЭЛТ-телик мерцающий.
                            –2
                            для начала, египетские скобки крайне неудобочитаемы
                        +5
                        Весьма оригинальный GOTO без GOTO :-) Школу с бейсиком вспомнил.
                        0
                        Стандартный трюк по оборачиванию лестницы условий, по-моему даже свое название имеет. В Adobe SDK все примеры так обернуты, мы решили по стилю не выделяться.

                        Впрочем, еще можно встретить вот такое

                        __try {
                            if(foo) __leave;
                        }
                        __except(...) {
                        
                        }
                        


                        В MS DDK встречается в примерах.
                          0
                          Уж лучше честно писать goto, чем городить такое.
                            0
                            Так это замена не goto, и не замена пачке if.

                            Я ж специально более понятный пример привел — это имитация try/catch в том случае, когда не допускается использование родного.

                            Например — когда собирается проект с флажками -fno-exceptions -fno-rtti. Или у микрософта — без флажка -EHsc. Или нижележащий фреймворк принципиально не знает про throw.

                            Поймите правильно — я совершенно не в восторге от такого трюкачества. Но — приходится.
                        +2
                        Какая цель приследовалась при переходе на Java 7? В ней конечно есть некоторые фишечки, но не так уж и много и не такие уж и фундаментальные…
                          +1
                          Поддержка OpenType font collections. Наконец-то.
                          0
                          А можно полюбопытствовать, какой юзкейс решает связка Adobe InDesign Server CS5 + встраиваемая Java?
                            +2
                            SVG диаграммы в документы вставлять в одном автоматизированном решении. Это было проще, чем конвертировать в растр и потом сражаться с непопаданием в pantones. «А у вас наш шестикомпонентный цвет на печати выглядит как россыпь точек — вы не попали в заданный CMYK color set!»

                            Поэтому взяли Apache Batik и дали ему нарисовать картинку на IGraphicsPort.

                            [irony]Так что уменьшили энтропию, сотня бангалорских индусов больше не выравнивает тысячи диаграмм в фотошопе каждый квартал.[/irony]

                            К слову, когда решали обратную задачу (нарезали InDesign документы в стопку одностраничных SVG) — использовали тот же подход в виде Apache PDFBox ;-)

                          Only users with full accounts can post comments. Log in, please.