charon-core  0.3.1
WindowsPluginLoader.cpp
Go to the documentation of this file.
1 /* This file is part of Charon.
2 
3  Charon is free software: you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as published by
5  the Free Software Foundation, either version 3 of the License, or
6  (at your option) any later version.
7 
8  Charon is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU Lesser General Public License for more details.
12 
13  You should have received a copy of the GNU Lesser General Public License
14  along with Charon. If not, see <http://www.gnu.org/licenses/>.
15  */
22 #include <string>
23 #include <io.h>
24 #include <cstdlib>
25 #include <windows.h>
26 #include <regex>
27 
28 #ifdef MSVC
29 #include <strsafe.h>
30 #endif
31 
32 #include <charon-core/WindowsPluginLoader.h>
35 
36 
38  const std::string & n,
39  std::vector<std::string> &plpaths,
40  std::string &lSuffix,
42  AbstractPluginLoader(n,plpaths,lSuffix,versionCheck) {
43  hInstLibrary = NULL;
44 }
45 
46 #ifdef MSVC
47 LPCTSTR WindowsPluginLoader::lastError(LPTSTR func) const {
50  LPVOID lpMsgBuf;
51  LPVOID lpDisplayBuf;
52  DWORD dw = GetLastError();
53 
54  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
55  | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL,
56  SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL);
57  lpDisplayBuf
58  = (LPVOID) LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR) lpMsgBuf)
59  + lstrlen((LPCTSTR) func) + 40) * sizeof(TCHAR));
60  if(dw == 0) {
61  StringCchPrintf((LPTSTR) lpDisplayBuf, LocalSize(lpDisplayBuf)
62  / sizeof(TCHAR), TEXT(""));
63  }
64  else {
65  StringCchPrintf((LPTSTR) lpDisplayBuf, LocalSize(lpDisplayBuf)
66  / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), func, dw,
67  lpMsgBuf);
68  }
69 
70  return (LPCTSTR) lpDisplayBuf;
71 }
72 #endif
73 
75  std::string path, pathS, pathDir;
76  //error code must be retrieved immediately after the failure,
77  DWORD lastErrorCode = 0;
78  std::string lastErrorMsg;
79 
80  // find plugin file
81  for(std::vector<std::string>::const_iterator cur = pluginPaths.begin();
82  cur != pluginPaths.end(); cur++) {
83  pathDir = *cur;
84 #ifdef MSVC
85  path = *cur + "\\" + pluginName + ".dll";
86  pathS = *cur + "\\" + pluginName + libSuffix + ".dll";
87 #else
88  path = *cur + "\\lib" + pluginName + ".dll";
89  pathS = *cur + "\\lib" + pluginName + libSuffix + ".dll";
90 #endif
91  if(libSuffix.size() > 0) {
92  if (FileTool::exists(pathS)) {
93  // prefer suffixed over plain version
94  path = pathS;
95  break;
96  }
97  }
98  if (FileTool::exists(path)) {
99  break;
100  }
101  }
102  if (!FileTool::exists(path)) {
103  throw PluginException("Failed to load the plugin \"" + pluginName
104  + "\". The file " + pluginName
105  + ".dll could not be found. (Possible suffix: "+ libSuffix + ")",
107  }
108  sout << "(DD) File: " << path << std::endl;
109 
110  std::string oldDir = FileTool::getCurrentDir();
111  FileTool::changeDir(pathDir);
112 
113  if (_versionCheck != PluginManagerInterface::PluginVersionIgnore) {
114  try {
115  std::string charonCoreVersion (CHARON_CORE_VERSION) ;
116  std::string noVersionMsg = "The plugin contains no valid charon-core version information." ;
117 
118  DWORD handle = 0; //dummy value
119  DWORD size = GetFileVersionInfoSize(path.c_str(), &handle);
120  if(!size) {
121  throw PluginException( noVersionMsg, pluginName,
123  }
124  BYTE* versionInfo = new BYTE[size];
125  if (!GetFileVersionInfo(path.c_str(), handle, size, versionInfo)) {
126  delete[] versionInfo;
127  throw PluginException( noVersionMsg, pluginName,
129  }
130 
131  struct LANGANDCODEPAGE {
132  WORD wLanguage;
133  WORD wCodePage;
134  } *lpTranslate;
135 
136  UINT dwBytes; //size of contained code tables
137 
138  // get language and code page block from file version information
139  BOOL res = VerQueryValue(versionInfo,
140  TEXT("\\VarFileInfo\\Translation"),
141  (LPVOID*)&lpTranslate,
142  &dwBytes);
143  if (res == 0 || dwBytes == 0) {
144  delete[] versionInfo;
145  throw PluginException( noVersionMsg, pluginName,
147  }
148 
149  // usualy we should have only one block of version information per file
150  // but theoretically there could be more
151  for(int i=0; i < (dwBytes/sizeof(struct LANGANDCODEPAGE)); i++ ) {
152  LPTSTR subBlock ;
153  subBlock = new char[50] ;
154  BOOL hr = StringCchPrintf(subBlock, 50,
155  TEXT("\\StringFileInfo\\%04x%04x\\ProductName"),
156  lpTranslate[i].wLanguage,
157  lpTranslate[i].wCodePage);
158  if (FAILED(hr)) {
159  delete[] versionInfo;
160  delete[] subBlock ;
161  throw PluginException( noVersionMsg, pluginName,
163  }
164  LPCTSTR infoBuffer ;
165  UINT bufferLen = 0;
166  // Retrieve file description for language and code page "i".
167  BOOL res = VerQueryValue(versionInfo,
168  subBlock,
169  (LPVOID*)&infoBuffer,
170  &bufferLen);
171  // check that the "ProductName" field is set to "Charon-Suite"
172  if(res == 0 || bufferLen == 0 || std::string("Charon-Suite") != infoBuffer) {
173  delete[] versionInfo;
174  delete[] subBlock;
175  throw PluginException( noVersionMsg, pluginName,
177  }
178 
179  hr = StringCchPrintf(subBlock, 50,
180  TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"),
181  lpTranslate[i].wLanguage,
182  lpTranslate[i].wCodePage);
183  if (FAILED(hr)) {
184  delete[] versionInfo;
185  delete[] subBlock ;
186  throw PluginException( noVersionMsg, pluginName,
188  }
189  bufferLen = 0;
190  res = VerQueryValue(versionInfo,
191  subBlock,
192  (LPVOID*)&infoBuffer,
193  &bufferLen);
194  if (res && bufferLen) {
195  // parse the version string, assume it has the following form
196  // major.minor.patch
197  // we do a simple string comparison, as there is currently no plan for
198  // upward/backward compatibility
199  std::string version(infoBuffer) ;
200  if(charonCoreVersion != version) {
201  delete[] versionInfo;
202  delete[] subBlock;
203  throw PluginException("Plugin \"" + pluginName
204  + "\" was linked against charon-core version " + version
205  + "\n\tbut we are using charon-core version "
206  + charonCoreVersion
207  , pluginName,
209  }
210  }
211  delete[] subBlock;
212  }
213  delete[] versionInfo;
214  }
215  catch(PluginException& excpt)
216  {
217  if(_versionCheck < PluginManagerInterface::PluginVersionDiscard)
218  { //show errors as warnings
219  std::regex e ("\\(EE\\)");
220  std::string msg = excpt.what() ;
221  sout << "(WW) " << std::regex_replace (msg,e,std::string("(WW)")) << std::endl ;
222  }
223  else
224  throw ;
225  }
226  } // VersionCheck
227 
228  // load the library
229  hInstLibrary = LoadLibrary(path.c_str());
230 #ifdef MSVC
231  if(!hInstLibrary) {
232  lastErrorCode = GetLastError() ;
233  lastErrorMsg = lastError("LoadLibrary") ;
234  }
235 #endif
236  FileTool::changeDir(oldDir);
237 
238  if (!hInstLibrary) {
239  // error loading dll file
240 #ifdef MSVC
241  std::string addInfo;
242  if (lastErrorMsg.empty()) {
243  addInfo += "This is usually caused by missing dll dependencies.";
244  }
245  else {
246  addInfo += lastErrorMsg ;
247  if(lastErrorCode == ERROR_MOD_NOT_FOUND) {
248  addInfo += "This is usually caused by missing dll dependencies." ;
249  }
250  }
251  throw PluginException(
252  "Failed to load the plugin \"" + pluginName
253  + "\".\n" + addInfo,
255 #else
256  throw PluginException("Failed to load the plugin \"" + pluginName
257  + "\". Maybe the file is damaged.", pluginName,
259 #endif
260  }
261 
262  create = (ParameteredObject*(*)(const std::string&, ParameteredObject::template_type))
263  GetProcAddress(hInstLibrary,"create");
264  if (!create) {
265  lastErrorCode = GetLastError() ;
266  lastErrorMsg = lastError("GetProcAddress") ;
267 
268  FreeLibrary(hInstLibrary);
269  hInstLibrary = NULL;
270 #ifdef MSVC
271  std::string errorMsg;
272  if (lastErrorCode == ERROR_PROC_NOT_FOUND) {
273  errorMsg += "function \"create\" missing in dll file";
274  }
275  else {
276  errorMsg += "Invalid plugin format.\nDescription of the error:\n";
277  errorMsg += lastErrorMsg;
278  }
279  throw PluginException(
280  errorMsg, pluginName,
282 #else
283  throw PluginException(
284  "function \"create\" missing in dll file",
286 #endif
287  }
288 
289  destroy = (void(*)(ParameteredObject*))
290  GetProcAddress(hInstLibrary,"destroy");
291 
292  if (!destroy) {
293  lastErrorCode = GetLastError() ;
294  lastErrorMsg = lastError("GetProcAddress") ;
295  create = NULL;
296  FreeLibrary(hInstLibrary);
297  hInstLibrary = NULL;
298 #ifdef MSVC
299  std::string errorMsg;
300  if (lastErrorCode == ERROR_PROC_NOT_FOUND) {
301  errorMsg += "function \"create\" missing in dll file";
302  }
303  else {
304  errorMsg += "Invalid plugin format.\nDescription of the error:\n";
305  errorMsg += lastErrorMsg;
306  }
307  throw PluginException(
308  errorMsg, pluginName,
310 #else
311  throw PluginException(
312  "function \"destroy\" missing in dll file",
314 #endif
315  }
316 
318  GetProcAddress(hInstLibrary,"getBuildType");
319  if (!getBuildType) {
320  sout << "(WW) No Typecheck in Plugin " << pluginName << ":\n"
321  << "(WW) \tfunction \"getBuildType\" missing in dll file\n"
322  << "(WW) \tno checks for runtime compatibilty are perfomed"
323  << std::endl;
324  }
325  else
326  {
328  {
329 #ifndef _DEBUG
330  FreeLibrary(hInstLibrary);
331  hInstLibrary = NULL;
332  throw PluginException(
333  "This Plugin is build in DEBUG "
334  "configuration while charon-core is in RELEASE Mode.\n"
335  "Plugin will not be used as runtime libraries are incompatible",
337 #endif
338  }
340  {
341 #ifdef _DEBUG
342  FreeLibrary(hInstLibrary);
343  hInstLibrary = NULL;
344  throw PluginException(
345  "This Plugin is build in RELEASE "
346  "configuration while charon-core is in DEBUG Mode.\n"
347  "Plugin will not be used as runtime libraries are incompatible",
349 #endif
350  }
351  }
352 }
354  if (hInstLibrary) {
355  FreeLibrary(hInstLibrary);
356  hInstLibrary = NULL;
357  create = NULL;
358  destroy = NULL;
359  sout << "(DD) Successfully unloaded plugin \"" << pluginName << "\"."
360  << std::endl;
361  } else {
362  throw PluginException("Plugin \"" + pluginName + "\" is not loaded.",
364  }
365 }
366 
368  if (hInstLibrary) {
369  unload();
370  }
371 }
File is a shared object but misses required functions.
charon-core Version Information
#define CHARON_CORE_VERSION
charon-core version string
Definition: configVersion.h:32
virtual void unload()
Unloads the plugin.
there is a missmatch between the current charon-core version and the version the plugin was compiled ...
build_type
defined build type
SplitStream sout
Dummy instance for usage in other files (for interface too).
template_type
Integer which represents a template type.
File is damaged, unreadable, not a shared object or the user access rights are insufficient.
Base class for serializable objects.
Declaration of class ParameterFile.
bool charon_core_DLL_PUBLIC exists(const std::string &file)
Check if file exists.
Definition: FileTool.cpp:154
void(* destroy)(ParameteredObject *)
Function pointer to the destructor of the plugin.
the object was compiled in release mode
int charon_core_DLL_PUBLIC changeDir(const std::string &dir)
Change current working directory.
Definition: FileTool.cpp:91
ParameteredObject *(* create)(const std::string &, ParameteredObject::template_type)
Function pointer to the constructor of the plugin.
std::vector< std::string > & pluginPaths
Paths where the plugins are stored.
Plugin was linked against incompatible runtime library.
std::string pluginName
The name of the Plugin (without prefix "lib" and without extension)
std::string charon_core_DLL_PUBLIC getCurrentDir()
Get current working directory.
Definition: FileTool.cpp:95
Plugin file doesn't exist or is placed in the wrong directory.
std::string & libSuffix
Lib suffix e.g. _d for debug builds.
PluginVersionCheckLevel
info how to handle version information
virtual void load()
Loads the plugin.
the object was compiled in debug mode
While unloading a plugin or deleting an instance: The plugin is not loaded.
PluginManagerInterface::PluginVersionCheckLevel _versionCheck
should the Loader ignore embedded charon-core version strings?
WindowsPluginLoader(const std::string &n, std::vector< std::string > &plpaths, std::string &lSuffix, PluginManagerInterface::PluginVersionCheckLevel versionCheck=PluginManagerInterface::PluginVersionIgnore)
Default constructor.
HINSTANCE hInstLibrary
Points to the loaded library.
virtual ~WindowsPluginLoader()
Default destructor.
Version information for the plugin is missing while explicit version check was requested.
ParameteredObject::build_type(* getBuildType)()
Function pointer to the getBuildType function of the plugin.
Abstract base class for a plugin loader.