Developers Knowledge Base

Read articles and post questions in this comprehensive developer community and support forum.
Showing results for 
Search instead for 
Do you mean 

Spatial Modeler and DLL Hell

by on ‎03-16-2020 01:18 PM (163 Views)

As you extend Spatial Modeler with operators or dialogs you may find yourself in “DLL Hell”, where at best your plugin fails to load, or worse, crashes Spatial Modeler. This can happen when your plugin depends on DLLs outside of Spatial Modeler that have the same name as those deployed by Spatial Modeler but are incompatible.

 

This article examines ways you can resolve any DLL Hell problems you may encounter.

 

Why Won't My Plugin Load?

You’ve built your operator or dialog plugin, deployed it correctly for Spatial Modeler to discover, but when you run the Spatial Modeler Editor and drag your operator onto the design canvas the application crashes or shows an error. Debugging confirms that your operator is not loading the supporting DLL(s) that your plugin was built against. Dependency Walker, or using the Global Flags tool to enable DLL Loader Snaps in the debugger can show which supporting DLL isn't loading and why.

 

How Do I Fix This?

Once yolu have identified the supporting DLL that's incompatible with your plugin, you need to either remove the need to load the supporting DLL, resolve the name clash or enable multiple instances of DLLs with the same name in the Spatial Modeler process. There are several techniques from which you can choose. In summary they are:

 

  • Link your plugin against the Spatial Modeler SDK libraries
  • Statically link your chosen supporting functionality into your plugin
  • Rebuild your supporting functionality from source with a different name
  • Use Win32 (side-by-side) assemblies
  • Rename your supporting DLL without source 

 

Link your plugin against the Spatial Modeler SDK libraries

If the supporting DLL is known to provide the same functionality as its Spatial Modeler counterpart you can usually resolve the incompatibility by compiling and linking against the headers and import libraries provided in the Spatial Modeler SDK.

 

Statically link your chosen supporting functionality into your plugin

If you find that the version of, say, an Open Source DLL deployed in Spatial Modeler is missing functionality that your plugin requires and there is a static library available, then you can remove the runtime dependency by statically linking against that library.

 

Rebuild your supporting functionality from source with a different name

If have the source code for the problematic DLL you can rebuild the DLL with a different output name to resolve the name clash.

 

Use Win32 (side-by-side) assemblies

If you are not able to statically link or rename the problematic DLL you can use Win32 assemblies to enable Spatial Modeler to host multiple instances of a DLL with the same name at the same time, This technique involves adding information that identifies the version of the supporting DLL and adding information about that dependency into your plugin. 

 

A Win32 assembly is simply a DLL that incorporates an XML resource known as an assembly manifest. It is this manifest that contains identity and/or dependency information. When the operating system loads your plugin it first probes for an assembly manifest and if successful looks for information that describes the dependency.  If successful it searches for DLLs containing a manifest that has matching identity information. If  a match is found then that DLL is loaded, even if a DLL with the same name is already loaded in the process.

 

Here's an example assuming your plugin is called foo.dll and your supporting DLL is called bar.dll

 

First create an assembly manifest file for bar.dll that includes the assembly identity. Call it bar.dll.manifest

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity name="bar" type="win32" processorArchitecture="x86" version="1.0.0.0"></assemblyIdentity>
  <file name="bar.dll"></file>
</assembly>

 

Next create an assembly manifest for your plugin to add the dependency description. Call it foo.dll.manifest

 

<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity name="bar" type="win32" processorArchitecture="x86" version="1.0.0.0" />
    </dependentAssembly>
  </dependency>
</assembly>

 

Use Microsoft's manifest tool to embed the manifests into your 2 DLLs.

 

mt -manifest foo.dll.manifest -outputresource:foo.dll;2

mt -manifest bar.dll.manifest -toutputresource:bar.dll;2

 

The DLLs are now ready for redeployment.

 

Rename your supporting DLL without source 

