2014-01-22 1 views
1

Я сделал расширение для Thunderbird. Он вызывает (через js-ctypes) написанную мной C++ DLL, которая, в свою очередь, ссылается на другие DLL, которые являются сборками, написанными на C# (существующий код). Если все файлы находятся в том же каталоге, что и исполняемый файл Thunderbird, все работает нормально.DLLs, ищущие библиотеки DLL

Теперь я переместил свои собственные файлы в каталог, который я сделал, чтобы отличать их от файлов Thunderbird. Каталог находится в пути, поэтому моя C++ DLL загружается при вызове. Однако, когда он начинает искать ссылочные сборки, он терпит неудачу.

Procmon показывает, что он ищет только ссылки в каталоге, в котором работает Thunderbird. Не только нет пути, он даже не смотрит в системные каталоги.

Что я могу сделать, чтобы загрузить мои DLL-файлы с его зависимостями, не вбрасывая все в собственную папку Thunderbird, которая, а также быть немного грязной, станет глупой, когда я переношу расширение на другие почтовые программы?

Редактировать: Добавлены выдержки из кода JS.

Из моей функции «init» есть;

this._kernel32 = ctypes.open("kernel32.dll"); 

this._setDLLDir = this._kernel32.declare("SetDllDirectoryA", 
           ctypes.default_abi, 
           ctypes.bool, 
           ctypes.char.ptr); 

var ret; 
ret = this._setDLLDir("C:\\Program Files (x86)\\AuthentStreamAttacher"); 

this._lib = ctypes.open("AttacherC.dll"); 
this._getStr = this._lib.declare("GetPackage", 
         ctypes.default_abi, 
         ctypes.char.ptr); 

this._freeStr = this._lib.declare ("FreePackage", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr);

ret = this._setDLLDir (null);

И где я фактически звоню _getStr и ищут зависимости AttacherC.dll, это is;

var ret; 
ret = this._setDLLDir("C:\\Program Files (x86)\\AuthentStreamAttacher"); 
var str = this._getStr(); 

В каждом случае RET истинно (в соответствии с отладчиком на пошаговом), что предполагает вызов SetDllDirectory успешно. Поведение одинаково, независимо от того, использую ли я версию «A» или «W», в JS ничего нет, просто позвольте мне назвать «SetDllDirectory». Как будто каждый вызов происходит в его изолированном контексте, но в моей DLL «GetPackage» использует malloc для выделения некоторой памяти, которая затем должна быть освобождена в «FreePackage». FreePackage не генерирует исключения, предполагая, что выделенная память сохраняется между двумя вызовами.

Больше нестандартного поведения; если я укажу случайную строку как путь в SetDllDirectory («helloworld» в этом случае) ret is еще true. Таким образом, либо SetDllDirectory фактически не получает строку корректно через ctypes, либо не выполняет никаких проверок на ней.

Мое чувство теперь в том, что каждый вызов js-ctypes происходит в его собственном контексте, в некотором роде, и это расстраивает механизм поиска ассемблера .net, и единственный способ заставить это работать - иметь отдельную родную DLL с одной функцией, вызванной из javascript. Затем он вызывает SetDllDirectory и LoadLibrary в том же контексте, чтобы вызвать следующую оболочку в цепочке, которая затем вызывает мой реальный код C#. Мессия и кажется более склонным к тому, что происходит неправильно, поэтому я надеюсь, что кто-то придет и докажет, что я ошибаюсь?

+0

Есть ли в руководстве по расширению ТБ что-нибудь сказать? Обычно вы вызываете 'SetDllDirectory'. Или используйте привязку времени загрузки. Последнее не очень весело. –

+0

Не то, чтобы я видел. Я использую js-ctypes для вызова моей DLL из JavaScript, так как я потерпел неудачу с XPCOM. Документация js-ctypes охватывает поиск исходной DLL, но не ниже. –

+0

Хорошо, тогда я думаю, у вас есть маршрут. У вас уже есть уровень JS, который загружает вашу DLL. Предположительно с вызовом 'LoadLibrary'. Поэтому добавьте вызов 'SetDllDirectory' непосредственно перед вызовом' LoadLibrary' в C++ DLL. Когда это вернется, добавьте второй вызов в 'SetDllDirectory', чтобы отменить изменение. –

ответ

1

Поскольку никто другой не имеет ответа, я буду документировать то, что я закончил.

Когда собственный код вызывает dotnet DLL, CLR запускается за кулисами, чтобы запустить его. Хотя собственный код может искать DLL в разных местах, включая те, которые заданы SetDllDirectory, CLR будет отображаться только в каталоге, из которого был запущен начальный исполняемый файл, и в глобальном кэше сборок. Чтобы получить доступ к сборкам, с которыми связана ваша DLL, добавив ссылки на них в Visual Studio, они должны находиться в одном из этих двух мест.

Поскольку я тоже не хотел этого делать, нужно сделать DLL .net, которая напрямую зависит только от сборки фреймов, без ссылок на мои собственные. Затем он получает CLR и запускает мой код. Затем я могу загрузить сборку, которую я хочу использовать через Assembly::LoadFrom(), и вызывать метод, который я хочу использовать, как описано в документе here.

Конечно, загрузка сборки таким образом по-прежнему приведет к поиску любых других зависимых сборок в исполняемом каталоге или в GAC, если они еще не загружены, и во всех, кроме самого тривиального случая, это тоже сложно потрудиться, чтобы явная загрузка каждой сборки по порядку с самого фундаментального вверх. Поэтому сначала регистрируется событие AssemblyResolve. Когда CLR не может найти сборку в двух своих поисковых точках, она вызывает это событие и позволяет мне определить полный путь сборки и загрузить его, снова используя Assembly::LoadFrom().

Конечно, LoadFrom должен знать базовый путь - единственная информация, которая, кажется, доступна, относится к каталогу исполняемого файла, но есть много способов решить это.

0

Вам необходимо будет изменить DLL search path.

Позвоните по телефону SetDllDirectory, прежде чем звонить ctypes.open(), чтобы загрузить вашу C++ DLL. Перейдите в SetDllDirectory каталог, содержащий вашу DLL, и его зависимые модули.Когда возвращается вызов ctypes.open(), снова вызовите SetDllDirectory, минуя NULL, чтобы отменить модификацию пути поиска.

+0

Чтобы прояснить, если кто-то читает, думает, что это ответ, и просто ждет, когда я вернусь к нему и приму, это не работает для меня, как описано в основной части комментариев :) –