Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 395
  • Last Modified:

static variables

Hi.  I have a static variable question.

I have a class called Globals that contains a static
CDatabase object as follows:
Globals.h
---------
#ifndef Globals_H
#define Globals_H

class Globals
{
public:
      Globals();

      //Operations
public:
      CDatabase* GetDatabase(){return &m_Database;}
private:
      static CDatabase m_Database;
};

#endif      //Globals_H

Globals.cpp
-----------
#include "stdafx.h"
#include "Globals.h"

CDatabase Globals::m_Database;

Globals::Globals()
{
}

I then have a .exe that contains a member variable of
type Globals called m_Globals.  I call m_Globals.GetDatabase()->Open(...) to open the CDatabase.

I also have a set of .DLLs, all with AFX_EXT_CLASS defined
classes.

After opening the database as seen above, I construct a class contained in a .DLL called Person.  Person creates
another instance of the Globals class called globals
and constructs a CRecordset-derived class using
MyRecordset(globals.GetDatabase()) in an attempt to
load itself from the database.

At this point,
globals.GetDatabase() returns a different CDatabase object,
instead of the one I originally used for the
CDatabase::Open() call shown above.

I have verified that all code works perfectly if the code
is all in a single .EXE, so I am assuming that it has
something to do with crossing a static variable
across .EXE and .DLL boundaries.

What am I doing wrong?

Thank you,
MFCGuy
0
MFCGuy
Asked:
MFCGuy
  • 11
  • 3
  • 2
  • +4
1 Solution
 
mbhaktaCommented:
Why would you make the m_Database static ? If you are accessing one instance of the Globals  and re-accessing that instance once more (provided Globals is in scope) the value of m_Database should not change, even if you access it from a DLL.

Either , your access to objects has been implemented wrongly or your calling conventions from the exe to dll have some problem. Check that out.

If you want me to write a dummy example and mail it to you give me your e-mail address.
0
 
MFCGuyAuthor Commented:
I am not accessing the same instance, I want the same m_Database across all instances of the Globals class so that I can open a CDatabase object once and use it for a large number of CRecorset classes throughout the lifetime of my program.

Here's the order of the code I am performing:
1.I have a member variable in my .EXE App class called m_pGlobals
of type Globals*, which I access in MyApp::InitInstance to call
m_Globals->GetDatabase().Open(...)
This all works fine.
2.I then (in InitInstance) instanciate an instance of class Person using:
Person person;  //Person is an AFX_EXT_CLASS from Person.dll
3. I call person.Load() which creates a local Globals as such:
void Person::Load()  //in Person.dll
{
Globals globals;
//here is where GetDatabase returns a different m_Database than
//the one returned in 1.
MyRecordset recSet(globals.GetDatabase());
recSet.Load(Person->GetEmailAddress());
}

I must reiterate that everything works fine when I tested it all
contained in a single .EXE, only when I introduce the calls to .DLL functions does a problem occur.  My question is:
Can you have staic member variables across .EXE / .DLL boundaries?  I assumed since they are in the same memory space, you could but apparently I am doing something wrong.
0
 
MFCGuyAuthor Commented:
In 1., I meant to type:
m_pGlobals->GetDatabase().Open(...)
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
MFCGuyAuthor Commented:
see comment
0
 
laleonardCommented:
Is the Person object static by any chance?
0
 
RONSLOWCommented:
Your Globals class has not been marked as AFX_EXT_CLASS, so it won't be exported correctly in your DLL's.

Alternatively, you can try adding AFX_DATA to your static declaration - make sure it is appropriately defined before-hand.  Look for it in the article "Export and Import Using AFX_EXT_CLASS" in the on-line help


0
 
MFCGuyAuthor Commented:
1. The Person object is not static.  Why do you ask?

2. Currently, in working this problem, I do have Globals declared as AFX_EXT_CLASS and it is in its own .DLL now - Globals.dll  
Unfortunately, the same problem is still occuring.  

I know I am close because when I call Globals.dll for the second time (from Person.dll), the debugger jumps into the Globals c'tor, everything works fine, but when it returns to Person.dll, BAGlobals::m_Database is not the same one I just saw in the debugger 1 line before - it is reset to an invalid CDatabase object.

0
 
AVaulinCommented:
Do you want to say that Globals constructor called twice?
0
 
MFCGuyAuthor Commented:
An update on the problem:

1.  I have a class called Globals, contained in Globals.dll, written as such:
Globals.h
---------
class AFX_EXT_CLASS Globals
{
public:
Globals();

//Operations
public:
   CDatabase* GetDatabase(){return m_Database;}
private:
   static CDatabase* m_Database;
};

Globals.cpp
-----------
#include "stdafx.h"
#include "Globals.h"

