Существуют ли причины, по которым перенаправление DLL не будет работать, кроме наличия манифеста? - PullRequest
6 голосов
/ 03 августа 2010

У нас есть устаревшее приложение VB6, которое использует Crystal Reports XI для создания печатных отчетов. Опыт показывает, что механизм печати Crystal Reports дает сбой, если он обнаруживает неверную версию usp10.dll (библиотека Windows Uniscribe).

Один клиент постоянно испытывает проблемы с печатью на своих компьютерах с Windows 7 (под управлением Windows 7 Enterprise, 32-разрядная версия). Однако у нас есть несколько других клиентов, использующих различные выпуски Windows 7, у которых нет проблем.

На одной из машин, у которой были проблемы с печатью, я заметил, что в папке C:\Program Files\Common Files\Microsoft Shared\Office10\ была более старая версия usp10.dll (несовместимая с Crystal Reports XI). Я не уверен, какое приложение установило эти файлы, потому что у клиента не установлен Office 2002 (поэтому я предполагаю, что другое приложение установило их). Однако я временно переименовал файл, и наше приложение смогло правильно печатать, поэтому кажется, что наше приложение изначально загружало эту версию файла, что вызывало сбои.

Сбой происходит только в тот момент, когда пользователь пытается распечатать отчет. Наше приложение напрямую зависит от craxdrt.dll (библиотека времени выполнения Crystal Reports ActiveX Designer) и crviewer.dll (библиотека средства просмотра отчетов Crystal ActiveX), и происходит сбой, если мы печатаем непосредственно через craxdrt.dll или через элемент управления Report Viewer.

В прошлом мы решали эту проблему, копируя известную исправную версию usp10.dll в каталог нашего приложения и создавая файл .local для включения перенаправления DLL. На сайте клиента я попробовал это, а также попробовал альтернативный подход - создать папку .local для нашего EXE и поместить туда usp10.dll , но ни один подход не работал машина, к которой я был подключен.

Я заметил, что usp10.dll - это «известная» DLL в Windows (в ней есть запись в HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\KnownDLLs), но я протестировал наше приложение на другом компьютере с Windows 7 (под управлением Professional Edition, 32-разрядная версия), в которой также была указана библиотека DLL в реестре как известная библиотека DLL, и, используя Dependency Walker , я мог видеть, что перенаправление работает на этом компьютере. Это несколько сбивает с толку, так как документация Microsoft гласит, что известные DLL не могут быть перенаправлены. Кроме того, как я и подразумевал в заголовке вопроса, наш основной EXE-файл не использует файл манифеста (в документации Microsoft указано, что наличие манифеста, встроенного или автономного, отключает перенаправление DLL).

Итак, мой вопрос: есть ли другая причина, по которой перенаправление DLL будет работать на некоторых компьютерах, а не на других, и имеет ли это какое-либо отношение к различиям между Windows 7 и Windows XP? Я рассмотрел вопрос об удалении всего в разделе реестра KnownDLLs, но поскольку перенаправление работало на машине с таким же набором KnownDLLs, я не уверен, что это действительно решит проблему, и я не хочу удалить этот ключ, если мне не нужно. У меня еще не было возможности снова подключиться к машине клиента, чтобы запустить Dependency Walker , но я не уверен, что смогу интерпретировать его журналы в любом случае (даже на машине, где он работал Я видел много LoadLibrary вызовов usp10.dll , указывающих на папку, отличную от перенаправленной, но некоторые вызовы были, по-видимому, перенаправлены, поэтому я не уверен, что это значит либо).


EDIT : Я должен был также упомянуть, что на каждом проверенном нами компьютере есть еще одна копия usp10.dll в папке System32. Глядя на ответ Криса и в этом блоге Ларри Остермана , объясняющий немного больше о том, как работают известные библиотеки DLL, я понял, что это, вероятно, вообще не учитывает проблему, так как наш программа не загружает копию usp10.dll, которая находится в папке System32.


РЕДАКТИРОВАТЬ # 2 : после игры с Dependency Walker еще немного на моей машине для разработки VB6 (Windows XP SP3), где печать всегда работала, я смог найтинекоторая информация.Я профилировал наше приложение в Dependency Walker и настроил его на запись полных имен путей, и похоже, что одна из зависимостей Crystal Reports (другая DLL-библиотека Crystal Reports) пытается загрузить usp10.dll от нескольких (жестко запрограммированных) путей, прежде чем сдаться и просто запросить его по имени файла.Оказывается, он сначала пытается загрузить его из папки Crystal Reports bin, а затем пытается загрузить из C:\Program Files\Common Files\Microsoft Shared\Office10\usp10.dll.Если он не может найти его ни в одном месте, он просто запрашивает у Windows usp10.dll (который захватит тот в System32).Но даже это не соответствует.Иногда он запрашивает файл в папке Office10, а затем, кажется, игнорирует тот факт, что он не может найти файл, в то время как в других случаях происходит серия вызовов LoadLibrary , где он выглядит какКод Crystal Reports активно ищет альтернативные копии файла в разных местах.Еще более запутанным является то, что, по крайней мере, один из компонентов Crystal Reports выглядит так, как будто на самом деле имеет зависимость времени загрузки от usp10.dll , поэтому компонент всегда получает копию в System32.

