I cannot see any downside to this solution.
I am not much of a Windows user, but I must admit that the solution
of implicitly finding dynamic libraries where stands the executable
is of great help.
You put the whole content of your application (the executables and
the dynamic libraries/plugins) anywhere and as soon as you manage
to run the executable everything else will be found.
Of course, if you plan to deliver many different applications
with a common subset of dynamic libraries, it could be better
to put all theses libraries in a common place and adjust the
PATH environment variable accordingly.
But it is not worth the effort for a single application.
In a mingw-w64 (not exactly Msys64 and MingW, but very close)
based application (with plugins) that I delivered a few months ago,
I just provided libgcc_s_seh-1.dll, libstdc++-6.dll and
libwinpthread-1.dll as a complement to my own binaries and it
worked without any problem.
Using objdump.exe -p my_program.exe
(then recursively
on the displayed result) helps finding the needed dynamic
libraries (like ldd
on Linux or otool -L
on Macosx).
That's what I like in the mingw-like solution: it makes a native
Windows application that does not depend on many other unusual
components (that the user would have had to retrieve first).
There is no need to deal with some .lib
files; building a
.dll
and linking against it is enough (see the example below).
It works exactly as we do on UNIX (with .so
files).
I really don't know why Visual-C++ relies on a so complicated
combination of .lib
and .dll
files...
I just re-tested with this simple example.
file prog.cpp
#include <windows.h>
#include <iostream>
__declspec(dllimport)
int
my_library_function(int arg);
int
main()
{
std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
int result=my_library_function(123);
std::cout << "result=" << result << '\n';
std::cout << "~~~~ still in " << __func__ << " ~~~~\n";
HINSTANCE lib=LoadLibrary("my_plugin.dll");
if(lib)
{
FARPROC symbol=GetProcAddress(lib, "my_plugin_function");
if(symbol)
{
int (*fnct)(int)=NULL;
memcpy(&fnct, &symbol, sizeof(fnct));
int result=fnct(123);
std::cout << "result=" << result << '\n';
}
FreeLibrary(lib);
}
std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
return 0;
}
file my_library.cpp
#include <iostream>
__declspec(dllexport)
int
my_library_function(int arg)
{
std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
std::cout << "arg=" << arg << '\n';
std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
return 2*arg;
}
file my_plugin.cpp
#include <iostream>
extern "C" __declspec(dllexport)
int
my_plugin_function(int arg)
{
std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
std::cout << "arg=" << arg << '\n';
std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
return 2*arg;
}
build process
==== compiling [opt=0] my_plugin.cpp ====
g++ -o my_plugin.o my_plugin.cpp -c -g -O0 -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG -std=c++17 -Wno-missing-braces -Wno-sign-conversion
==== linking [opt=0] my_plugin.dll ====
g++ -shared -o my_plugin.dll my_plugin.o -g -O0
==== compiling [opt=0] my_library.cpp ====
g++ -o my_library.o my_library.cpp -c -g -O0 -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG -std=c++17 -Wno-missing-braces -Wno-sign-conversion
==== linking [opt=0] my_library.dll ====
g++ -shared -o my_library.dll my_library.o -g -O0
==== compiling [opt=0] prog.cpp ====
g++ -o prog.o prog.cpp -c -g -O0 -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG -std=c++17 -Wno-missing-braces -Wno-sign-conversion
==== linking [opt=0] prog.exe ====
g++ -o prog.exe prog.o -g -O0 -lmy_library
execution
C:\Work\PluginTest>prog.exe
~~~~ entering main ~~~~
~~~~ entering my_library_function ~~~~
arg=123
~~~~ leaving my_library_function ~~~~
result=246
~~~~ still in main ~~~~
~~~~ entering my_plugin_function ~~~~
arg=123
~~~~ leaving my_plugin_function ~~~~
result=246
~~~~ leaving main ~~~~
C:\Work\PluginTest>
This still works when the mingw64 directory (containing the toolchain)
is renamed, as soon as the files libgcc_s_seh-1.dll
,
libstdc++-6.dll
and libwinpthread-1.dll
are placed in the
same directory as prog.exe
.
It even works when the prog.exe
is launched by clicking (not
from the command line); to see this I had to add an infinite loop
at the end of the program to keep the window open long enough to
see the displayed messages.