//create a shared memory space for the static variable
#pragma data_seg("Shared")
CDatabase* Globals::m_Database = NULL;
#pragma data_seg()

Globals::Globals()
{
   if(m_Database == NULL)
      m_Database = new CDatabase;
   if(!m_Database->IsOpen())
      m_Database->Open(...);
}

2. I then have another .DLL, Person.dll, that in order to compile and link with Globals.dll, I must declare BAGlobals::m_Database in Person.cpp like this:
Person.cpp
---------------
#include "Person.h"
CDatabase* BAGlobals::m_Database;

.....  rest of Person.dll

When Person.dll constructs an instance of Globals, I can follow the debugger into the construction of the Globals class and everything looks great.  When the Globals c'tor ends and control returns to Person.dll, the newly constructed Globals class is NULL.  I believe this is because of the declaration in Person.cpp that I noted above.

My question now is:
If you have a static variable in one .DLL  (Globals.dll), how do you compile and link to it without having to include Globals.cpp in your new project (Person.dll)?

Any help (or code) you can provide would be much appreciated.

MFCGuy
0
 
MFCGuyAuthor Commented:
To Avaulin:

I don't understand your question.

Thanks for your response,
MFCGuy
0
 
AVaulinCommented:
Can you send sources to me (av@protec.kiev.ua)?
0
 
MFCGuyAuthor Commented:
What sources do you want?  I provided the code as seen above.
Globals is contained in an MFC Extension DLL called GlobalsDLL.
Person is contained in an MFC Extension DLL called PersonDLL.

I can not send you the MSVC projects (they are internal company property anyway) as I simply extracted a very small part to post this question.

Thank you for any assistance you can provide.

0
 
MelangeCommented:
Try this in your header file for Globals:

#ifdef __EXPORTGLOBALS
    #define DLLINTERFACE __declspec(dllexport)
#else
    #define DLLINTERFACE __declspec(dllimport)
#endif

class DLLINTERFACE Globals
{
....
};

#undef DLLINTERFACE     // Do this in case your mixing exports and imports so there's no conflict


in you globals.cpp (inside the DLL project):

#define __EXPORTGLOBALS
#include "globals.h"


This way you will be sure that you class is exported in your DLL and imported in your applications.

0
 
MFCGuyAuthor Commented:
1. How is what you decsribed different from using AFX_EXT_CLASS as I indicated previously?

2. How does this address a static variable problem?

Thank you,
MFCGuy
0
 
stefanrCommented:
If I have got this right: you have an .EXE using two MFC Extension .DLLs, and one of these .DLLs also use the other .DLL. Thats tricky indeed. What I have come up with is this:

In Project/Setting for the :DLL using the other one, define a macro, lets say it's called _PERSDLL, and then in the header file Globals.h insert the following code before defining the class CGlobals:

#if defined(_PERSDLL)
   #define MY_EXT_CLASS _declspec(dllimport)
#else
   #define MY_EXT_CLASS AFX_EXT_CLASS
#endif

class MY_EXT_CLASS CGlobals
{
...
};

That should do it.
/Stefan
0
 
MFCGuyAuthor Commented:
Sorry, that's the same answer as Melange.

It does not work.  The real problem I am having is with accessing the static variable CGlobals::m_Database as I indicated near the bottom of the list of answers.

Thank you for your answer.  Do know the answer to my static variable question?

Regards,
MFCGuy




0
 
stefanrCommented:
I must be missing some important piece of information here. My test app, named Statics, have no problem with the static member variable.

I made Statics as an ordinary MFC MDI app with database support (headers only). Then I made two sub projects to Statics, GlobDLL and PersDLL, as MFC Extension DLL:s. I changed the path where the DLL:s put their .DLL files to the Statics Debug directory. I defined the macro _PERSDLL in the PersDLL Project Settings. I also included the GlobDLL and PersDLL directories as additional include directories for the Statics project, and ..\GlobDLL for the PersDLL project.

The CPersonRecordset class was created using an Access 97 test database.

In GlobDLL I put the CGlobals class:

-------------------- Globals.h ----------------------------------------
#if defined(_PERSDLL)
class _declspec(dllimport) CGlobals
#else
class AFX_EXT_CLASS CGlobals
#endif
{
public:
   CGlobals();
   virtual ~CGlobals();
   CDatabase* GetDatabase() { return m_pDatabase; } // This could be made static...

private:
   static CDatabase* m_pDatabase;
   static int m_cInstance;
};
----------------------- Globals.cpp ---------------------------------
#include "StdAfx.h"
#include "Globals.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#pragma data_seg("Shared")
CDatabase* CGlobals::m_pDatabase = NULL;
int CGlobals::m_cInstance = 0;
#pragma data_seg()

