Hi!
I am trying to design and build a plugin-system to a project of mine. The basic design is to use DLL's for each plugin. Each plugin have a special function that loads all of its functions as macros to the main engine, which has been passed as a pointer to the loader function which acts as a interface to the main program. Everything of the main program resides in the form of objects within the engine class.
A "macro" is a loaded plugin function which the main engine handles. A macro can call other macros via the engine pointer. A plugin function returns void and takes one parameter: the engine pointer. This way the macro can manipulate the main program.
Now you might be asking: "Doesn't this restrict their use due to their lack of parameters?". Here comes the idea - and the problem. You see, the engine keeps track of a very special object that keeps track of an internal environment, where variables accessable for both writing and reading - both temporary ones (if it's read, it gets deleted automatically) - and permanent ones. These variables are stored in an internal std::map object. The error is - like you've already suspected - an access violation. My guess is that it's due to a violation to the rule where memory allocated in one thread may not be read or written in another, but I can't find the source of the problem.
Please, also note me about design flaws and other design errors.
As you surely would like to have if you want to help, I will post all the involved source and header files.
Headers:
Main program & Plugin - YAMP_Generic.h
#ifndef _YAMP_GENERIC_H_
#define _YAMP_GENERIC_H_
#include <string>
#include <vector>
#include <windows.h>
#include <map>
#include <sstream>
#include <deque>
#include <fstream>
#include <cctype>
#include <iomanip>
#define STLString std::string
#define STLStringStream std::stringstream
#define STLVector std::vector
#define STLMap std::map
#define STLDeque std::deque
#define STLInFileStream std::ifstream
#define STLOutFileStream std::ofstream
#define EndLine std::endl
#define YAMPMain() int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, char *cmdParam, int cmdShow) {
#define YAMPEnd() MSG msg; int status; while ((status = GetMessage(&msg, 0, 0, 0)) != 0) { if (status == -1) { return -1; } DispatchMessage(&msg); } return msg.wParam; }
class YAMP_Engine;
typedef void (*YAMP_Macro) (YAMP_Engine*);
#endif
Main program - YAMP_Engine.h
#ifndef _YAMP_ENGINE_H_
#define _YAMP_ENGINE_H_
#include "YAMP_Generic.h"
#include "YAMP_MacroHandler.h"
#include "YAMP_PluginHandler.h"
#include "YAMP_Environment.h"
class YAMP_Engine
{
public:
YAMP_Engine(HINSTANCE, const char*, UINT);
~YAMP_Engine();
void Init();
void Exit();
YAMP_MacroHandler macro;
YAMP_PluginHandler plugin;
YAMP_Environment env;
private:
void parseArgs(const char *);
void addArg(STLString);
HINSTANCE win32_instance;
UINT win32_cmdshow;
int win32_argi;
};
#endif
Main program & Plugin - YAMP_Environment.h
#ifndef _YAMP_ENVIRONMENT_H_
#define _YAMP_ENVIRONMENT_H_
#include "YAMP_Generic.h"
class YAMP_Engine;
class YAMP_Environment
{
public:
YAMP_Environment();
~YAMP_Environment();
void setVar(STLString name, STLString value="", bool temp=false);
void setVar(STLString name, int value=0, bool temp=false);
void setEngine(YAMP_Engine *_yamp);
int getVarInt(STLString name);
STLString getVarStr(STLString name);
STLVector<STLString> getVarList();
private:
YAMP_Engine *yamp;
STLMap<STLString, STLString> vars;
STLMap<STLString, bool> vars_tempflags;
};
#endif
Main program - YAMP_MacroHandler.h
#ifndef _YAMP_MACROHANDLER_H_
#define _YAMP_MACROHANDLER_H_
#include "YAMP_Generic.h"
#include "YAMP_Variable.h"
class YAMP_Engine;
class YAMP_MacroHandler
{
public:
YAMP_MacroHandler();
~YAMP_MacroHandler();
void setEngine(YAMP_Engine*);
STLVector<STLString> getMacroList();
void registerMacro(STLString, YAMP_Macro macro);
void Error(STLString);
bool macroExists(STLString);
void operator() (STLString name);
private:
STLMap<STLString, YAMP_Macro> macros;
YAMP_Engine *yamp;
};
#endif
Plugin - in_yamp32.h
#ifndef _IN_YAMP32_H_
#define _IN_YAMP32_H_
#define DLLIMPORT __declspec (dllexport)
class YAMP_Engine;
extern "C" DLLIMPORT void getMacroList(YAMP_Engine*);
extern "C" DLLIMPORT void test(YAMP_Engine*);
#endif
Sources
Main program - YAMP_Engine.cpp
#include "include/YAMP_Engine.h"
#include "include/YAMP_CoreMacros.h"
YAMP_Engine::YAMP_Engine(HINSTANCE hinstance, const char *cmdParam, UINT cmdShow)
{
win32_instance = hinstance;
win32_cmdshow = cmdShow;
win32_argi = 1;
parseArgs(cmdParam);
}
YAMP_Engine::~YAMP_Engine()
{
}
void YAMP_Engine::Init()
{
env.setEngine(this);
plugin.setEngine(this);
plugin.Load("plugins\\in_yamp32.dll");
}
void YAMP_Engine::Exit()
{
exit(0);
}
void YAMP_Engine::addArg(STLString arg)
{
if (arg.length() > 0)
{
STLStringStream ss;
ss << "arg_" << win32_argi;
env.setVar(ss.str(), arg);
win32_argi++;
}
}
void YAMP_Engine::parseArgs(const char *args)
{
STLString argstr = args;
STLStringStream *parsed = new STLStringStream();
bool quoted = false;
for(unsigned int i = 0; i < argstr.length(); i++)
{
if (argstr[i] == '"')
{
if (quoted)
{
quoted = false;
addArg(parsed->str());
delete parsed;
parsed = new STLStringStream();
i++;
}
else
{
quoted = true;
i++;
}
}
if (!quoted)
{
if (argstr[i] != ' ')
{
(*parsed) << argstr[i];
}
else
{
addArg(parsed->str());
delete parsed;
parsed = new STLStringStream();
}
}
else
{
(*parsed) << argstr[i];
}
}
if (parsed->str().length() > 0)
{
addArg(parsed->str());
delete parsed;
parsed = new STLStringStream();
}
delete parsed;
}
Main program & Plugin - YAMP_Environment.cpp
#include "include/YAMP_Environment.h"
#include "include/YAMP_Engine.h"
YAMP_Environment::YAMP_Environment()
{
}
YAMP_Environment::~YAMP_Environment()
{
vars.clear();
vars_tempflags.clear();
}
STLVector<STLString> YAMP_Environment::getVarList()
{
STLVector<STLString> r;
for(STLMap<STLString, STLString>::iterator it = vars.begin(); it != vars.end(); it++)
{
r.push_back(it->first);
}
return r;
}
int YAMP_Environment::getVarInt(STLString name)
{
if (vars.find(name) != vars.end())
{
STLString value = vars[name];
if (vars_tempflags[name])
{
vars_tempflags.erase(vars_tempflags.find(name));
vars.erase(vars.find(name));
}
return atoi(value.c_str());
}
else
{
STLStringStream ss;
ss << "No YAMP-environment variable called '" << name << "' is defined.";
MessageBox(0, ss.str().c_str(), "Error", MB_ICONHAND);
return 0;
}
}
STLString YAMP_Environment::getVarStr(STLString name)
{
STLString _name = name;
if (vars.find(_name) != vars.end()) // <--- Access violation
{
STLString value = vars[_name];
if (vars_tempflags[_name])
{
vars_tempflags.erase(vars_tempflags.find(_name));
vars.erase(vars.find(_name));
}
return value;
}
else
{
STLStringStream ss;
ss << "No YAMP-environment variable called '" << _name << "' is defined.";
MessageBox(0, ss.str().c_str(), "Error", MB_ICONHAND);
return "";
}
}
void YAMP_Environment::setVar(STLString name, STLString value, bool temp)
{
vars[name] = value;
vars_tempflags[name] = temp;
}
void YAMP_Environment::setVar(STLString name, int value, bool temp)
{
char *buffer = new char[255];
_itoa_s(value, buffer, 255, 10);
vars[name] = buffer;
vars_tempflags[name] = temp;
delete buffer;
}
void YAMP_Environment::setEngine(YAMP_Engine *_yamp)
{
yamp = _yamp;
}
Main program - YAMP_MacroHandler.cpp
#include "include/YAMP_Generic.h"
#include "include/YAMP_MacroHandler.h"
#include "include/YAMP_Engine.h"
YAMP_MacroHandler::YAMP_MacroHandler()
{
}
YAMP_MacroHandler::~YAMP_MacroHandler()
{
}
void YAMP_MacroHandler::setEngine(YAMP_Engine *_yamp)
{
yamp = _yamp;
}
STLVector<STLString> YAMP_MacroHandler::getMacroList()
{
STLVector<STLString> macrolist;
for(STLMap<STLString, YAMP_Macro>::iterator it = macros.begin(); it != macros.end(); it++)
{
macrolist.push_back(it->first);
}
return macrolist;
}
bool YAMP_MacroHandler::macroExists(STLString name)
{
bool r = true;
if (macros.find(name) == macros.end())
{
r = false;
}
return r;
}
void YAMP_MacroHandler::operator() (STLString name)
{
if (macroExists(name))
{
macros[name](yamp);
}
else
{
STLStringStream ss;
ss << "Running macro: '" << name << "' failed." << EndLine;
ss << " Macro does not exist." << EndLine;
Error(ss.str());
}
}
void YAMP_MacroHandler::Error(STLString error)
{
MessageBox(0, error.c_str(), "Macro Error", MB_ICONHAND);
}
void YAMP_MacroHandler::registerMacro(STLString name, YAMP_Macro macro)
{
if(!macroExists(name))
{
macros[name] = macro;
}
else
{
STLStringStream ss;
ss << "yamp->macro.registerMacro(\"" << name << "\", " << macro << ");" << EndLine;
ss << " Unable to register macro. (A macro by that name is already registered)." << EndLine;
Error(ss.str());
}
}
Plugin - in_yamp32.cpp
#include "include/in_yamp32.h"
#include "../../include/YAMP_Engine.h"
#include "../../include/YAMP_Environment.h"
#include "../../include/YAMP_Generic.h"
#include <windows.h>
void test(YAMP_Engine *yamp)
{
MessageBox(0, yamp->env.getVarStr("message_test").c_str(), "test", MB_ICONHAND);
}
void getMacroList(YAMP_Engine *yamp)
{
YAMP_Macro pt_test = test;
yamp->macro.registerMacro("test", pt_test);
yamp->macro("test");
}
BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH: break;
case DLL_PROCESS_DETACH: break;
case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
}
return TRUE;
}