OSGeo / gdal

GDAL is an open source MIT licensed translator library for raster and vector geospatial data formats.
https://gdal.org
Other
4.75k stars 2.5k forks source link

c# / .NET Bindings - GdalConfiguration calls kernel32.dll AddDllDirectory #3368

Open UliEgger opened 3 years ago

UliEgger commented 3 years ago

In GdalConfiguration.cs, methode AddDllDirectory is new and used for the gdal\x64 (or gdal\x86) and gdal\x64\plugins (or gdal\x86\plugins) directories.

This has an undesirable impact on the whole program using the gdal library. After the Gdal is configured the first time, dlls that are located in the program bin directory are not found any more.

I found this when loading the mod_spatialite extension for a Sqlite database connection via SQLiteConnection.LoadExtension

UliEgger commented 3 years ago

I would appreciate the old mechanism using the "PATH" environment variable. But as I try this by editing the GdalConfiguration.cs, it seems not to work any more.

rouault commented 3 years ago

CC @szekerest @bjornharrtell

szekerest commented 3 years ago

@UliEgger I recall this new mechanism was introduced because the earlier version didn't work. However in this particular case, I'd require the detailed steps to reproduce this problem.

bjornharrtell commented 3 years ago

I've not seen anything like this so I have no ideas and agree that we need a detailed reproduction.

UliEgger commented 3 years ago

I'll try to build a small sample application to reproduce the error.

I can say so far that because some of our products are plugins to other products, the startup path may be different from the bin directory. That is why before loading mod_spatialite, the bin directory (where also mod_spatialite.dll is located) is added to the PATH environment variable for the current process. And precisely this mechanism does not work any more. I can get the mod_spatialite loaded if I also use AddDllDirectory method. But then I still have problems with other native dlls.

We also wondered about the flags SetDefaultDllDirectories is called with. Why do you not use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS ?

I tried to remove the SetDefaultDllDirectories and AddDllDirectory methods in the GdalConfiguration.cs and modify the PATH variable and this works for me at least for the functionality we currently use.

UliEgger commented 3 years ago

I have created a test project that can be downloaded from here:

http://vpn.itwh.de/fop/m74HJSaM/TestGdalAndSpatialite.7z

If you run the project and click on the "Create SQLite Connection" button, no error should occur. However if you click the button "Configure Gdal" first, and then hit the "Create SQLite Connection" button, you should get the exception when loading the extension.

bjornharrtell commented 3 years ago

Thanks @UliEgger that clears it up a bit for me and it does seem to be behavior that that can be fixed/improved. I don't think I will be able to work on this in a near future but would welcome a pull request - it's however difficult and or time consuming to verify/test under different scenarios. A pull request would be a start though.

UliEgger commented 3 years ago

From my point of view, I could fix the problem by going back to modifying the PATH environment variable of the process. Do you mean it makes sense to create a pull request with this change ? I do not have another idea to fix the problem.

Tamas Szekeres said that there was a reason for the current technique to load the dlls. He said the old way didn't work any more. But I am not aware what precisely does not work. Currently, I use Gdal only to load georeferenced background images of various formats. But we probably will use much more functionality in the future.

bjornharrtell commented 3 years ago

@UliEgger if that is your workaround no I guess there is nothing we can do here. I suggest closing this as we do not have any ideas how to improve the situation generally.

szekerest commented 3 years ago

@UliEgger Going to do a test with your sample case and consult with the original author of GdalConfiguration.cs what was the reason of the change. I'd somewhat prefer to modify the nuget to install the dll-s into the output directory directly depending on the selected architecure (x86 or x64) not requiring to implement a bootstrap code for that.

veikkoeeva commented 3 years ago

Maybe looking at https://github.com/MaxRev-Dev/gdal.netcore @MaxRev-Dev gives ideas or directions? He seem to be wrestling partially with the same issues. There seem to be overlap over these projects, maybe this should be coordinated.

MaxRev-Dev commented 3 years ago

@UliEgger I don't see here any problem with GDAL configuration. Please, look at mod_spatialite extension loader in Form1.cs. You should add your {projectRoot}/spatialite directory to search paths like this.

AddDllDirectory(extModulePath);
connection.LoadExtension("mod_spatialite");

Simple solution)

UliEgger commented 3 years ago

That is true and I have tried this, too. The problem is when using both approaches in the same project.

The question is: Which approach is state of the art ?

But I had another problem in my original application. After the calls of SetDefaultDllDirectories and/or AddDllDirectory, another native dll could not be used any more as before (where we used the Import of methods exposed by this dll.

I can try to modify my example in order to reproduce this.

The suggestion of Tamas Szekerst "I'd somewhat prefer to modify the nuget to install the dll-s into the output directory directly depending on the selected architecure (x86 or x64) not requiring to implement a bootstrap code for that" sounds promising to me.

UliEgger commented 3 years ago

I can't reproduce it in my sample application. So I will try again with my original application. But there, we also had the question why SetDefaultDllDirectories is not called with the flag

LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000

UliEgger commented 3 years ago

I found the remaining problem in case of AddDllDirectory approach must be between mod_spatialite and our own dll "ExtranSim.dll".

MaxRev-Dev commented 3 years ago

install the dll-s into the output directory directly

I'm shipping bindings dependencies for different runtimes using this feature, but it's not a good approach. And it's all about "DLL hell". If you use other library with similar native dependencies, there can be a dll version collision. And even if you are using this type of shipping, you still need some bootstrappers for PROJ.db and gdalplugins

UPD: There are many target environments - production bundles, VS debugger env, testrunner hosts, docker containers. All of them use different folder structure - project root, package root, application root. And sometimes package contents can not be found easily.

UliEgger commented 3 years ago

I am currently testing if the use of SetDllDirectoryW instead of SetDefaultDllDirectory and AddDllDirectory could be an alternative. It seems to have no side effects and works to load mod_spatialite. I will test this in Gdal, too.

I was also wrong when saying the problem is only between mod_spatialite and ExtranSim.dll. Our dll also fails to load after calling GdalConfiguration.

Update:

I can confirm now that using SetDllDirectoryW instead of SetDefaultDllDirectory and AddDllDirectory works

In my tests it was essential that SetDefaultDllDirectory is not called. In contrast to AddDllDirectory, SetDllDirectory works without calling SetDefaultDllDirectory before.

So from my point of view, this would be the solution I'd prefer.

For comparison, I have uploaded the two versions here:

http://vpn.itwh.de/fop/0QxQVJ6P/ProblemDllsLaden.7z

h0st1le commented 2 years ago

I've also ran into this issue. The link to the example project seems to no longer exist. Any chance I can get some additional information about how to handle this problem?