Archived

This topic is now archived and is closed to further replies.

kindfluffysteve

dll help visual C++ 6.0

Recommended Posts

Hello, could somebody please explain to me how to put class methods into a .dll? My situation is this. I''m writing a flightsim, and want to enable the possibilities of bright users to modify some sort of cockpit .dll my sim runs as a win32 console app using glut. I have ofcourse tried googling for hours and never really quite getting the answer I''m after - Its easy to stick c functions into a dll, but the rest - I''m having some difficulty with. http://www.thunder-works.com

Share this post


Link to post
Share on other sites
mputters    126
quote:
Original post by TheNamelessOne
Funny thing i was just looking at this, but i was looking at run-time loaded dlls. anyway check this out

edit: spelling


[edited by - TheNamelessOne on September 2, 2003 12:52:05 AM]


That code in the URL you gave has some flaws (the kind of flaw that leads to a crash).

Share this post


Link to post
Share on other sites
TheNamelessOne    122
quote:
Original post by mputters
That code in the URL you gave has some flaws (the kind of flaw that leads to a crash).



It was really more for the concept, but here''s working code for visual c++ 6.0.

FooInterface.h

//-------- FooInterface.h --------//
#ifndef FOOINTERFACE_H
#define FOOINTERFACE_H

class IFoo
{
public:
virtual int GetNumber() = 0;
virtual void SetNumber( const int & ) = 0;
};

#endif // FOOINTERFACE_H


FooClass.h

//-------- FooClass.h --------//
#ifndef FOOCLASS_H
#define FOOCLASS_H

#include "FooInterface.h"

class FooClass ublic IFoo
{
public:
FooClass();
virtual int GetNumber();
virtual void SetNumber( const int & );
private:
int number;
};

#endif // FOOCLASS_H


FooClass.cpp

//-------- FooClass.cpp --------//
#include "FooClass.h"

FooClass::FooClass()
{
number = 0;
}

int FooClass::GetNumber()
{
return number;
}

void FooClass::SetNumber(const int &arg)
{
number = arg;
}


DllMain.cpp

//-------- DllMain.cpp --------//
#define __dll__
#include "DllExports.h"
#include <windows.h>

IFoo* TheFoo = NULL;

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}

IMPEXP void* CreateFooClassInstance()
{
TheFoo = (IFoo*)new FooClass;
return (void*)TheFoo;
}

IMPEXP void* DeleteFooClassInstance()
{
delete TheFoo;
return NULL;
}


DllExports.h

//-------- DllExports.h --------//
#ifndef DLLEXPORTS_H
#define DLLEXPORTS_H

#ifdef __dll__
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif // __dll__

#include "FooClass.h"


extern "C" IMPEXP void* CreateFooClassInstance();
extern "C" IMPEXP void* DeleteFooClassInstance();


#endif // DLLEXPORTS_H


ExeMain.cpp

//-------- ExeMain.cpp --------//
#include "FooInterface.h"
#include <windows.h>
#include <iostream.h>
#include <conio.h>

typedef void* (*pvFunctv)();

int main(int argc, char* argv[])
{
HINSTANCE hdll = NULL;
IFoo* piFoo = NULL;
pvFunctv CreateFoo;
pvFunctv DeleteFoo;

hdll = LoadLibrary("FooDll.dll"); // load the dll
CreateFoo = (pvFunctv)GetProcAddress( hdll, "CreateFooClassInstance" );

piFoo = (IFoo*)CreateFoo(); // get pointer to object

piFoo->SetNumber(8); // start using the object
cout << "Foo::number is epual to: "
<< piFoo-> GetNumber()
<< endl;

cout << "Enter new value for Foo::number: ";
int temp;
cin >> temp;
piFoo->SetNumber(temp);
cout << "Foo::number is now: " << piFoo->GetNumber()
<< endl;
getch();

DeleteFoo = (pvFunctv)GetProcAddress( hdll, "DeleteFooClassInstance" );
DeleteFoo(); // delete the object

piFoo = NULL;
FreeLibrary(hdll); // free the dll

return 0;
}



Share this post


Link to post
Share on other sites
TjoekBezoer    122
typedef void* (*pvFunctv)();

