Загрузка незарегистрированной сборки.NET из неуправляемой нативной библиотеки C++ в приложении Java
Я использую приложение Java, которое может быть расширено с помощью пользовательских пакетов, которые могут включать в себя собственный код, поэтому я не имею никакого контроля над средой Java, кроме возможности добавления расширений jar.
В этом случае мне нужно иметь возможность вызывать код C# из расширения Java. Это код C#, который ранее использовался из чисто нативного контекста с использованием свободного от регистрации COM - который отлично работает. Теперь мне нужно иметь возможность сделать такой же код пригодным для использования из этого контекста Java, но я столкнулся с проблемой, что, похоже, нет никакого способа контролировать, где должна быть найдена DLL. Требование установки DLL в каталог bin JRE/JDK явно не вариант, и требование регистрации DLL в GAC не было бы идеальным.
В настоящее время я надеюсь, что все зависимости DLL будут упакованы в jar-файл расширения и будут загружаться при необходимости во время выполнения (без установки вручную).
Я надеялся, что смогу использовать API контекста активации, чтобы это заработало, и протестировал несколько вариантов этого кода с помощью DLL в D: \ somepath \:
ACTCTX actctx = {sizeof(actctx)};
actctx.lpSource = "D:\\somepath\\dll.manifest";
actctx.lpAssemblyDirectory = "D:\\somepath\\";
actctx.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
HANDLE hActCtx = CreateActCtx(&actctx);
ULONG_PTR ulpCookie = 0;
ActivateActCtx(hActCtx, &ulpCookie);
CoCreateInstance(...);
Манифест содержит единственную зависимую сборку с деталями сборки, содержащей класс C#, на который ссылается вызов CoCreateInstance.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity name="[assemblyname]" processorArchitecture="msil" publicKeyToken="[token]" version="[version]" />
</dependentAssembly>
</dependency>
</assembly>
Также пробовал с манифестом в этом формате, выгруженным из DLL с помощью mt.exe:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="[assemblyname]" version="[version]" publicKeyToken="[token]" processorArchitecture="msil"/>
<clrClass clsid="..." progid="..." threadingModel="Both" name="..." runtimeVersion="v4.0.30319"/>
...
<file name="[assemblyname].dll" hashalg="SHA1"/>
</assembly>
Вызовы CreateActCtx и ActivateActCtx успешны, но вызов CoCreateInstance всегда терпит неудачу, возвращая 0x80070002, если я не помещаю DLL в C:\Program Files\Java...
Есть ли какое-либо решение (кроме использования GAC), которое позволило бы полностью неуправляемой C++ DLL загрузить и создать экземпляр класса C# из DLL в произвольном известном месте?
1 ответ
Я работаю над приложением с аналогичными требованиями, т.е. мне нужно активировать управляемый COM-сервер без регистрации (нашел его в DLL, написанном на C#/.net v4.8) из неуправляемого клиентского процесса (написанного на C++), а C# DLL должна находиться в каталоге вне каталога приложения C++.
На данный момент бесплатная активация COM-сервера управляемой DLL-библиотеки COM-сервера работает для меня только тогда, когда я
активировать COM-сервер с помощью пользовательского контекста активации (CreateActCtx / ActivateActCtx и т. д.) с помощью
actctx.lpAssemblyDirectory = ...
установить в каталог COM DLL иизменить , чтобы он мог найти мою C# DLL, чего он не делает по умолчанию.
Что касается последнего, в настоящее время я делаю это, добавляя для C++ EXE, в котором я активирую режим разработки , а также устанавливаю для переменной среды DEVPATH то же значение, что и для `actctx.lpAssemblyDirectory'. Это работает для меня, потому что мне нужно, чтобы каталог C# DLL находился вне каталога C++ EXE во время разработки.
Обратите внимание, что существуют и другие варианты изменения алгоритм проверки сборки на стороне .netалгоритма проверки сборки .net . Например, вы могли бы
- добавить файл конфигурации приложенияконфигурационный файл приложения и указать дополнительные подкаталоги (!) для поиска . Работает только для относительных путей/фактических подкаталогов каталога приложения
- добавьте пользовательский преобразователь сборки .net (см. AssemblyResolve), который должен быть загружен средой выполнения .net, прежде чем она фактически попытается найти сборку C# COM-сервера. Я еще не изучил, как заставить это работать, хотя...
Возможно (если это вариант для вас) взгляните на .Net DllExport , который позволяет вам экспортировать методы .net как функции DLL из сборки .net. Это позволяет вам взаимодействовать со сборкой .net так же, как с собственной библиотекой DLL (используя LoadLibrary, GetProcAddress и т. д.).