Path for plugin DLL dependencies [SOLVED]
-
On 22/09/2015 at 09:27, xxxxxxxx wrote:
User Information:
Cinema 4D Version: R17
Platform: Windows ;
Language(s) : C++ ;---------
Hi,I'm developing a C++ plugin that uses an API exposed via an import lib + DLL.
The plugin's folder structure follows the cinema4d_cpp_sdk sample structure, essentially like this:MyPluginDir/
MyPluginDir/MyPlugin.cdl64
MyPluginDir/res/..
MyPluginDir/res/description/..
MyPluginDir/res/strings_us/..The plugin has been placed in
C:\Users\Me\AppData\Roaming\MAXON\CINEMA 4D R17_8DE13DAD\plugins\As my plugin depends on the external DLLs at plugin load-time, if any of the DLL dependencies are missing my plugin will not load, and so I am not able to provide an additional DLL search path via SetDllDirectory as some people on the forum have suggested. When trying to debug my plugin I get a message like this:
Could not load Extra.dll due to Windows System Error #126: The specified module could not be found. [win_dll.cpp(211)].. (file:///.../AppData/Roaming/MAXON/CINEMA 4D R17_8DE13DAD/plugins/MyPlugin/MyPlugin.cdl64) [win_dll.cpp(232)]
Cause: Windows System Error #126: The specified module could not be found. [win_dll.cpp(225)]In the documentation as well as the forum there are hints about an explicit plugin-specific DLL search path that C4D adds in the form
MyPluginDir/resource/libs/win64I have tried placing my dlls there, but still my plugin fails to load. If I move my extra dlls in the same directory as "CINEMA 4D.exe" everything works as expected.
What do you think is the correct location for such dlls?
Thanks
-
On 23/09/2015 at 03:14, xxxxxxxx wrote:
Hi,
welcome to the Plugin Café forums !
Smile
[URL-REMOVED]
There is actually a tiny bit of documentation on this topic. Have a look at the Project Structure page.
But I agree, this can be improved. I'll at least try to reword that part a bit to make it more clear.Basically you put your DLLs NOT in your plugins folder (which could be easily misread on above mentioned page), but the DLLs go into [Cinema4D application folder]/resource/libs/win64.
I think, this should already do the trick for you.But this may not be the most convenient option for user's of your plugin, as the installation of your plugin is not done by simply unzipping a folder into the plugins directory.
So you may want to consider other options:
a) Static Linking
If the library you want to use supports this option (I mean, if the developer delivers a statically linkable version of the library), it can be a quick and simple workaround, as you don't need to deliver the additional DLL at all.b) Manual Loading
You can load the library manually on plugin startup. The function to look for on Windows is LoadLibrary() (or LoadLibraryA()) and on Mac it would be dlopen(). This way you could place the libraries in your plugins folder (e.g. in a resource subfolder in your plugins directory). Preferably in a folder not scanned by Cinema4D for plugins, see Plugin Directory Structure. It's probably a bit more challenging to implement, but provides you with all freedom and everything can be kept in a single plugins folder.
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
On 23/09/2015 at 03:36, xxxxxxxx wrote:
I use method b) and it works very well for dll linking. If some basic code is required, let me know.
-
On 23/09/2015 at 12:39, xxxxxxxx wrote:
Thanks for the help!
The problem is that currently the API I want to use requires linking with an import library, so all DLL dependencies need to be available before any user code is executed. So my plugin would not even load unless these were in C4D DLL path.I think I have solved this issue like this:
I mark the dependent DLL as delay-load with the "/DELAYLOAD" linker option ("Delay loaded dll" option in Project Properties/Linker). This allows my plugin to load. Then at PluginStart() time, before any DLL function is invoked, I manually load the DLL with a function like this// Load a dll relative to the C4D plugin path HMODULE LoadPluginDLL(const wchar_t *dllName) { String path = GeGetPluginPath().GetString(); path += String(L"\\"); path += String(dllName); maxon::BaseArray<Utf16Char> arr; if(!path.GetUtf16(arr)) return 0; // Append null at end arr.Append(Utf16Char(0)); HMODULE mod = LoadLibraryW(arr.GetFirst()); return mod; }
Note that if a DLL itself has other dependencies not available in the path, these need to be loaded in advance, otherwise the DLL will fail to load.
Finally at PluginEnd() time I unload the DLLs in reverse order.
Andreas: Is there a user-specific dll directory equivalent for [Cinema4D application folder]/resource/libs/win64? For example can I also use
C:\Users\Me\AppData\Roaming\MAXON\CINEMA 4D R17_8DE13DAD\resource\libs\win64?Thanks
Kostas -
On 25/10/2016 at 01:42, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Basically you put your DLLs NOT in your plugins folder (which could be easily misread on above mentioned page), but the DLLs go into [Cinema4D application folder]/resource/libs/win64.
I think, this should already do the trick for you.But this may not be the most convenient option for user's of your plugin, as the installation of your plugin is not done by simply unzipping a folder into the plugins directory.
So you may want to consider other options:
a) Static Linking
If the library you want to use supports this option (I mean, if the developer delivers a statically linkable version of the library), it can be a quick and simple workaround, as you don't need to deliver the additional DLL at all.b) Manual Loading
You can load the library manually on plugin startup. The function to look for on Windows is LoadLibrary() (or LoadLibraryA()) and on Mac it would be dlopen(). This way you could place the libraries in your plugins folder (e.g. in a resource subfolder in your plugins directory). Preferably in a folder not scanned by Cinema4D for plugins, see Plugin Directory Structure. It's probably a bit more challenging to implement, but provides you with all freedom and everything can be kept in a single plugins folder.Sorry for bumping this thread, but better than creating a new topic about the same subject I guess.
Ever since I updated from R17.016 to R17.055, my plugin won't load dll dependencies from resource/libs/win64 properly. Putting the dependencies in Windows/System32 works however.
I've also tried to statically link the library I'm using (Poco) with /MT runtime library to my plugins .cdl64, but I'm unable to since cinema.framework (and possibly its dependencies?) is dynamically linked with /MD runtime library.
This causes both LIBCMT and MSVCRT to be linked at the same time which results in conflicts. How would you create a plugin that does static linking for C4D? I'm assuming it's possible since you mentioned it here. -
On 27/10/2016 at 07:53, xxxxxxxx wrote:
Hi Takyon, thanks for writing us.
Compiling both the Cinema framework as well as your plugin as Multi-threaded[MT] (or Multi-threaded Debug[MTd]) is doable with no side effects. It's relevant that both projects (cinema.framework and your plugin) are set as /MDd or MD otherwise you will end with libs linking issue as the one you reported. Doing this also enables you to statically link also against Poco. In my test I've successfully implemented one of the Poco Foundation examples as C4D plugin.
Best, Riccardo.
EDIT: Beside the notes above I also confirm that delaying the dll loading as pointed out by Karanik by setting the /DELAYLOAD flag during the linking step and implementing the delayed loading function he describes still works properly in R17.055 without requiring to switch to MT.
-
On 29/10/2016 at 10:53, xxxxxxxx wrote:
Thanks. It looks like I simply missed defining the POCO_STATIC preprocessor symbol. I'm unable to test this with the plugin at the moment, but it will most likely solve the issue I was having.
Edit: This was indeed the issue and the plugin statically links Poco now. This works when compiling the plugin using the default MD runtime library.
Everything about changing runtime library was just a sidetrack I thought was necessary to statically link libraries, which turns out to be false (MD/MT only applies to dynamic/static linking of the runtime library). Rookie mistake.