Can someone explain to me what that line does exactly?

I know its a typedef for a void pointer, but I can''t figure out what the (*pvFunctv)() part does. Thanks!

Greetings,

TjoekBezoer

Share this post


Link to post
Share on other sites
SiCrane    11839
It declares a typedef for a pointer to a function that takes no arguments and returns a void * pointer. By casting the result of GetProcAddress() to a pvFunctv, you can call the function pointer like a function that shares its signature.

Share this post


Link to post
Share on other sites
TjoekBezoer    122
Ok thanks

But now, I am experimenting some myself. I am trying to load a dll with one non-virtual class in it.

The class alone works, no problems with it, but as soon as i call it in my main program i get linking errors (unresolved symbols when calling methods)

Part of my main code:


typedef void* (*voidFunc)();
void main() {
HINSTANCE handle;
Stack* myStack;
voidFunc CreateStack;

handle = LoadLibrary("DevCentral CInDLL.dll");
CreateStack = (voidFunc)GetProcAddress(handle, "returnStackPtr");
myStack = (Stack*)CreateStack();

int intResult;

myStack->Push(5);
myStack->Pop(intResult);

cout << intResult;
}


So i try to call returnStackPtr from my DLL, which looks like this:

DLL_API void* returnStackPtr() {
Stack* ptrStack = new Stack(10);
return static_cast(ptrStack);
}


DLL_API being dllexport in this case.

During the linking process I get these errors:

main.obj : error LNK2019: unresolved external symbol "public: bool __thiscall Stack::Pop(int &)" (?Pop@Stack@@QAE_NAAH@Z) referenced in function _main
main.obj : error LNK2019: unresolved external symbol "public: bool __thiscall Stack::Push(int)" (?Push@Stack@@QAE_NH@Z) referenced in function _main


My problem is that I don't see what I am doing wrong

Thanks for your help

Greetings,

TjoekBezoer

[edited by - tjoekbezoer on March 31, 2004 7:55:41 PM]

edit(AR): don't stretch the tables, thanks.

