Solved

static variables

Posted on 1997-05-13
20
371 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
ID: 1301522
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
ID: 1301523
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
ID: 1301524
In 1., I meant to type:
m_pGlobals->GetDatabase().Open(...)
0
 
LVL 1

Author Comment

by:MFCGuy
ID: 1301525
see comment
0
 

Expert Comment

by:laleonard
ID: 1301526
Is the Person object static by any chance?
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1301527
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
ID: 1301528
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
ID: 1301529
Do you want to say that Globals constructor called twice?
0
 
LVL 1

Author Comment

by:MFCGuy
ID: 1301530
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
ID: 1301531
To Avaulin:

I don't understand your question.

Thanks for your response,
MFCGuy
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 4

Expert Comment

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

Author Comment

by:MFCGuy
ID: 1301533
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
ID: 1301534
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
ID: 1301535
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
ID: 1301536
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
ID: 1301537
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
ID: 1301538
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
ID: 1301539
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
ID: 1301540
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
ID: 1301541
Thanks!
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
GIF file bit manipulation for color change 5 107
how do i create updater to My Activex application? 3 79
iSeries DB2 Query 2 90
has22 challenge 11 78
This is to be the first in a series of articles demonstrating the development of a complete windows based application using the MFC classes.  I’ll try to keep each article focused on one (or a couple) of the tasks that one may meet.   Introductio…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
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 is a video that shows how the OnPage alerts system integrates into ConnectWise, how a trigger is set, how a page is sent via the trigger, and how the SENT, DELIVERED, READ & REPLIED receipts get entered into the internal tab of the ConnectWise …

914 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

19 Experts available now in Live!

Get 1:1 Help Now