DLL Programming help (C++)

Started by
10 comments, last by Programmer16 18 years, 8 months ago
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!
Advertisement
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
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!
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]
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
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!
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.

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.
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");
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

This topic is closed to new replies.

Advertisement