Я до сих пор не уверен на 100% , почему перенаправление .local не будет работать в такой ситуации на компьютерах этого клиента, но я думаю, что это частично объясняет, почему у этого конкретного клиента возникают проблемы,поскольку на всех компьютерах с этой проблемой есть папка Office10 с явно несовместимой версией usp10.dll.

Но, опять же, у меня все еще остается основной вопрос: если эти компонентыищите этот файл в самых разных местах, как я могу гарантировать, что все они будут использовать одну и ту же копию?

Ответы [ 4 ]

2 голосов
/ 04 августа 2010

Моя первая мысль: файлы .manifest и все, что они подразумевают, были добавлены в Windows XP в 2002/2003 году. Почему @ # $% делает ваше приложение - и библиотеки, которые ваше приложение использует в этом отношении - не использует эту технологию, чтобы решить эту маленькую «дьявольскую проблему». Это точно сценарий, который они были разработаны, чтобы решить.

Далее, я почти уверен, что «KnownDlls» охватывает только те библиотеки DLL, которые ОС фактически находит в System32. Нахождение DLL в случайных местах (как в папке Office2002), я надеюсь, что, по крайней мере, не пройдет какой-то внутренний тест на проверку работоспособности (is-the-dll-a-real-KnownDlls -андидат). И PATH ищется по системным папкам, так что к тому времени, когда usp10.dll будет найден в ... \ Office10 \ - это не может быть реально известная dll (по определению).

Далее, я также уверен, что файл .local не делает то, что вы думаете. Документация к файлам .local вообще не имеет смысла, потому что все, что она на самом деле говорит, - это порядок поиска dll после применения файла .local, это точно порядок поиска по умолчанию для dll - обычно папка exe всегда ищется ДО системных папок в любом случае.

Единственный раз, когда .local фактически создает потенциальную разницу, это когда приложение использует явный путь для загрузки библиотеки DLL в вызове loadLibrary (или использует флаг измененного пути поиска для LoadLibrary или использует API SetDllSearchDirectory). Во всех случаях, когда exe или dll, выполняющие загрузку, выбирают очень специфическую dll - которую вы, автор приложения, хотите каким-то образом переопределить. Файлы .local не могут (в любом случае, мне) изменить поведение поиска для любых файлов dll, которые указаны только по их имени.

Итак, если usp10.dll установлен в system32, он, вероятно, будет выбран как KnownDll, а затем использован, несмотря на вашу локальную копию (и файл .local). Если это где-то еще по пути, копия в папке вашего exe-файла должна быть использована в любом случае первой - (при условии, что LoadLibrary ("usp10.dll") - это способ загрузки DLL).

Даже если вы приложите все усилия для создания сборки, содержащей ваш заведомо исправный файл usp10.dll, а затем сделаете свое приложение зависимым от этого, я все же думаю, что полный путь, переданный в LoadLibrary, будет нарушать любую логику поиска. вообще - включая поиск dll в списке зависимых сборок.

Итак, ваша проблема меня смущает. Используемый usp10.dll должен быть

  • тот, что в system32, если присутствует (из-за KnownDlls перезаписывает тот, что находится в папке вашего exe)
  • тот, который находится в папке вашего приложения, потому что логика поиска всегда найдет его первым, если KnownDlls пропущен.

если только usp10.dll на самом деле не является коммандой, или в реестре нет полного пути к нему, который потребители используют для загрузки, в этом случае загруженный должен быть:

  • тот, который находится в папке вашего приложения, потому что, похоже, это тот случай, когда могут применяться файлы .local.

Учитывая, что наличие DLL в KnownDLLs препятствует функционированию .local, и учитывая, что dll-отчеты Crystal являются патологическими ...

Все, что я могу предложить, это:

Этот поток содержит функцию PatchIAT - импортируйте ее в свой код.

Перед использованием любых функций в dll Crystal Reports (что заставляет их искать usp10.dll) - вызовите LoadLibrary, чтобы получить указатель на dll, затем вызовите PatchIAT на дескрипторе, чтобы перенаправить вызов dll в LoadLibrary на функция, которую реализует ваш EXE.

В вашей процедуре EXEL LoadLibraryThunk передайте любые вызовы системной LoadLibrary, если только для явного пути к usp10.dll - в этих вызовах - не возвращается код ошибки.

Отключить сглаживание для определенного контекста устройства GDI

1 голос
/ 24 мая 2012

У меня только что была похожая проблема на TS под управлением Server 2008 R2. Ошибка из журнала событий:

Log Name: Application
Source: Application Error
Date: 5/23/2012 10:32:37 AM
Event ID: 1000
Task Category: (100)
Level: Error
Keywords: Classic
User: N/A
Computer: 
Description:
Faulting application name: crw32.exe, version: 11.0.0.1282, time stamp: 0x422d5c77
Faulting module name: usp10.dll, version: 1.420.2600.5969, time stamp: 0x4bc88269
Exception code: 0xc0000005
Fault offset: 0x00014ee4
Faulting process id: 0x1744
Faulting application start time: 0x01cd38f8ce57fbd5
Faulting application path: C:\Program Files (x86)\Business Objects\Crystal Reports 11\crw32.exe
Faulting module path: C:\Program Files (x86)\Common Files\Microsoft Shared\Office10\usp10.dll