CGlobals::CGlobals()
{
   TRACE("CGlobals::CGlobals()\n");

   if (m_pDatabase == NULL)
      m_pDatabase = new CDatabase;
   if (!m_pDatabase->IsOpen())
      m_pDatabase->Open(_T("Statics"));

   TRACE("m_pDatabase = %p\n", m_pDatabase);
   afxDump << m_pDatabase << "\n";

   m_cInstance++;
}

CGlobals::~CGlobals()
{
   TRACE("CGlobals::~CGlobals()\n");

   m_cInstance--;
   if (m_cInstance == 0)
   {
      m_pDatabase->Close();
      delete m_pDatabase;
      m_pDatabase = NULL;
   }
}

In PersDLL I put the CPerson and CPersonRecordset classes:
-------------------- Person.h ----------------------------------------
#include <Globals.h>

class AFX_EXT_CLASS CPerson  
{
public:
      void Load();
      CPerson();
      virtual ~CPerson();

protected:
};
-------------------- Person.cpp --------------------------------------
#include "stdafx.h"
#include "Person.h"

#include "PersonRecordset.h"

CPerson::CPerson()
{
   TRACE("CPerson::CPerson()\n");
}

CPerson::~CPerson()
{
   TRACE("CPerson::~CPerson()\n");
}

void CPerson::Load()
{
   TRACE("void CPerson::Load()\n");

   CGlobals Globals;
   TRACE("Before: Globals.GetDatabase() = %p\n", Globals.GetDatabase());

   CPersonRecordset rs(Globals.GetDatabase());
   if (!rs.IsOpen())
      rs.Open();

   TRACE("After: Globals.GetDatabase() = %p\n", Globals.GetDatabase());

   afxDump << Globals.GetDatabase() << "\n";

   if (!rs.IsEOF() && !rs.IsBOF())
   {
      TRACE("%s %s %s\n", rs.m_PERSID, rs.m_NAME, rs.m_EMAIL);
   }
}

In Statics, finally:

-------------------- Statics.h ----------------------------------------
#include <Globals.h>

class CStaticsApp : public CWinApp
{
...
protected:
      CGlobals* m_pGlobals;
};
-------------------- Statics.cpp ---------------------------------------
...
#include <Person.h>
...
CStaticsApp::CStaticsApp()
{
   m_pGlobals = NULL;
}
...
BOOL CStaticsApp::InitInstance()
{
...
   pMainFrame->ShowWindow(m_nCmdShow);
   pMainFrame->UpdateWindow();

   m_pGlobals = new CGlobals;
   TRACE("m_pGlobals->GetDatabase() = %p\n", m_pGlobals->GetDatabase());

   OnTestLoad();

   return TRUE;
}
...
int CStaticsApp::ExitInstance()
{
   delete m_pGlobals;
   m_pGlobals = NULL;
      
   return CWinApp::ExitInstance();
}

void CStaticsApp::OnTestLoad()
{
   TRACE("void CStaticsApp::OnTestLoad() Begin\n");

   CPerson person;
   person.Load();

   TRACE("void CStaticsApp::OnTestLoad() End\n");
}
...

The pointer returned by CGlobals::GetDatabase() is always the same.

What is the info I am missing ? I suppose that you are not using multiple inheritance.

/Stefan
0
 
MFCGuyAuthor Commented:
Ok, I was totally wrong.

What you specified will work, I never removed
CDatabase* BAGlobals::m_Database;
from Person.cpp so it was still giving me a NULL pointer every time.

Please submit another response as an answer so I may give you the points.

Also, on this topic, if I have an application with a large number of Business Object classes, is it better to have, say 20 .DLLs, each with a logical subset of those classes, or is it better to make 20 .LIBs that are then linked into subsequent .EXEs?  Or is better to have a single .DLL holding all of the Business Objects (this options would seem to make it difficult to break up enhancement work across a team of programmers)?

Thank you for your help,
MFCGuy
0
 
stefanrCommented:
I'm glad that it worked out for you !

Regarding the .DLL/.LIB dilemma: I think that I have seen a Knowledge Base article discussing the advantage/disadvantage using several DLL:s. I think that the discussion ended with that it would be better to have a large DLL instead of several small ones if loading time is important. If you really wanted to use several small DLL:s, it would render a performance gain to rebase them. Of course that would only work as intended if the DLL:s were rebased specifically for a certain type of application. Using a LIB file instead would be more effective initially, but I don't think that it would help in the long run. And a LIB file can't contain resources as a DLL can. Personally I prefer using DLL:s.

/Stefan
0
 
MFCGuyAuthor Commented:
Thanks!
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 11
  • 3
  • 2
  • +4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now