Skip to main content
GameDev.net gamedev.net
🔒 Locked

DLL Programming help (C++)

Started by Programmer16 Aug 9, 2005 at 2:50 AM 10 replies 2.1k views
Original Post
Programmer16
Programmer16
Ok, I've written this code hundrends of times and I've never had problems before. Can somebody point out what I did wrong? I'm working on a dll interface template class for my code base, and I'm having a slight problem. After everything is done and the app has returned, I get a bunch of "First-chance exception in Code Base.exe (KERNEL32.DLL): 0xC0000005: Access Violation." Can someone help me find my error? The code seems to work fine, up until the app closes. Here's my code:

	//######################################################################
	// Log
	//######################################################################
	void Log(const char* pStream, ...)
	{
#ifdef _DEBUG
		va_list VaList;
		char szBuffer[1024];

		va_start(VaList, pStream);
		vsprintf(szBuffer, pStream, VaList);
		va_end(VaList);

		std::ofstream fout("log.txt", std::ios::app);
		fout<<szBuffer;
		fout.close();
#endif
	}

	//######################################################################
	// DllLoader
	//######################################################################
	class DllLoader
	{
		HINSTANCE	m_Handle;
		std::string	m_FileName;
	public:
		DllLoader()
		{
			m_Handle = 0;
		}

		~DllLoader()
		{
		}

		bool Load(const char* pFileName)
		{
			// If a DLL was already loaded, we'll unload it for the new DLL
			if(m_Handle)
				Release();

			// If a filename wasn't supplied, we'll return false
			if(!pFileName)
			{
				Log("DllLoader::Load() - No filename was supplied.\n");
				return false;
			}

			// Load the library
			m_Handle = LoadLibrary(pFileName);

			// If the library wasn't loaded (for some reason) then we return false
			if(!m_Handle)
			{
				Log("DllLoader::Load() - Library could not be loaded.\n");
				return false;
			}
			Log("DllLoader::Load() - %s was loaded into memory.\n", pFileName);
			m_FileName = pFileName;
			return true;
		}

		void Release()
		{
			// If the DLL wasn't loaded, we can't unload it. So, we return
			if(!m_Handle)
			{
				Log("DllLoader::Release() - A DLL was never loaded into memory.\n");
				return;
			}

			// Free the library
			FreeLibrary(m_Handle);
			Log("DllLoader::Release() - %s was released.\n", m_FileName.c_str());
		}

		template <typename GF_FUNCTYPE>
		bool GetFunction(const char* pFuncName, GF_FUNCTYPE& fpFunc)
		{
			// If a DLL wasn't loaded then return false
			if(!m_Handle)
			{
				Log("DllLoader::GetFunction() - A DLL was never loaded into memory.\n");
				return false;
			}

			// If a function name wasn't supplied, then return false
			if(!pFuncName)
			{
				Log("DllLoader::GetFunction() - A function name was not supplied.\n");
				return false;
			}

			// Get the proc. address
			fpFunc = (GF_FUNCTYPE)GetProcAddress(m_Handle, pFuncName);

			// If the function failed to load, return false
			if(!fpFunc)
			{
				Log("DllLoader::GetFunction() - %s failed to load.\n", pFuncName);
				return false;
			}

			Log("DllLoader::GetFunction() - %s was loaded into memory.\n", pFuncName);
			return true;
		}
	};

	//######################################################################
	// DllInterface
	//######################################################################
	template <typename INTERFACETYPE>
	class DllInterface
	{
		// Type definitions for each INTERFACETYPE
		typedef bool (*CREATEINTERFACE)(INTERFACETYPE* pInterface, void* pData);
		typedef void (*DESTROYINTERFACE)(INTERFACETYPE* pInterface);
		INTERFACETYPE	m_pInterface;
		DllLoader		m_Loader;
	public:
		DllInterface()
		{
			m_pInterface = 0;
		}

		~DllInterface()
		{
		}

		bool Load(const char* pFileName, void* pData)
		{
			// If we can't load the dll, then return false
			if(!m_Loader.Load(pFileName))
				return false;

			// If we can't load the function, then return false
			CREATEINTERFACE CreateInterface = 0;
			if(!m_Loader.GetFunction("CreateInterface", CreateInterface))
				return false;

			// If we fail to create the interface, then return false
			if(!CreateInterface(&m_pInterface, pData))
				return false;
			
			// The interface was loaded, return true
			return true;
		}

		void Release()
		{
			// If the interface wasn't loaded, return
			if(!m_pInterface)
				return;

			// If we can't load the DestroyInterface function, then return false
			DESTROYINTERFACE DestroyInterface = 0;
			if(!m_Loader.GetFunction("DestroyInterface", DestroyInterface))
				return;

			// Destroy the interface
			DestroyInterface(&m_pInterface);

			// Release the DLL
			m_Loader.Release();
		}

		INTERFACETYPE GetInterface() const
		{
			return m_pInterface;
		}
	};





#include <windows.h>
#include "dftCommon.h"
#include "ITextOut.h"

dftlib::DllInterface<ITextOut*> g_TextOut;

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
	g_TextOut.Load("TextOut.dll", 0);
	g_TextOut.GetInterface()->DrawText("Test 123");
	g_TextOut.Release();
	return 0;
}

[/soure]

ITextOut

#ifndef _ITEXTOUT_H_
#define _ITEXTOUT_H_

class ITextOut
{
public:
	virtual void DrawText(const char* pString)	= 0;
};

#endif




CTextOut.h

#ifndef _TEXTOUT_H_
#define _TEXTOUT_H_

