Jump to content

[SOLVED] Calling into a plugin from another plugin


photo

Recommended Posts

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!

 

Link to comment

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.
 

Link to comment

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.

Link to comment

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");

 

Link to comment

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.

Link to comment

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.

Link to comment

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:

Link to comment

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...)

Link to comment

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:

Link to comment

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

Link to comment

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:

123.png
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

  • Like 3
Link to comment

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)

Link to comment

 

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

Link to comment
  • silent changed the title to [SOLVED] Calling into a plugin from another plugin
×
×
  • Create New...