Solved

static variables

Posted on 1997-05-13
20
364 Views
Last Modified: 2013-11-20
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
Comment
Question by:MFCGuy
  • 11
  • 3
  • 2
  • +4
20 Comments
 
LVL 4

Expert Comment

by:mbhakta
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
In 1., I meant to type:
m_pGlobals->GetDatabase().Open(...)
0
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
see comment
0
 

Expert Comment

by:laleonard
Comment Utility
Is the Person object static by any chance?
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 4

Expert Comment

by:AVaulin
Comment Utility
Do you want to say that Globals constructor called twice?
0
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
To Avaulin:

I don't understand your question.

Thanks for your response,
MFCGuy
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 4

Expert Comment

by:AVaulin
Comment Utility
Can you send sources to me (av@protec.kiev.ua)?
0
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 3

Expert Comment

by:Melange
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 3

Expert Comment

by:stefanr
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 3

Expert Comment

by:stefanr
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
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
 
LVL 3

Accepted Solution

by:
stefanr earned 200 total points
Comment Utility
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
 
LVL 1

Author Comment

by:MFCGuy
Comment Utility
Thanks!
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Suggested Solutions

Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now