[edited by - Andrew Russell on April 1, 2004 7:39:08 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
You are including the header file in main()''s .cpp file?

Share this post


Link to post
Share on other sites
TerrorFLOP    100
Mmmm...

Class Methods in dlls...

The BEST way is to define your class as a COM class!!!

COM... and other COM based languages like DCOM, are, believe it or not, the ultimate examples of FULLY Object Oriented Programming (OOP).

By the way, I say language.... Well, you could use the Interface Defintion Language IDL, which COM is based on... Using that will even allow you to write class dlls for other languages such as Visual Basic, Java and Pascal etc etc...

But if you''re language platform is strictly C / C++... Then you don''t even have to learn IDL... Simply sprinkle your C++ code with some COM runtime functions and you are away!!!

I cannot emphasize just what an impact COM based dll programming has had on me. It REALLY is what C++ should have been. You know, encapsulation, black box component technology, plug ins etc etc...

With COM based dlls....You can change the class completely... even change the number or order of class members... so long as the class interface remains static... You can totally rewrite the class dll... And if you wish to add new features... COM can do that too...

Anyway, I''m waffling (COM is like a religion to me)... If you''re
REALLY interested in writing totally upgradeable class dlls in C++... COM.... or a COM based approach IS the ONLY way to go...

Here''s a book which really made me see the light in the REAL power of distributive programming:

Essential COM: By Don Box

It shows you WHY COM was invented, to solve problems like sharing class dlls between programmers and users alike... And then takes you through SIMPLE steps towards building your own COM based dlls...

Anyway, check it out man!!! The book won''t take more than a few weeks to read... And to be honest... chapters 1 - 3, 4 is all you really need to write COM based dlls.

And remember... DirectX, OpenGL, and countless other APIs are infact COM based...

If you want to know more... I''ll send you my email...

Hope this helps!!!

Share this post


Link to post
Share on other sites
SiCrane    11839
quote:
Original post by TjoekBezoer
But now, I am experimenting some myself. I am trying to load a dll with one non-virtual class in it.

Think about it for a moment. You''ve gotten a pointer to an object, and you''re trying to call a non-virtual function. That means the class''s member function implementation needs to be known at link time. However, you''re trying to put the implementation of the class in a DLL that you load with LoadLibrary(), which means that the class''s member function implementation doesn''t get loaded until runtime. These are two incompatible situations.

You should either use an abstract interface class as shown in the post above, or you need to do a link time specification of the DLL, in which case you can bind a non-virtual function to a dll procedure properly. (i.e. use __declspec(dllexport) on the class instead of the factory methods and specify the dll in your linker settings.)

Share this post


Link to post
Share on other sites
TerrorFLOP    100
SiCrane...

Point taken... But COM has ways around this which are TOTALLY
transparent to the programmer.

Consider the following example of setting up a DirectDraw7 object via COM:

// Declare a pointer or interface to a DirectDraw 7.0 object:
LPDIRECTDRAW lpDD7;

// Initialize the COM runtime:
if (FAILED (CoInitialize (NULL)) return FALSE;

// Now, use COM to locate DirectDraw on the local, or even a
// REMOTE system (yes... COM can do this!!!) and initialize
// the pointer / interface:
if (FAILED (CoCreateInstance (CLSID_DirectDraw, NULL, CLSCTX_ALL, &IID_IDirectDraw7, &lpDD7))) return FALSE;

// K... Now lpDD7 basically points to the default graphics card.
// All we have to do is initialize it and we''re home dry!!!
if (lpDD7->Initialize (NULL)) return FALSE;

// When the program ends... Its good practise to unintialize
// COM:
CoUnitialize ();

And thats it!!!

okay, so it took some steps to get a DirectDraw object... But
you could write a function which does all this anyway... And
in fact Microsoft did exactly that (called DirectDrawCreateEx).

The point I''m trying to make, is that this is how large
organizations implement class dlls... With DirectDraw being
just a small part... a class object... of the DirectX runtime...
which itself is a dll.

And if this is good for Microsoft and open source projects like
OpenGL (yeap... thats COM too)... Then it should be good enough
for us bedroom programmers.

As I said ealier, you can even totally mangle up your class and
even let Visual Basic clients or Java clients use your classes.
So long as you don''t change the protoypes of the class methods
themselves, you have almost unlimited freedom to change your
classes.

And they''re in dlls... Simply plug in your new classes and away
you go... No compiling... no linking (at build time anyway)... no problems.

Most professional programmers program this way... I''m not sure
about the COM aspect of their approach, but I''m willing to bet
they use COM based techniques.

Hell... How''d you think patches are created??? They''re simply
dlls that jump straight into the program without us users having
to do anything apart from downloading it in the first place.

Okay... I hear you... So you may have a pointer that points to
an unloaded object to begin with... But with a few calls, you
can bind that pointer to its correct class in no time at all...

Small price to pay methinks, in order to build fully upgradable
class dlls...

Share this post


Link to post
Share on other sites
TjoekBezoer    122
Heh, thx siCrane, you helped me a lot there. If you come to think about it, it was a rather unlogical thing to do indeed, I completely forgot about it. Maybe because it was late at night

Anyways, I got it to compile (and build) without errors now, which is a step forward for me.

But now when I try to tun my .exe i get a nice "this program encountered an error and needs to close" error. The problem is I am not that experienced in coding c++ yet, so I am hoping someone is willing to look at my code _again_.

Here are the relevant pieces of code that are changed:

----Class declarations----

class virtStack {
public:
virtual bool Push(int intGetal) = 0;
virtual bool Pop(int &intReceiver) = 0;
};

class Stack : public virtStack {
private:
int intStack; // -1 If stack is empty
int intMaxElements;
int *arrStack; // Buffer
public:
Stack(int intElements);
~Stack();
bool Push(int intGetal); // Enter a new number to the stack
bool Pop(int &intReceiver); // Receiver will contain the popped int
};



----The functions in the DLL that are exported----

virtStack* pStack = NULL;
DLL_API void* returnStackPtr() {
pStack = (virtStack*)new Stack(10);
return (void*)pStack;
}

DLL_API void* destroyStackPtr() {
delete pStack;
return NULL;
}



----Main code of the .exe----

typedef void* (*voidFunc)();
void main() {
HINSTANCE handle = NULL;
virtStack* myStack = NULL;
voidFunc createStack;
voidFunc destroyStack;

handle = LoadLibrary("cindll.dll");
createStack = (voidFunc)GetProcAddress(handle, "returnStackPtr");
myStack = (virtStack*)createStack();

int intResult;

myStack->Push(5);
myStack->Pop(intResult);

cout << intResult;

// ending...
destroyStack = (voidFunc)GetProcAddress(handle, "destroyStackPtr");
destroyStack();
myStack = NULL;
FreeLibrary(handle);
}



Summary:
All the code compiles and builds without errors. When trying to run the .exe windows throws me an "encountered a problem" error.

Helping this poor, learning soul would be greatly appreciated

Greetings,

TjoekBezoer

[edited by - tjoekbezoer on April 1, 2004 4:32:04 AM]

Share this post


Link to post
Share on other sites
Toolmaker    967
I am busy with DLLs at this moment aswell and I am using the DLL Interface approach aswell. For now, let''s stick to non-COM based DLLs, because it''s purely C++. This is what I am doing:

IPakFile.h -> Define pure virtual class, DLL export declaration and 2 Get/Free functions and their typedefs
IPakFile.cpp -> Implements the 2 Get/Free functions
CPakFile.h/c.pp -> Define class
CRC.H -> CRC32 class, used by CPakFile.h

My "public" header looks like this:

#ifndef _TOOL_PAKFILEINTERFACE_H_
#define _TOOL_PAKFILEINTERFACE_H_

#include <windows.h>

// DLL export definitions
#ifdef PAKFILE_EXT_
#define PAKFILE_INTERFACE __declspec(dllexport)
#else
#define PAKFILE_INTERFACE __declspec(dllimport)
#endif

// Error returns code
enum PAK_RESULT { PAK_SUCCESS = 0,
PAK_NOTFOUND = 1,
PAK_INVALIDFILE = 2,
PAK_READERROR = 3,
PAK_WRITEERROR = 4,
PAK_MEMERROR = 5,
PAK_UNKNOWNFORMAT = 6,
PAK_CORRUPT = 7
};

// Define interface
struct /*PAKFILE_INTERFACE*/ I_PakFile
{
virtual void AddFile(const char *szFilename) = 0;
virtual void RemoveFile(const char *szFile) = 0;
virtual PAK_RESULT Create(const char *szOutput) = 0;
virtual PAK_RESULT Load(const char *szFilename) = 0;
virtual PAK_RESULT GetFile(const char *szFile, BYTE *cBuffer, DWORD &dwBuffersize) = 0;
virtual DWORD GetFilesize(const char *szFile) = 0;
virtual DWORD GetCount() = 0;
};

// Helper functions/function defines
extern "C"
{
PAKFILE_INTERFACE HRESULT GetPakInterface(I_PakFile ** pInterface);
typedef HRESULT (*GETPAKINTERFACE) (I_PakFile **pInterface);

PAKFILE_INTERFACE HRESULT FreePakInterface(I_PakFile ** pInterface);
typedef HRESULT (*FREEPAKINTERFACE) (I_PakFile **pInterface);
};

#ifdef _DEBUG
#define DLL_NAME "PAKFile_d.dll"
#else
#define DLL_NAME "PAKFile.dll"
#endif

#endif // #define _TOOL_PAKFILEINTERFACE_H_


After I compiled it and I view it in my Dependency walker, I see that there are no functions exported(And calling GetProcAddress(MyDLL, "GetPakInterface"); returns NULL).

What''s wrong with this?

Toolmaker



-Earth is 98% full. Please delete anybody you can.

Share this post


Link to post
Share on other sites
TerrorFLOP    100
Have you tried just slotting the extern "C" directive right into
the function prototype i.e.:

#define ___PAKFILE_INTERFACE extern "C" __declspec (dllexport)

And then declare:

PAKFILE_INTERFACE HRESULT GetPakInterface(I_PakFile ** pInterface);

If this don''t work... You could create a .DEF file.

Simply create a new text file in MS VC++, but use the file
extension .def

Then type in the following:

LIBRARY MyDLL
EXPORTS
GetPakInterface private

This could sort it...

Anyway... I hope this help... I''ll have to look at your code
more carefully next time... But I''m knackered (tired) and have to go and teach students the beauty of calculus (YAWN)!!!

Share this post


Link to post
Share on other sites
Toolmaker    967
I sorted it out, for some reason the extern "C" { ... } blocked caused it. After I put extern "C" __PAKFILE_INTERFACE infront of each function it worked.

Toolmaker



-Earth is 98% full. Please delete anybody you can.

Share this post


Link to post
Share on other sites
TerrorFLOP    100
Yeah... I had a feeling the extern "C" directive needed to be placed right into the function prototype itself.

I''ve never seen it used in that ''block'' way before.

By the way... Do you intend to typedef ALL the fucntions in
your dll...???

Far easier to wrap them up in a class and let a interface
structure bind to these functions at runtime via virtual
methods.

Anyway, I was just wondering... I''m a bit of a COM addict
you see.

Right... I better get off to work LOL!!!

Share this post


Link to post
Share on other sites
TjoekBezoer    122
Extern "C" did it for me too, but i don''t really understand why I am using it in this case. Ofcourse, you prefix extern if the value of the var is assigned in another file, but how does that rhyme in this case? How does that work with functions?


extern "C" DLL_API void* returnStackPtr();
extern "C" DLL_API void* destroyStackPtr();


these are declared in the header included by my DLL, and also used by the .cpp file used for the main.exe

A small explanation about extern is appreciated, maybe I don''t know enough about the use of extern. Google etc couldn''t tell me much more then I already knew about extern, so did my books about c++

Thanks in advance!

Greetings,

TjoekBezoer

Share this post


Link to post
Share on other sites
SiCrane    11839
What was probably happening before is that when you called GetProcAddress(), GetProcAddress() was returning a null pointer. When you tried calling the null pointer as a function, the program crashed. The reason GetProcAddress() returned a null pointer was that the name in the DLL was actually probably something like "_returnStackPtr@0" or something similar, depending on the default calling convention of your project. The extra junk before and after the name is called name mangling or name decoration, and is what allows type safe linkage in C++. By using extern "C" it disables the name mangling, and changes the exported symbol name in the DLL to "returnStackPtr" as you were expecting.

Share this post


Link to post
Share on other sites
TjoekBezoer    122
Thanks SiCrane,

but I don't really understand the real meaning of extern "C". I know what it is for now, but what does it exactly *do*?

It's just for some in-depth understanding of the language that i'm asking this

Greetings,

TjoekBezoer

[edited by - tjoekbezoer on April 1, 2004 7:33:33 PM]

Share this post


Link to post
Share on other sites
SiCrane    11839
Ok, in the beginning, there was C. And in C, you could only define one symbol with external linkage with a given name, so when the symbol tables for object files are built, it was fine to just export the name. This is sometimes called undecorated naming.

Then came Stroustrup. He decided that he wanted a programming language that had classes and other higher level constructs like Simula, but wanted it to be competitive with C, so he implemented his new language, C with Classes, on top of C. One of the original goals of C with Classes was to be as compatible as possible with C, and this included using many of the same tools like the preprocessor and the linker. So, at first, C with Classes used undecorated naming in its object files as well. The ensured link compatability with C object files.

However, then C with Classes became C++. And it added, amoung other things, function overloading. With function overloading, exporting a function by name alone was no longer sufficient. With multiple functions possibly sharing the same name, there had to be some way to tell them apart. Therefore, C++ started using name mangling. Odd strings of characters are appended and prepended to function names so its possible to tell f(int) apart from f(double) or f(char *). This is called name mangling, name decoration or sometimes type-safe linkage. However, that broke compatability with C, so extern was modified so that you specify a function to have C linkage.

To get a better look at this in practice, try running dumpbin /exports cindll.dll from the command line (or whatever your dll name is) with and without using extern "C". You should see that the function names in the export table change depending on whether or not you''ve used the extern "C" statement. (This is assuming that you''re using MSVC as your compiler, which I''m guessing from the error codes that your program produced. Depending on your compiler you might have a different program to dump symbol names from the module.)

Share this post


Link to post
Share on other sites