Fody / Costura

Embed references as resources
MIT License
2.4k stars 274 forks source link

Support for LoadLibrary calls? #75

Closed gharpur closed 6 years ago

gharpur commented 10 years ago

Costura is working well for me (thanks!), but I call a 3rd party .NET (mixed mode) DLL, which in turn uses LoadLibrary (I assume) to load a native DLL. That call is not finding the native DLL in the temp folder because it's not on the path. I thought of adding a call to SetDllDirectory in my own code, but I don't have access to the hashed directory name.

Would it be feasible to add a call to SetDllDirectory into Costura at the point the native DLLs are extracted to handle this type of situation?

distantcam commented 10 years ago

Did you try including the native libraries the Costura way?

https://github.com/Fody/Costura#native-libraries-and-preloadorder

gharpur commented 10 years ago

Yes, the library is included in a costura32 folder (and I'm using the same method for other native libraries successfully), I assume the issue from looking at the Costura code is that it makes a call to LoadLibrary containing the explicit path to the temp folder. My understanding is that when the 3rd party DLL makes its call to LoadLibrary (without a path) that will fail because (a) when the string passed is different Windows doesn't think it's already loaded and (b) it can't find the DLL in any of the usual places.

In other words, even though Costura has already loaded the library, Windows isn't clever enough to use that one on the 2nd call to LoadLibrary (see http://msdn.microsoft.com/en-us/library/ms684175%28VS.85%29.aspx and in particular the comment "If lpFileName does not include a path and there is more than one loaded module with the same base name and extension, the function returns a handle to the module that was loaded first.")

I think if Costura made a call to SetDllDirectory and then did a LoadLibrary call without a path name it would help two ways - when the second call to LoadLibrary came Windows would just return a handle to the already loaded library, and even if it weren't already loaded, Windows would look in the correct place for it.

distantcam commented 10 years ago

Ah, I understand the problem now. I'll look into it.

gharpur commented 10 years ago

That's great - many thanks.

gharpur commented 10 years ago

Just wondering whether you've got any update on this? It's fairly urgent from my side, so I'd be happy to attempt a fix and submit a pull request if it's not something you'll be able to look at in the near future.

distantcam commented 10 years ago

Please try 1.3.2.

gharpur commented 10 years ago

Thank you. Unfortunately, it didn't work for me, but on further investigation it looks like the 3rd party DLLs I'm using must have its own search mechanism overriding the standard LoadLibrary one, so they don't seem to be taking the SetDLLDirectory call into account. It looks like I'll need to hack something, or ask the 3rd party to honour the SetDLLDirectory call.

Regardless, I think the change should be useful for the generic case where a managed or unmanaged assembly is making an explicit LoadLibrary call. I think it might in addition be useful to remove the full path from the Fody LoadLibrary call now that SetDLLDirectory is in place (i.e. take out Path.Combine at Line 270 of Common.cs), because as you have it, the DLL could end up being loaded twice.

gharpur commented 10 years ago

There's an issue with the SetDLLDirectory call - it is currently being set to the top level hashed folder (i.e. the managed folder) not the native sub-folder (i.e. with a \32 or \64 suffix).

On a separate note, it looks like managed assemblies are being loaded from a byte array rather than disk, even when CreateTemporaryAssemblies='true' and I don't believe that's the desired behaviour.

Darkvater commented 9 years ago

I've experienced the same. The path that is added is not the \32, \64 subpath, but the root, as thus I am unable to load the 64-bit DLL into my assembly.

SimonCropp commented 6 years ago

if this issue still occurs in v2 please upload a small solution that reproduces the problem

froque commented 6 years ago

Costura75.zip

I had this issue when trying to use SevenZipSharp. I work around this with:

var name = typeof(SevenZip.ArchiveFileInfo).Assembly.GetFile("sevenzipsharp.dll").Name;
SevenZip.SevenZipBase.SetLibraryPath(Path.Combine(Path.GetDirectoryName(name), @"32\7z.dll"));

It would be nice for Costura to support this without any workaround. This still occurs in v2 @SimonCropp. I have uploaded a small project.

Monns commented 4 years ago

Costura75.zip

I had this issue when trying to use SevenZipSharp. I work around this with:

var name = typeof(SevenZip.ArchiveFileInfo).Assembly.GetFile("sevenzipsharp.dll").Name;
SevenZip.SevenZipBase.SetLibraryPath(Path.Combine(Path.GetDirectoryName(name), @"32\7z.dll"));

It would be nice for Costura to support this without any workaround. This still occurs in v2 @SimonCropp. I have uploaded a small project.

thanks