If one or more of your components and the SMSDK depend on a single third-party DLL for which you have only headers and an import library, it is still possible to resolve a naming clash. Doing so will involve:

 

  • Creating a renamed copy of the third-party DLL
  • Recreating the third-party DLL’s import library
  • Linking your component against the recreated import library
  • Deploying both your rebuilt component and the renamed third-party DLL

 

A DLL’s import library contains the details of all the functions exported by the DLL. Included in these details is the name of the DLL itself, so when you link against a DLL’s import library you embed this dependency information into your plugin. When the operating system loads your component into a process it retrieves this information and therefore knows which supporting DLLs are required by your plugin.

 

If you want to rename a dependency you must create an import library that references the new DLL name. To do this we extract and parse the function definitions from the original import library and create a new import definition (DEF) file that contains the new DLL name and a list of the functions exported by it. We use the Visual Studio DUMPBIN and LIB utilities in a batch file to do just that. The following sample DOS batch script illustrates how

 

echo off
REM This script creates a renamed copy of a DLL and import library alongside the original files
REM usage: rename_dll path_to_original_DLL path_to_original_import_library renamed_dll_name

set ORIGINAL_DLL=%1
set ORIGINAL_LIB=%2
set NEWDLLNAME=%3

if "%3"=="" goto usage

for /f %%i in ("%ORIGINAL_DLL%") do @set DLL_PATH=%%~dpi
for /f %%i in ("%ORIGINAL_LIB%") do @set IMPORTLIB_PATH=%%~dpi
for /f %%i in ("%NEWDLLNAME%") do @set NEWNAME=%%~ni

@set NEW_DLL=%DLL_PATH%%NEWNAME%.dll
@set NEW_IMPORTLIB=%IMPORTLIB_PATH%%NEWNAME%.lib
@set DEF_PATH=%IMPORTLIB_PATH%%NEWNAME%.def

@set ARCH=x86

echo Getting machine architecture
dumpbin /headers %ORIGINAL_DLL% > %IMPORTLIB_PATH%headers.txt
for /f "delims=() tokens=1,2" %%i in (%IMPORTLIB_PATH%headers.txt) do if %%j == x64 @set ARCH=x64

if %ARCH%==x64 (
 @set ARCHOPT=/MACHINE:X64 
) ELSE (
@set ARCHOPT=/MACHINE:X86
)

echo Extracting exports from %ORIGINAL_LIB%
dumpbin /exports %ORIGINAL_LIB% > %IMPORTLIB_PATH%exports.txt

if not %ERRORLEVEL%==0  (
echo Failed to extract exports
goto error_exit
)

@echo LIBRARY %NEWNAME%.dll  > %DEF_PATH%
@echo EXPORTS >> %DEF_PATH%
for /f "tokens=4 skip=19" %%i in (%IMPORTLIB_PATH%exports.txt) do @echo %%i >> %DEF_PATH%
echo Creating new import library
lib /nologo /def:%DEF_PATH% %ARCHOPT% /out:%NEW_IMPORTLIB%

if not %ERRORLEVEL%==0  (
echo Failed to create import library
goto error_exit
)

REM Delete the temporary output
del /f %DEF_PATH%
del /f %IMPORTLIB_PATH%exports.txt

echo Copying DLL
copy %ORIGINAL_DLL% %NEW_DLL%

if not %ERRORLEVEL%==0  (
echo Failed to create new DLL
goto error_exit
)

echo Renamed library creation succeeded
goto :EOF

:error_exit
echo Renamed library creation failed

:usage
echo usage: rename_dll path_to_original_DLL path_to_original_import_library renamed_dll_name

Note that this approach only works where the renamed DLL isn’t itself dependent on other DLLs that are contributing to DLL Hell. This is because the solution outlined here relies on you being able to recompile and link any components that depend on the renamed DLL. For more complex dependences using Win32 Assemblies is a more suitable solution.

 

 

 

Overview