Amerio.Stephane Posted February 6, 2024 Posted February 6, 2024 Hi, We need to call a plugin function from another plugin. Let's have a example: - plugin A can receive network messages from a simulation host an will treat it in update() - plugin B can encode data in some way. In short, Plugin A::update() shall call plugin B::encode(const char* data) if plugin B has been installed (and just do nothing if plugin B is missing) Both plugins are defined will __declspec(dllexport) at compile time and __declspec(dllimport) at import time in their respective header, with separate builds. No troubles here ... #ifdef DLL_EXPORT_A #define DLLEI_A __declspec(dllexport) #else #define DLLEI_A __declspec(dllimport) #endif class DLLEI_A APlugin: plugin Unigine::Plugin { public: ... int init() override; void update() override; ... }; ... #ifdef DLL_EXPORT_B #define DLLEI_B __declspec(dllexport) #else #define DLLEI_B __declspec(dllimport) #endif class DLLEI_B BPlugin: plugin Unigine::Plugin { public: ... void encode(const char* data); //< our custom function, to be called from A::update() if the plugin B is installed ... }; In 2.16 and earlier, we could just implement this in plugin A with this: #include "....../BPlugin.h" // in class APlugin declaration BPlugin* bplugin = nullptr; // in APlugin::init() cpp bplugin = Engine::get()->getPlugin<BPlugin>("BPlugin"); // in APlugin::update() cpp if (bplugin) bplugin->encode(data); And it worked fine! But in 2.17 and 2.18, the location of installed plugins has changed, and the plugin A can no longer be loaded. I guessed it's trying to locate BPlugin right beside it but it can't find it anymore. We're surely doing something terribly wrong here. Is it the right way of doing this kind of things? (having an optional treatment inside a plugin, called by another) How can we fix that? Thanks!
victor Posted February 7, 2024 Posted February 7, 2024 Hello, Quote and the plugin A can no longer be loaded Could you show any log messages or give more information about? According to the changed naming convention for plugins you should put your plugin binaries there: bin\plugins\<VendorName>\<PluginName> (e.g. bin\plugins\VendorName\PluginName\VendorNamePluginName_plugin_double_x64.dll) After it the Engine will be able to load your plugin by using this shell command: -extern_plugin VendorNamePluginName Please see this documentation for more details: https://developer.unigine.com/en/docs/2.18/code/cpp/plugin?rlang=cpp#path_to_plugin_files Also you may see where to put your headers - "include\plugins\<VendorName>\<PluginName>\", it's a preferred path. If you face with any troubles, please let us know.
Amerio.Stephane Posted February 7, 2024 Author Posted February 7, 2024 Yes, the plugins path for dll and headers are the new one, and they are correct. I think the issue is related to the dll searching path for the application at runtime.
victor Posted February 7, 2024 Posted February 7, 2024 I may suppose, that you need to write this code and it will be enough: #define DLLEI_EXPORT_A __declspec(dllexport) class DLLEI_EXPORT_A APlugin: plugin Unigine::Plugin Because you don't need to link your plugins to each other. The Engine will load them and then you will be able to get the instance pointer: BPlugin *bplugin = Engine::get()->getPlugin<BPlugin>("BPlugin");
Amerio.Stephane Posted February 7, 2024 Author Posted February 7, 2024 This is all already done. Again, this was working in 2.16. It's "broken" in 2.18, although the plugins' name and binary location are correct. Other plugins are working just fine.
victor Posted February 7, 2024 Posted February 7, 2024 Yes, it worked, because all plugin binaries were in one place - /bin and the OS dynamic linker was able to find "linked" plugins. Although there was no reason to link them, because the Engine loads them and then you are able to use their instances without such a linkage. And we decided to split every plugin to its directory to allow to have its own dependencies in a separate directory. For example, it can be a content of some plugin (A): bin\plugins\MyCompany\VideoPreview\MyCompanyVideoPreview_plugin_x64.dll bin\plugins\MyCompany\VideoPreview\ffmpeg.dll And there is a content of another plugin (B): bin\plugins\AnotherCompany\Preview\AnotherCompanyPreview_plugin_x64.dll bin\plugins\AnotherCompany\Preview\ffmpeg.dll These `ffmpeg.dll` files can be from different versions and every plugin is able to load its version of `ffmpeg.dll` to avoid crashes for such a case. Of course, if both plugins will be loaded, the OS linker will load `ffmpeg.dll` from the first loaded plugin, but we can't solve all such problems, we are trying to minimize the probability of error. This solution has been done to allow a plugin to have their resources in its directory. Moreover a user is able to keep some common resources for its plugins in the directory `bin\plugins\MyCompany`, nobody prohibits it.
silent Posted February 7, 2024 Posted February 7, 2024 Looks like in order to fix that you would need to manually add paths of both plugin directories into the %PATH% system variable before the engine starts, so Windows was able to load symbols correctly. Stephane, could you please check if that would work? Thanks! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN
Amerio.Stephane Posted February 7, 2024 Author Posted February 7, 2024 We can't modify %PATH% here, but I guess this would work. I think a solution would be to simply break the compile time dependency of APlugin to BPlugin and just use the dynamic loading capabilities and find the 'encode' entry point, but I can't find a way to have it work (not my specialty...)
silent Posted February 8, 2024 Posted February 8, 2024 In case if you can't modify %PATH% either via code (before the engine launch) or via system variable editing we would need some additional time to think how else this can be implemented. Could you please tell how critical is that for you at the moment and should we start research right now? Thanks! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN
Amerio.Stephane Posted February 8, 2024 Author Posted February 8, 2024 well, this prevents one of our plugin to be loaded at all, but it's not critical at the moment while we're still on 2.16. We have other difficulties with 2.18 (VR...) so this is not an urgent matter. I think it's more of a windows/dll/cpp thing than a Unigine specific issue, albeit we have to use Engine::getPlugin to reach the plugin. Thanks
victor Posted February 8, 2024 Posted February 8, 2024 Hello, Quote I think the issue is related to the dll searching path for the application at runtime. Yes, you are absolutely right. With the type of linkage that you've mentioned the only way is modification of PATH either before app launch or from the code. I think we can suggest some solution to add plugin's directories to PATH or something like this (Windows only!), but I recommend you to look at our solution that we use for our plugins. Quote I think a solution would be to simply break the compile time dependency of APlugin to BPlugin and just use the dynamic loading capabilities and find the 'encode' entry point, but I can't find a way to have it work (not my specialty...) We use such a solution to break dependencies because we quite often use one plugin from another one. It's based on abstract C++ classes and only vtable-s are used in runtime. It perfectly works for Windows and Linux. I prepared some sample to show how the plugin A can use the plugin B and vice-versa. To check it follow next steps: 1. Create an empty project with the name 'two_plugins', C++ (CMake) and float precision, like this: 2. Replace all the content from the ZIP archive in the project directory 3. Now you will be able to run CMake to configure and build an app with the plugins. On Windows I run "x64_x86 Cross Tools Command Prompt for VS 2022" and type commands like these in the `source` directory: cmake -B ../build -S . cmake --build ../build Please look at this sample and tell us if it's suitable for you. two_plugins.zip 3
Amerio.Stephane Posted February 8, 2024 Author Posted February 8, 2024 Thanks for the code. Just got a look at it but are you sure it's supposed to work with plugin A and B are built as separate DLL? (just making sure before moving to the dev env, and not accustomed to CMAKE)
victor Posted February 8, 2024 Posted February 8, 2024 Quote are you sure it's supposed to work with plugin A and B are built as separate DLL? Yes, they will be two separate dlls: PluginA -> plugins/MyCompany/PluginA/MyCompanyPluginA_plugin_x64.dll PluginB -> plugins/MyCompany/PluginB/MyCompanyPluginB_plugin_x64.dll
Amerio.Stephane Posted February 8, 2024 Author Posted February 8, 2024 This works! You saved the day, victor :) Thanks 1
Recommended Posts