Я пытался скопировать usp10.dll из \ Windows \ System32 в C: \ Program Files (x86) \ Common Files \ Business Objects \ 3.0 \ bin. Затем запустил файл crdeploy.reg, но пришлось вручную обновить regkey для включения (x86) из-за 64-битной ОС на сервере. Приложение Crystal Reports по-прежнему не распознает DLL, оно продолжало искать в папке \ Office10. Поэтому я просто переименовал копию библиотеки DLL в этой папке, а затем снова скопировал из \ System32. Это работало как чудо, также версии файлов в DLL были точно такими же, как и Mike Spross.

1 голос
/ 04 августа 2010

Решение было на самом деле довольно простым, но мне потребовалось некоторое время, чтобы понять, почему оно работает.

На клиентском компьютере я скопировал usp10.dll из C:\Windows\System32 (известная версия) в папку C:\Program Files\Common Files\Business Objects\3.0\bin (где установлено большинство компонентов Crystal). Затем я запустил файл crdeploy.reg, который уже присутствовал в папке bin: этот файл добавляет ключ HKEY_LOCAL_MACHINE\SOFTWARE\Business Objects\Suite 11.0\Crystal Reports в реестр и устанавливает значение CommonFiles в C:\Program Files\Common Files\Business Objects\3.0\bin.

Поскольку я не смог подключиться к компьютеру клиента ранее сегодня, я провел еще несколько тестирований этой проблемы на виртуальной машине с Windows 7. Как я упоминал в одном из моих исправлений, на этом компьютере Crystal Reports никогда не просматривал каталог C:\Program Files\Common Files\Business Objects\3.0\bin для usp10.dll , поэтому он немедленно попытался бы загрузить копию в папку C:\Program Files\Common Files\Microsoft Shared\Office10.

Оказывается, когда Crystal Reports вызывает LoadLibrary, он проверяет следующие папки на наличие usp10.dll :

  • Если в реестре присутствует HKEY_LOCAL_MACHINE\SOFTWARE\Business Objects\Suite 11.0\Crystal Reports\CommonFiles, он вызывает LoadLibrary по этому пути.

  • Если раздел реестра отсутствует или usp10.dll не существует в этой папке, Crystal Reports вызовет LoadLibrary с C:\Program Files\Common Files\Microsoft Shared\Office10\usp10.dll в качестве пути.

  • Если файл не найден в папке Office10, он просто передает имя файла (usp10.dll) в LoadLibrary, после чего Windows загружает копию в System32.

Итак, на моей тестовой машине с Windows 7 у меня не было установлен ключ реестра CommonFiles, поэтому Crystal Reports всегда загружал версию usp10.dll , которая была в папке Office10 даже после помещения копии usp10.dll в C:\Program Files\Common Files\Business Objects\3.0\bin. После того, как я установил ключ реестра, чтобы он указывал на правильное место, Crystal Reports загрузил правильную версию файла.

На компьютере клиента в реестре уже был указан путь CommonFiles к нужной папке, но программа установки нашего приложения не устанавливала usp10.dll в эту папку, поэтому он все еще выбирал скопировать копию в папку Office10.

Окончательный обходной путь, предоставленный клиенту, был глупо прост:

  1. Скопируйте версию usp10.dll из System32 в C:\Program Files\Common Files\Business Objects\3.0\bin.

  2. Запустите файл crdeploy.reg в папке bin, чтобы убедиться, что раздел реестра CommonFiles существует и указывает на C:\Program Files\Common Files\Business Objects\3.0\bin.

Первоначально я думал, что помещение копии usp10.dll в папку bin решит проблему на компьютерах клиента, но, как я уже сказал, это не сработало в моем тесте Windows 7 машина, потому что мне не хватало ключа реестра CommonFiles, поэтому я не решался считать выпущенный исправленным.

Кроме того, в случае, если это поможет кому-либо еще, столкнувшемуся с этой проблемой, использовались следующие версии usp10.dll :

  • 1.405.2416.1: это версия в папке Office10, которая вызывает сбой Crystal Reports. При печати отчета нарушение доступа происходит, когда Crystal Reports вызывает одну из функций в usp10.dll (у меня нет оригинальной трассировки стека, но я думаю, что это была функция ScriptApplyDigitSubstitution).

  • 1.626.7600.16385: Это известная хорошая версия, которая корректно работает с Crystal Reports. Похоже, эта версия установлена ​​по умолчанию в Windows 7.

Существуют и другие версии, например, установленные по умолчанию в Windows XP в папке System32, которые также отлично работают с Crystal Reports.

0 голосов
/ 05 августа 2010

Мне не кажется хорошей идеей иметь дополнительные копии, о которых Windows не знает (как они будут обновляться?)

Почему вы не можете назвать LoadLibrary("usp10.dll") самим первым делом при запуске?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...