I need to implement some functionality of the otherwise monolithic application via DLL that is loaded at runtime. (Think about a customized DLL -- different for each customer.)
The Visual C++ from VS 2013 is used.
The application gets the full name name of the DLL. The function to be called has a fixed name (convert
in my case) and fixed number and type of the arguments (a reference to a constant vector of strings -- see the cref_vs
below). This means the function is implemented in C++.
I have modified the example from the MSDN doc, and it seems to work. However, I must be sure I did not overlooked something important. Here is the code (simplified) to load the DLL and call the function:
typedef const std::vector<std::string> & cref_vs;
typedef int(*CONVERTPROC)(cref_vs vs);
Notice that unlike in the official example that mixes calling a C code in DLL from a C++ function, I do not use __cdecl
. I guess that this way I can reliably pass a C++ object (like a vector of strings). Is it correct?
int call_convert_from_dll(const std::string & dllname,
const std::vector<std::string> & vs)
{
HINSTANCE hinstLib;
CONVERTPROC convert;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(dllname.c_str());
// If the handle is valid, try to get the function address.
if (hinstLib != nullptr) {
convert = (CONVERTPROC)GetProcAddress(hinstLib, "convert");
// If the function address is valid, call the function.
if (convert != nullptr) {
(convert)(vs);
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
if (!fRunTimeLinkSuccess)
return 1;
return 0;
}
Now, because I want the unmangled name of the function inside the DLL, I need to use extern "C"
, and it must be exported via __declspec(dllexport)
. However, I do not want to use __cdecl
calling convention:
#include <windows.h>
#include <iostream>
using namespace std;
extern "C" {
__declspec(dllexport) int convert(const std::vector<std::string> & vs)
{
cout << "my.dll -- convert called with arguments:\n";
for (int i = 0; i < argc; ++i)
cout << argv[i] << endl;
return 0;
}
} // extern "C"
Can you confirm that I am doing it correctly?
1 Answer 1
Short answer: No, but I won't hold it against you. ;)
I'd suggest reading up on using C++ with DLLs in a compatible fashion, and would suggest this excellent article as a good place to start. This article also touches on exporting STL types, which your code is doing.
So, the question is, how portable/robust do you need/expect your approach to be? If you need developers from anywhere to be able to plug into your system, stick to a C interface if possible (avoiding STL types -- pass a char**
across the boundary, and convert that on the C++ side to an STL vector<string>
). Otherwise, upgrade to a virtual class based system -- and make sure that you don't directly use STL, but instead use virtual class wrappers for those types as well. Note that this carries on -- e.g., if you want to use an iterator class used by the STL type you're wrapping, you'll need another abstract (iterator) class that your abstract (iterable) class references, so it can get a little ugly.
Minor code notes...
fFreeResult
is assigned, but never used.fRunTimeLinkSuccess
is not set (after initialization).- The "convert" string might better be contained in a macro (or a small set of macros), so that your plugin clients can declare a convert function, and you can refer to that function name without the possibility of typo.
- Your example plugin refers to
argc
andargv
that don't exist.
-
1\$\begingroup\$ @LokiAstari Good catch on fRunTimeLinkSuccess never changing from its initialized value. \$\endgroup\$Travis Snoozy– Travis Snoozy2014年04月26日 16:07:02 +00:00Commented Apr 26, 2014 at 16:07
-
\$\begingroup\$ For the
fRunTimeLinkSuccess
-- corrected. I did remove some other lines. This one should stay there. For theargc
andargv
. This was actually copied from another version of the code (my fault), that was passed the arguments from the command line (for testing). The newer version parses the string from a file (StringTok
tovector<string>
. So, I should do the opposite way -- to convertvector<string>
to theint
andchar*[]
, right? Thanks, I will be back after reading the article. \$\endgroup\$pepr– pepr2014年04月26日 16:35:11 +00:00Commented Apr 26, 2014 at 16:35 -
\$\begingroup\$ Thanks for the article. It seems that it will be better to pass the
char*
(the string from a file) and wrap the function body to thetry
/catch
to avoid exceptions bubble from DLL to the caller (returning integer error code). \$\endgroup\$pepr– pepr2014年04月26日 17:22:18 +00:00Commented Apr 26, 2014 at 17:22 -
\$\begingroup\$ Please, have a look at the second version codereview.stackexchange.com/q/48260/16189 \$\endgroup\$pepr– pepr2014年04月26日 19:31:29 +00:00Commented Apr 26, 2014 at 19:31