I have a pure virtual base class called IVSystemDLL. I also have a class called CVSystemDLL which inherits from IVSystemDLL. When I create a new CVSystemDLL and convert it to a IVSystemDLL *, and try to use one of it's functions, I get an access violation.

It's not something inside the function that's causing the access violation because I've put a break point inside the function and it doesn't get hit.

This is my code (snipped):

class IVSystemDLL
{
public:
	virtual void Main( HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCommandLine, int nCmdShow ) = 0;
};

class CVSystemDLL : public IVSystemDLL
{
	CWindow window;
public:
	void Main( HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCommandLine, int nCmdShow );
};

int main( int argc, char **argv )
{
        IVSystemDLL *pSystem = static_cast< IVSystemDLL * >( new CVSystemDLL );
        pSystem->Main( NULL, NULL, NULL, 0 );
}

Edited 5 Years Ago by tomtetlaw: n/a

The first thing I'd say is that the static_cast is inappropriate. dynamic_cast would be more type-safe. But because CVSystemDLL derives from IVSystemDLL, you should be able to point your IVSystemDLL pointer directly at a new instance of a CVSystemDLL object with no need to cast.
I haven't had my first coffee of the day yet and i'm slightly hung over, so I'm not exactly 100% awake atm, but as long as the pointer allows you to access the derived classes implementation of the main function and not the base classes pure virtual one you should be OK. Otherwise you'd have to cast the base-pointer back to it's correct derived type using a dynamic_cast before calling main. As I said I'm not quite alive yet, so I could be wrong, but that's my two pence!

Well I tried using dynamic_cast , I tried not even casting at all, I tried casting back to CVSystemDLL and all methods had the same results, an access violation. If it's related to the problem, the code that I'm using has the code to create new CVSystemDLL's in a DLL. And the code that gets one, and calls main is in an EXE, would that make a difference?

There is nothing wrong with this snippet (but yes, use dynamic_cast).
Maybe you set the breakpoint wrong, You're saying something with a DLL and an EXE, so if the code of Main() is in the DLL, how did you set the breakpoint?

You can try instead to show a MessageBox.

(I'm using VC++ 2010)
I know because when I put my mouse over pSystem, and look inside the __vfptr member (the virtual function table) it shows me 'The expression could not be evaluated' which means an invalid pointer inside the __vfptr..

First, in response to thelamb and JasonHippy, dynamic_cast is not needed in this case, and if you have created the CVSystemDLL class in a separate DLL, then dynamic_cast will not work in the exe (if that is what is being done).

Second,
>>If it's related to the problem, the code that I'm using has the code to create new CVSystemDLL's in a DLL. And the code that gets one, and calls main is in an EXE, would that make a difference?

It can make a huge difference! There are several things that could be problematic when doing this. Here are a few sources of problems (DLLs and EXEs are referred to as "modules"):
1) On windows, modules have separate heaps. This means that memory allocated in one module cannot be deallocated in another. If your class does such a thing, expect a crash or access violation or heap corruption. This also means that anything that is created by a module will be invalid as soon as the module is un-loaded, if you try to access them, you will get an access violation because those pointers will no longer be pointing to memory that is in the virtual addressing space of the application.
2) Modules which are not compiled with the exact same compiler with the exact same compiler options (optimizations, debug symbols and hooks, etc.) cannot be expected to be compatible, as far as C++ features are concerned (C functions and types are OK). This is due to ABI (Application Binary Interface). Make sure both modules are compiled with identical compiler and identical options. If not, you can expect weird errors, crashes, memory corruption, and all sorts of nasty things. The easiest is to only use C functions and types from the DLL, if you need C++ objects, export the object as an opaque pointer and wrap all member functions with C functions taking the opaque pointer as a parameter.
3) If RTTI is used in one modules and not in the other, it will break ABI as well, for polymorphic classes. RTTI (Run-time Type Identification) is "turned on" by any single call to dynamic_cast() or typeid() or type_info.

If anyone of the above applies to you, it might explain the problem.

You said: "the code to create new CVSystemDLL's in a DLL"
How exactly do you accomplish that? You have to post code that reflects the structure of your DLL vs EXE code interactions.

I'll try to explain it with IVSystemDLL and CVSystemDLL as an example.

There's a function in each DLL in my project called InterfaceFactory, it's basically a string look up table for 'interface factories'.

Interface Factories are functions that take return a new instance of an implementation class (CVSystemDLL) which you cast to the interface class's type (IVSystemDLL*).

Here's some code:

InterfaceFactory:

DLL_EXPORT void *InterfaceFactory( char *pInterfaceName )
{
	CInterfaceFactory *pInterface;

	for( pInterface = CInterfaceFactory::m_pFactories; pInterface; pInterface = pInterface->m_pNext )
		if( !strcmp( pInterface->m_pName, pInterfaceName ) )
			return pInterface->m_pFactory( );

	return NULL;
}

The code inside pInterface->m_pFactory is as follows for IVSystemDLL:

static void *ret = NULL;
		if( !ret )
		{
			ret = (void*) new CVSystemDLL;
		}
		return ret;

The code I use to get a new IVSystemDLL is:

VSystemDLL *pSystem = ( IVSystemDLL * )Plat_GetInterface( "vsys.dll", SYSDLL_INTERFACE_VERSION );

vsys.dll is where the interface factory for IVSystemDLL is and SYSDLL_INTERFACE_VERSION is a string identifying the interface factory.

I hope this helps.

Edited 5 Years Ago by tomtetlaw: n/a

First, static local variables are created once, the first time control flows over them. So, for a singleton (which is what CVSystemDLL is) you would only need this as implementation of pInterface->m_pFactory:

static CVSystemDLL* ret = new CVSystemDLL;
  return ret; //let ret be implicitly cast to void* (don't use '(void*)ret')

Furthermore, dynamic memory allocation is not needed, you could do:

static CVSystemDLL ret;
  return &ret; //let ret be implicitly cast to void* (don't use '(void*)ret')

Now, the observation about the (void*) cast is valid at the call-site as well. You should not use the (void*) to cast pointers because the behavior is to perform a reinterpret_cast which has undefined behavior, by definition (C++98 Standard clause 5.2.10/3). The same rational goes for casting the void* back to IVSystemDLL*. This sequence of casts has undefined behavior, especially if it is across a module boundary. To perform the call-site cast, you need to use static_cast, and that's the only option.

VSystemDLL *pSystem = static_cast<IVSystemDLL*>(Plat_GetInterface( "vsys.dll", SYSDLL_INTERFACE_VERSION ));

Finally, what happens in Plat_GetInterface? I suspect you are doing this:

HMODULE dlhdl = LoadLibrary("vsys.dll");
  typedef void* (*InterfaceFactProc)(char*);
  InterfaceFactProc get_fact = (InterfaceFactProc) GetProcAddress(dlhdl, "InterfaceFactory");
  typedef void* (*InterfaceFactoryFuncPtr)();
  InterfaceFactoryFuncPtr fact = (InterfaceFactoryFuncPtr) get_fact();
  void* ret = fact();
  FreeLibrary(dlhdl);
  return ret;

If this is what you are doing, then I am not at all surprised that you are getting an access violation. You cannot use anything that has been obtained from a DLL after you have freed it. That includes pointers to exported functions in the DLL, pointers to functions that you obtained from an exported function, and any object that was allocated by the DLL (static or dynamic, doesn't matter).

If this is not roughly how Plat_GetInterface is implemented, then please do tell.

You were right, the call to FreeLibrary was the cause of the access violation. Thanks for your help!

This question has already been answered. Start a new discussion instead.