dll help visual C++ 6.0

Started by
19 comments, last by kindfluffysteve 20 years ago
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...
Advertisement
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]
Greetings,TjoekBezoer
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 codeenum 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 interfacestruct /*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 definesextern "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.

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)!!!
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.

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!!!
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
Greetings,TjoekBezoer
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.
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]
Greetings,TjoekBezoer
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.)

This topic is closed to new replies.

Advertisement