#include <windows.h>
#include "ITextOut.h"

class CTextOut : public ITextOut
{
public:
	void DrawText(const char* pString);
};

extern "C" __declspec(dllexport) bool CreateInterface(ITextOut** pTextOut, void* pData);
extern "C" __declspec(dllexport) void DestroyInterface(ITextOut** pTextOut);

#endif




CTextOut.cpp (TextOut.dll)

#include "TextOut.h"

void CTextOut::DrawText(const char* pString)
{
	MessageBox(0, pString, "Message", MB_OK);
}

extern "C" __declspec(dllexport) bool CreateInterface(ITextOut** pTextOut, void* pData)
{
	if(*pTextOut)
		return false;

	*pTextOut = new CTextOut;
	return *pTextOut != 0;
}

extern "C" __declspec(dllexport) void DestroyInterface(ITextOut** pTextOut)
{
	if(!*pTextOut)
		return;
	delete *pTextOut;
}




Any help would be awesome! Also, any suggestions about fixing some of my code? (Mostly my CreateInterface and DestroyInterface) Thanks!
Enigma
Enigma
Depending on your compiler it's possible that you're being bitten by mismatching calling conventions. Try making the calling conventions explicit for the exported functions and the function pointers:
// DllInterfacetemplate <typename INTERFACETYPE>	class DllInterface	{		// Type definitions for each INTERFACETYPE		typedef bool (__cdecl *CREATEINTERFACE)(INTERFACETYPE* pInterface, void* pData);		typedef void (__cdecl *DESTROYINTERFACE)(INTERFACETYPE* pInterface);// CTextOut.hextern "C" __declspec(dllexport) bool __cdecl CreateInterface(ITextOut** pTextOut, void* pData);extern "C" __declspec(dllexport) void __cdecl DestroyInterface(ITextOut** pTextOut);// CTextOut.cppextern "C" __declspec(dllexport) bool __cdecl CreateInterface(ITextOut** pTextOut, void* pData){// ...extern "C" __declspec(dllexport) void __cdecl DestroyInterface(ITextOut** pTextOut){

Or equivalently with __stdcall.

Enigma
Programmer16
Programmer16
I've tried that before, but I'll try it again.
I'm using MSVC++6

This has got me frustrated, since I've written this code so many times and never had problems with it.

Thanks!
Programmer16
Programmer16
Ok, I tried that, and it didn't work :(

Ok, I've figured out that it isn't my dll loading (because I rewrote the app using Win32 calls instead of my wrapper class), so it must be in my DLL somewhere.

[Edited by - Programmer16 on August 9, 2005 12:20:48 PM]
Enigma
Enigma
I think I spotted it. You don't have a virtual destructor on ITextOut. deleteing a pointer-to-derived via a pointer-to-base without a virtual destructor in base is a no-no.

Enigma
Programmer16
Programmer16
Nope that didn't work either. I'm going to try to dig up my old code to see what I'm doing different.

Thanks!
Zorbfish
Zorbfish
Quote:
Original post by Programmer16
Ok, I tried that, and it didn't work :(

Ok, I've figured out that it isn't my dll loading (because I rewrote the app using Win32 calls instead of my wrapper class), so it must be in my DLL somewhere.


If the exports in the DLL are not loading have you tried changing the __declspec(dllexport) symbol to __declspec(dllimport) in CTextOut.h? Try that with the existing DLL.

Programmer16
Programmer16
Quote:
Original post by Zorbfish
Quote:
Original post by Programmer16
Ok, I tried that, and it didn't work :(

Ok, I've figured out that it isn't my dll loading (because I rewrote the app using Win32 calls instead of my wrapper class), so it must be in my DLL somewhere.


If the exports in the DLL are not loading have you tried changing the __declspec(dllexport) symbol to __declspec(dllimport) in CTextOut.h? Try that with the existing DLL.


When I try changing the one, it gives me a warning and says that dllexport is assumed. When I try changing the definition, it says that import definition not allowed.

The test app loads fine and the text is displayed, but then I get the errors after the app closes.
Programmer16
Programmer16
Ok, I've narrowed it down to having something to do with my class. It doesn't give me errors until I call:
g_pTextOut->DrawText("Test 123");
Enigma
Enigma
Can you try changing your CreateInterface and DestroyInterface functions to:
extern "C" __declspec(dllexport) ITextOut * __stdcall CreateInterface(){	return new CTextOut;}extern "C" __declspec(dllexport) void __stdcall DestroyInterface(ITextOut* pTextOut){	delete pTextOut;}

with the appropriate modifications to your dll loading classes. See if that makes any difference.

Enigma
Programmer16
Programmer16
Quote:
Original post by Enigma
Can you try changing your CreateInterface and DestroyInterface functions to:
extern "C" __declspec(dllexport) ITextOut * __stdcall CreateInterface(){	return new CTextOut;}extern "C" __declspec(dllexport) void __stdcall DestroyInterface(ITextOut* pTextOut){	delete pTextOut;}

with the appropriate modifications to your dll loading classes. See if that makes any difference.

Enigma


Sorry it took so long, I was having problems with my compiler. I tried that and it still isn't working (I had to take __stdcall out for some reason. It wouldn't load the functions.)

[Edited by - Programmer16 on August 9, 2005 8:33:56 PM]
Programmer16
Programmer16
Ok, I took out the call to MessageBox() and it doesn't give me the access violation (It actually gives me these if I use any Win32 function, class, etc.) So does this mean that I can't use Win32 in a DLL or am I doing something wrong?

Thanks!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.