I'm developing a C++ library for export as a DLL in Visual Studio 2013 (C++ 11), and I'm running into a challenge where the library, once imported as an external dependency by another program, contains the classes that I wanted, but none of the functions of the classes are included.
View of classes within external dependency
I have included one particular class that is supposed to be part of this DLL export.
Here is my RegressionTrainer class header file:
#ifndef REGRESSION_TRAINER_H
#define REGRESSION_TRAINER_H
#include "MachineLearning.h"
#pragma once
#ifndef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#endif
using namespace MachineLearning;
namespace MachineLearningTraining{
public class RegressionTrainer{
public:
DLL_EXPORT RegressionTrainer();
virtual DLL_EXPORT ~RegressionTrainer();
std::vector<sample_type> DLL_EXPORT getInputData();
std::vector<double> DLL_EXPORT getAugmentedOutputs();
std::vector<double> DLL_EXPORT getNonAugmentedOutputs();
protected:
pugi::xml_parse_result DLL_EXPORT setDataFile(pugi::xml_document &doc, char* file_name);
void DLL_EXPORT setDataFolder(char* folder_name);
std::vector<char*> DLL_EXPORT findDataFiles();
char* data_folder;
std::vector<char*> file_names;
std::vector<sample_type> input_data;
/*
sample_type m;
m(0, 0) = 14.86;
m(1, 0) = 0.24;
*/
std::vector<double> augmented_outputs;
std::vector<double> non_augmented_outputs;
pugi::xml_parse_result result;
void DLL_EXPORT setInputData();
void DLL_EXPORT setAugmentedOutputs();
void DLL_EXPORT setNonAugmentedOutputs();
virtual int DLL_EXPORT trainAugmentedModel();
virtual int DLL_EXPORT trainNonAugmentedModel();
};
}
#endif
Here are the contents of MachineLearning.h:
#include <vector>
#include <iostream>
#include <exception>
#include <fstream>
#include <string>
#include <dlib/svm.h>
#include "pugixml.hpp"
namespace MachineLearning{
// Here we declare that our samples will be 2 dimensional column vectors.
typedef dlib::matrix<double, 3, 1> sample_type;
// Now we are making a typedef for the kind of kernel we want to use. I picked the
// radial basis kernel because it only has one parameter and generally gives good
// results without much fiddling.
typedef dlib::radial_basis_kernel<sample_type> kernel_type;
}
And here is my RegressionTrainer.cpp file:
#include "Stdafx.h"
#include "RegressionTrainer.h"
#include "dirent.h"
using namespace std;
using namespace dlib;
using namespace MachineLearning;
namespace MachineLearningTraining{
RegressionTrainer::RegressionTrainer(){
file_names = findDataFiles();
}
RegressionTrainer::~RegressionTrainer(){
}
pugi::xml_parse_result RegressionTrainer::setDataFile(pugi::xml_document &doc, char *file_name){
return doc.load_file(file_name);
}
void RegressionTrainer::setDataFolder(char *folder_name){
data_folder = folder_name;
}
std::vector<char*> RegressionTrainer::findDataFiles(){
DIR *dir;
struct dirent *ent;
std::vector<char*> file_names;
if ((dir = opendir(data_folder)) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir(dir)) != NULL) {
file_names.push_back(ent->d_name);
}
closedir(dir);
}
else {
/* could not open directory */
perror("Could not open directory");
}
return file_names;
}
std::vector<sample_type> RegressionTrainer::getInputData(){
return input_data;
}
std::vector<double> RegressionTrainer::getAugmentedOutputs(){
return augmented_outputs;
}
std::vector<double> RegressionTrainer::getNonAugmentedOutputs(){
return non_augmented_outputs;
}
void RegressionTrainer::setInputData(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node measures = doc.child("case").child("measures");
sample_type m;
int count = 0;
for (pugi::xml_node measure = measures.first_child(); measure; measure = measure.next_sibling()){
m(count, 0) = measure.text().as_double();
count++;
}
input_data.push_back(m);
}
}
void RegressionTrainer::setAugmentedOutputs(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviation");
augmented_outputs.push_back(output.text().as_double());
}
}
void RegressionTrainer::setNonAugmentedOutputs(){
pugi::xml_document doc;
for (unsigned i = 0; i < file_names.size(); i++){
setDataFile(doc, file_names[i]);
std::cout << "Load result: " << result.description() << endl;
pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviationAR");
augmented_outputs.push_back(output.text().as_double());
}
}
int RegressionTrainer::trainAugmentedModel(){
return 0;
}
int RegressionTrainer::trainNonAugmentedModel(){
return 0;
}
}
Would welcome any thoughts!
Your code is confusing:
public class RegressionTrainer
Is this C++ or C#? Some other part of your code clearly shows it is C++. Therefore you must always put exact (or syntactically same) code.
Back to your problem, you cannot export members of a class. You must export entire class from a DLL.
Then issues start to begin. For one, you must expose (export or not, doesn't matter) all types the class uses (pugi::xml_parse_result for example). Then, you need to take care of different compilers versions (Even VC2015, various versions, debug/release, compiler settings etc). For example a vector on VC2015 debug build would be different from a release version. Service pack would complicate the problem.
In short: Don't export entire class having data-members. Even if entire data is private, you will need to export entire class, so that client (consumer of DLL) can compile and link (to code) properly.
So, what's the solution?
Well, just export a helper class:
class DLL_EXPORT RegressionTrainerHelper
{
RegressionTrainer* pCoreClass;
};
Expose (export) all required methods (for the client) from this class. You'd just need to forward the calls from helper to real class.
Now, you might wonder, you will need to export the underlying type RegressionTrainer, and you are back to same problem.
Well, yes and no. If this helper class is being compiled within DLL, RegressionTrainer would be real thing. If not, just:
typedef int RegressionTrainer;
Pointer of anytype is of same size (32-bit or 64-bit). Hence the size of entire exported class would always match the size in DLL and in EXE.
EDIT
For example, there is a XML parser class, ParseXML, but it uses comlpex data members, you have a method Parse.
class ParseXML
{
// complex data members
// some private OR public datamembers and functions, you dont want/need to export
public:
void Parse(const char*); // or something different
};
You would want to export only Parse, through helper class.
class DLL_EXPORT Exported_ParseXML
{
private:
ParseXML* pCoreXMLInstance;
public:
Exported_ParseXML()
{
// implementation in CPP
pCoreXMLInstance = new ParseXML();
}
// The function!
void Parse(const char* pData)
{
// FORWARD
pCoreXMLInstance->Parse(pData);
}
The client will simply use the exported class:
Exported_ParseXML parser;
parser.Parse("XML-data");
The server compiler (DLL) would see ParseXML as real class type. But the client (EXE, or other DLL), would need see ParseXML as int. You got to figure out this!
Related
I'm working on a cross platform project where I have to load dynamic libraries. I therefore created a very basic platform independent templated wrapper class around dlopen/loadLibrary that act like a factory class and return unique_ptr<T>s of the lib object.
But this is very unpractical in most of the cases, I therefore wondered, how can I design a simple wrapper that can do the following:
Load a lib and become a factory on instances of that lib objects for when we have to use multiple instances of the lib object
Create a self managed entity (a struct containing a unique ptr on the object and the handle on the lib maybe ?) for when we want to use only one instance of a lib object and not bother with a factory
Use the library provided deleter (symbol "destroy") instead of the default one
Does this exist ? If not, what would be the best way to do this ?
My implementation so far:
#pragma once
#include <memory>
#include <iostream>
#if _WIN32
#include <Windows.h>
#else
#include <dlfcn.h> //dlopen
#endif
namespace utils
{
template <class T>
class DynLib
{
public:
DynLib() = default;
~DynLib()
{
if (handle_)
closeLib(handle_);
};
private:
bool printLibError()
{
#if _WIN32
std::cerr << GetLastError() << std::endl;
#else
std::cerr << dlerror() << std::endl;
#endif
return false;
}
void *openLib(const std::string &libName)
{
#if _WIN32
return LoadLibrary((libName + ".dll").c_str());
#else
return dlopen((libName + ".so").c_str(), RTLD_LAZY);
#endif
}
int closeLib(void *libHandle)
{
#if _WIN32
return FreeLibrary((HMODULE)libHandle);
#else
return dlclose(libHandle);
#endif
}
void *loadSymbol(void *libHandle, const char *sym)
{
#if _WIN32
return (void *)GetProcAddress((HMODULE)libHandle, sym);
#else
return dlsym(libHandle, sym);
#endif
}
public:
bool open(const std::string &filename, const char *csym = "create", const char *dsym = "destroy")
{
if (!(handle_ = openLib(filename)))
return printLibError();
if (!(create = (T * (*)()) loadSymbol(handle_, csym)))
return printLibError();
if (!(destroy_ = (void (*)(T *))loadSymbol(handle_, dsym)))
return printLibError();
return true;
}
std::unique_ptr<T> createUnique()
{
return (std::unique_ptr<T>(create()));
}
private:
void *handle_{nullptr};
T *(*create)();
void (*destroy_)(T *);
};
}
Have you considered Boost.DLL library?
It provides examples on how implement plugins/factory methods.
This is a very strange issue. I have two classes: a custom console class (CConsole) and a test class (CHashTableTest) which I've made to play around with maps and unordered_maps to learn how they work.
In my console class I have a public static member variable of CConsole that exposes a static console object to the rest of the project so that I can write to this console when ever I want. This works fine for all of my classes including the test class but only when that test classes uses map and not unordered_map!
The error that I receive is:
error LNK2001: unresolved external symbol "public static class CConsole CConsole:output" (?output#CConsole##2V1#A)
It comes from the class that calls the methods on the test class and not the test class itself but nothing strange is happening in the calling class, it simply instantiates the CHashTableTest object (passing in a CConsole object) and calls Add and Get on it. It is placed in a separate project but this isn't a problem when I use map as the static member variable has been made external using _declspec(ddlexport).
The solution is setup in Visual Studio 2012, the CConsole and CHashTableTest classes are in a DLL project which is an external reference of a Unit Test project where the calling code exists.
The CConsole and CHashTableTest files:
Console.h
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <time.h>
// Defines a set of output modes for the CConsole class. This will affect what output the console will target.
// The default is COUT.
enum _declspec(dllexport) EConsoleMode
{
// Output sent to a CConsole object will be printed using cout.
COUT,
// Output sent to a CConsole object will be printed to a file.
OUTPUT_FILE,
// Output sent to a CConsole object will be printed using OutputDebugString.
OUTPUT_DEBUG_STRING,
// Output sent to a CConsole object will be printed using Debug::WriteLine.
DEBUG_WRITE_LINE,
// Output sent to a CConsole object will be printed using Console::WriteLine.
CONSOLE_WRITE_LINE,
// Output sent to a CConsole object will not be printed.
NO_OUTPUT,
};
// An output wrapper class that allows logging and redirecting of log and debugging messages to different
// output targets.
class _declspec(dllexport) CConsole
{
public:
static CConsole output;
// Constructs a CConsole object with a specific console mode, default is COUT.
CConsole(EConsoleMode mode = COUT, const char* filePath = "C:/output.txt");
~CConsole(void);
// Gets the mode of this CConsole object.
EConsoleMode GetMode();
const char* GetFilePath();
// Sets the output file path of this CConsole object.
void SetFilePath(const char* filePath);
void TimeStamp();
// Logs a message with this specific CConsole object. An indirect call to an operator overload of
// CConsole << Type
template <typename T> void Log(const T &message);
protected:
// The mode of this CConsole object.
EConsoleMode m_mode;
// The file path of the output file for this CConsole object.
const char* m_filePath;
};
// Operator overload of CConsole << Type, queries the mode of the given CConsole object and
// selects the appropriate output method associated with the mode.
template <typename T>
CConsole operator<< (CConsole console, const T &input)
{
switch(console.GetMode())
{
case COUT:
{
std::cout << input << "\n";
break;
}
case OUTPUT_FILE:
{
ofstream fout;
fout.open (console.GetFilePath(), ios::app);
fout << input;
fout.close();
break;
}
#if ON_WINDOWS
case OUTPUT_DEBUG_STRING:
{
OutputDebugString(input);
break;
}
case DEBUG_WRITE_LINE:
{
Debug::WriteLine(input);
break;
}
case CONSOLE_WRITE_LINE:
{
Console::WriteLine(input);
break;
}
#endif
case NO_OUTPUT:
{
break;
}
default:
{
std::cout << input;
break;
}
}
return console;
}
// Logs a message by calling the operator overload of << on this CConsole object with the message
// parameter.
template <typename T>
void CConsole::Log(const T &message)
{
this << message;
}
#endif
Console.cpp
#include "Console.h"
CConsole CConsole::output = *new CConsole(OUTPUT_FILE, "C:/LocalProjects/---/output.txt"); // Known memory leak here, discussed in comments
// Constructs a CConsole object by assigning the mode parameter to the
// m_mode member variable.
CConsole::CConsole(EConsoleMode mode, const char* filePath)
{
m_mode = mode;
m_filePath = filePath;
TimeStamp();
}
CConsole::~CConsole(void)
{
//Log("\n\n");
}
// Returns the current mode of this CConsole object.
EConsoleMode CConsole::GetMode()
{
return m_mode;
}
const char* CConsole::GetFilePath()
{
return m_filePath;
}
void CConsole::TimeStamp()
{
if(m_mode == OUTPUT_FILE)
{
std::ofstream file;
file.open (m_filePath, std::ios::app); //
std::time_t currentTime = time(nullptr);
file << "Console started: " << std::asctime(std::localtime(¤tTime));
file.close();
}
}
void CConsole::SetFilePath(const char* filePath)
{
m_filePath = filePath;
}
HashTableTest.h
#ifndef _HASH_TABLE_TEST_H_
#define _HASH_TABLE_TEST_H_
#include <unordered_map>
#include <map>
#include <vector>
#include "Debuggable.h"
#include "Console.h"
using namespace std;
//template class __declspec(dllexport) unordered_map<int, double>;
struct Hasher
{
public:
size_t operator() (vector<int> const& key) const
{
return key[0];
}
};
struct EqualFn
{
public:
bool operator() (vector<int> const& t1, vector<int> const& t2) const
{
CConsole::output << "\t\tAdding, t1: " << t1[0] << ", t2: " << t2[0] << "\n"; // If I delete this line then no error!
return (t1[0] == t2[0]);
}
};
class __declspec(dllexport) CHashTableTest : public CDebuggable
{
public:
CHashTableTest(CConsole console = *new CConsole());
~CHashTableTest(void);
void Add(vector<int> key, bool value);
bool Get(vector<int> key);
private:
CConsole m_console;
unordered_map<vector<int>, bool, Hasher, EqualFn> m_table; // If I change this to a map then no error!
};
#endif
Just to be clear, if I change 'unordered_map' above to 'map' and remove the Hasher function object from the template argument list this will compile. If I don't I get the linker error. I haven't included HashTableTest.cpp because it contains practically nothing.
EDIT
I'm not sure if I'm using unordered_map properly here is the implementation for the CHashTableTest class:
HashTableTest.cpp
#include "HashTableTest.h"
CHashTableTest::CHashTableTest(CConsole console) : CDebuggable(console)
{
}
CHashTableTest::~CHashTableTest(void)
{
}
void CHashTableTest::Add(vector<int> key, bool value)
{
m_table[key] = value;
}
bool CHashTableTest::Get(vector<int> key)
{
return m_table[key];
}
When a DLL's generated import library is being linked with an consuming EXE (or another DLL) the declspecs of the items being brought in should be declspec(dllimport) on the receiver-side. When you're building the actual DLL those same items are made available by usage of declspec(dllexport). Your code has the latter, but not the former.
This is normally achieved by a single preprocessor macro that is defined only for your DLL project in the preprocessor section of the compiler configuration. For example in your header(s) that are declaring things exported from your DLL:
#ifndef MYDLL_HEADER_H
#ifdef MYDLL_EXPORTS
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
class EXPORT CConsole
{
// stuff here.
};
#endif
Doing this will tell the compiler when building the DLL to export the class members, including class statics, because MYDLL_EXPORTS is defined in the preprocessor configuration flags for your DLL project.
When building a consuming project, do NOT define MYDLL_EXPORTSin the preprocessor configuration of the compiler settings. It will thusly use dllimport, rather than dllexport. That should square away the linker to know where to look for the things it needs.
Best of luck.
I have a dll plugin ,myDLL.cpp, which has the following code:
#include "myDLL.h"
#include "MainApp.h"
class A{
public:
// NOTE: SomeType is defined in main application code.
A(SomeType* data) : m_data{data}
void MemberFunc(){
// NOTE: do_something should access data in main application.
m_data->do_something();
}
private:
SomeType* m_data;
};
// exported function
A* createInstance(SomeType* data){
return new A(data);
}
In the main application I have:
stc::vector<int> IntArray;
class SomeType{
SomeType(){
IntArray.resize(1000);
}
void do_something(){
// manipulate IntArray's contents.
IntArray[rand() % IntArray.size()] = rand();
}
};
typedef A*(_createInstance)(SomeType*);
void main(){
// Load the Dll and get createInstance()
_createInstance createInstance = LoadMyDLL();
SomeType someType;
A* a = createInstance(&someType);
a->MemberFunc();
// Free the instance of A and unload the DLL.
UnloadMyDLL(a);
}
The dll code now can use the API of the main application, but it can't access the right data. When I put a break point at m_data->do_something(); and enter the method call, then I see that IntArray is empty. What am I doing wrong and how do I solve the problem?
I could succesfully run your example without experiencing your problem:
I assumed that in your headers there are only the class definitions and not definition of its member functions
So I buid a DLL project. But it failed producing the dll because of the missing do_something() function. Normal, because with your architecture it should be defined in the application not in the DLL ! I could solve the problem by making do_something() virtual.
Then I build the application. I first chose for simplicity and linked the application with the DLL (no loading issues). Unfortunately it didn't find either MemberFunc() nor createInstance(). I could solve this by exporting the DLL entry.
FInally I updated the application, to have a dynamic load of the library. For avoiding unnecessary hassle of having to find back MemberFunc(), I made it virtual as well.
In all the tests above, I had absolutely no problem. IntArray was always correct. In debug mode I could see it with the expected content, as soon as it entered the scope.
My conclusion, from these tests and looking at your snippet (expecially with doSomething not being virtual): your problem is probably that have defined SomeType class with functions and eventually IntArray in Main.h.
If this is the case, your DLL refers to its own copy of these elements and not as you think to those in main ! THis explains why you don't see the expected values !
Solution:
File MainApp.h:
class SomeType{
public:
SomeType();
virtual void do_something();
};
File MyDLL.h:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
class A {
public:
A(SomeType* data);
virtual void MemberFunc(); // access through vtable. No need to link
private:
SomeType* m_data;
};
extern "C" { // use unmangled name for easo of use of dynamic loaded DLL
MYDLL_API A* createInstance(SomeType* data);
};
File MyDLL.cpp:
#define MYDLL_EXPORTS
#include "MainApp.h"
#include "MyDLL.h"
A::A(SomeType* data) : m_data{ data } {}
void A::MemberFunc(){ m_data->do_something(); }
extern "C" {
MYDLL_API A* cdecl createInstance(SomeType* data){ return new A(data); }
}
File main.cpp:
#include <Windows.h>
#include <iostream>
#include <vector>
#include "MainApp.h"
#include "MyDLL.h"
using namespace std;
vector<int> IntArray;
SomeType::SomeType(){
IntArray.resize(1000);
IntArray[0] = 1; IntArray[1] = 101; IntArray[2] = 10101;
}
void SomeType::do_something(){
for (int i = 0; i < 4; i++) // read
cout << IntArray[i] << endl;
IntArray[3] = 2702; // write
}
int main(int ac, char**av)
{
HINSTANCE LoadMe = LoadLibrary(L"MyDLL.dll");
if(LoadMe != 0)
cout << "DLL Library successfully loaded!\n";
else throw exception("DLL library failed to load!\n");
typedef A*(*_createInstance)(SomeType*);
_createInstance fcreateInstance = (_createInstance) GetProcAddress(LoadMe, "createInstance");
if (fcreateInstance)
cout << "DLL function found !\n";
else throw exception("Function not found in DLL!\n");
SomeType someType;
A* a = fcreateInstance(&someType);
a->MemberFunc();
cin.get();
}
Ok so I am trying to create a runtime DLL for another application that can be called using GetProcAddress.
So I have built the DLL in VS2012 C++ Express (Header and source below) and it returns NULL even when dll is in same folder as exe. So this lead me to believe there was an issue with the dllmain function. So I began looking through MSDN and found the following link
http://msdn.microsoft.com/en-us/library/vstudio/988ye33t.aspx
It states that it is already taken care for me when I create dll template which I did following this MSDN link.
http://msdn.microsoft.com/en-us/library/ms235636%28v=vs.80%29.aspx
So I decided to try build the DLL in release mode (thinking something about this might be the issue) but I get a linker error saying entry point must be define. It builds fine in debug mode but not in release mode. I assume I am missing something simple here.
Error 1 error LNK1561: entry point must be defined C:\Users\ProRip\Documents\Visual Studio 2012\Projects\PhantomAdapter\AdapterDLL\LINK AdapterDLL
Header
#ifdef PHANTOMADAPTER_EXPORTS
#define PHANTOMADAPTER_API __declspec(dllexport)
#else
#define PHANTOMADAPTER_API __declspec(dllexport)
#endif
#using <mscorlib.dll>
#using <system.dll>
using namespace System;
using namespace System::IO::Ports;
using namespace System::Threading;
namespace PhantomAdapter
{
class PhantomAdapter
{
public:
static PHANTOMADAPTER_API int open();
static PHANTOMADAPTER_API int close();
//static PHANTOMADAPTER_API double init(bool );
//static PHANTOMADAPTER_API int noDevices();
static PHANTOMADAPTER_API int angle(double& angle);
static PHANTOMADAPTER_API int torque(double torque);
static PHANTOMADAPTER_API int ready();
};
public ref class SerialPort
{
private:
static System::IO::Ports::SerialPort^ p1;
public:
static int openPort();
static void closePort();
static int read();
static void send(Byte data);
static int check();
};
}
Source
#include "stdafx.h"
#include "stdafx.h"
#include "PhantomAdapter.h"
#include <stdexcept>
using namespace std;
namespace PhantomAdapter
{
int PhantomAdapter::open()
{
int flag=0;
flag=SerialPort::openPort();
return flag;
}
int PhantomAdapter::close()
{
SerialPort::closePort();
return 1;
}
int PhantomAdapter::angle(double& angle)
{
SerialPort::send(0x82);
angle = (SerialPort::read() * 255) + (SerialPort::read());
angle = angle*(6.2832/512);
return 1;
}
int PhantomAdapter::torque(double torque)
{
return 1;
}
int PhantomAdapter::ready()
{
return SerialPort::check();
}
int SerialPort::openPort()
{
bool check=0;
p1 = gcnew System::IO::Ports::SerialPort();
p1->BaudRate = 57600;
p1->PortName = "COM3";
if(p1->IsOpen)
return 0;
else {
p1->Open();
return 1;
}
}
int SerialPort::check()
{
array<String^>^ serialPorts = nullptr;
int flag=0;
serialPorts = p1->GetPortNames();
for each(String^ port in serialPorts)
{
if(port=="COM3")
flag=1;
}
return flag;
}
void SerialPort::closePort()
{
p1->Close();
}
void SerialPort::send(Byte data)
{
array<unsigned char>^ buffer = gcnew array<Byte>(1);
buffer[0] = (char)data;
p1->Write(buffer,0,1);
}
int SerialPort::read()
{
return p1->ReadByte();
}
}
In your VS project, can you check the settings:
1. Configuration Properties > General > Configuration Type? Is this set to .dll in case of Release mode?
2. Configuration Properties > Linker > System > SubSystem? Is this the same between the 2 modes?
You cannot call a .NET method (and C++/CLI methods are .NET methods) from native code via LoadLibrary/GetProcAddress. .NET methods and classes are not exported that way. You will always get a NULL pointer.
What you can do:
Write a COM component in .NET to reference in plain C++
Export normal, plain C++ methods that call .NET code only internally
Import a managed dll into a managed (the same C++/CLI or C# or any other .NET language) executable by using the Assembly class and it's methods.
Say I have a DLL right that contains this:
//DLL
class foo {
static __declspec int Add(int a, int b)
{
return a+b
}
}
How Do I call it call the GetProc Address? I.e:
HINSTANCE hFoo = LoadLibrary("Foo.dll");
int* proc = NULL;
proc = (int*) GetProcAddress(hFoo, ??????);
//Main Exec linked to dll
How in the world would you get the address of a class created in a dll using GetProcAddress?
edtheprogrammerguy is right.
Here is more informations on how to get class properly exposed:
You need to prefix with the attribute :
__declspec(dllexport)...
all the features you want to expose.
See this.
Example for a C function:
__declspec(dllexport) int __cdecl Add(int a, int b)
{
return (a + b);
}
This can be simplified using MACROS: everything is explained on this helpful page.
For C++ classes, you only need to prefix each class (not every single method)
I usually do it that way :
Note : The following also ensures portability...
Include File :
// my_macros.h
//
// Stuffs required under Windoz to export classes properly
// from the shared library...
// USAGE :
// - Add "-DBUILD_LIB" to the compiler options
//
#ifdef __WIN32__
#ifdef BUILD_LIB
#define LIB_CLASS __declspec(dllexport)
#else
#define LIB_CLASS __declspec(dllimport)
#endif
#else
#define LIB_CLASS // Linux & other Unices : leave it blank !
#endif
Usage :
#include "my_macros.h"
class LIB_CLASS MyClass {
}
Then, to build, simply :
Pass the option -DBUILD_LIB to the usual compiler command line
Pass the option -shared to the usual linker command line
You can't get the address of a class from a .dll. If you want to use a class instance from a .dll, use dllexport/dllimport, which lets you export the class and use it as if it were declared locally.
Reference from Microsoft:
http://msdn.microsoft.com/en-us/library/81h27t8c(v=vs.80).aspx
I was trying to create an example of Explicit Linking Here is the example I finally came up with, I apologize for not specifically mentioning this earlier.
Here we go:
//DLL
#include "main.h"
#include <windows.h>
#include <stdexcept>
using namespace std;
class FOO{
static __declspec double ADD(double a, double b)
{
return a+b;
}
}
//EXEC
#include <windows.h>
#include <iostream>
#include <stdio.h>
using namespace std;
typedef double (*MYPROC)(double, double);
int main()
{
double d1 = 10;
double d2 = 30;
double retval;
MYPROC procx = NULL;
DWORD err;
HINSTANCE hDll = LoadLibrary("DynamicLinkTester.dll");
if(hDll != NULL)
{
cout << "Success";
procx = (MYPROC) GetProcAddress(hDll, "_ZN7MathDLL5MathX3ADDEdd");
if(NULL != procx )
{
retval= (procx)(d1, d2);
cout << retval;
}
}
}
If anyone was wondering the same thing as me:
While you can't explicitly call a class/object from a dll you can call its method.