Hi,
I have been stuck at this for more than a week, as always, I suppose this is due to some of my misconceptions and the fact I am upgrading an application from VS 2003 to VS 2010.
I am now upgrading a windows service developed in the past. In the previous version, running under Windows 2000 Server, compiled with Visual C++ 7.0 ( VS 2003 ) everything works fine. Now I have been porting this to Visual C++ 2010 and running it under Windows 2008 Server. The service has a warning log class that was implemented as a Singleton pattern.
log.h
class Log
{
public:
static Log *getInstance();
void _dummy(){};
private:
Log();
static Log *log;
};
The implementation follows the pattern requirements:
log.cpp
Log *Log::log = NULL;
Log *Log::getInstance()
{
if(log==NULL)
{
log = new Log();
log->AtualizarInfoRegistro(); return(log);
}
return(log);
}
Log::Log()
{
}
Ok, so far. Within the application this static class Log is called several times, by adding the log.h header and whenever it is needed to be called:
Log::getInstance()->_dummy();
The application calls this class whenever a real warning level is necessary and it must create just one circular log. If the application raises an exception within a try catch statement I call the above method _dummy, but whenever the program needs to execute it crashes the system. I have already commented out every call to this singleton class and it does work.
I assumed the linker could not be getting the address properly of this static class and whenever I try to use it it simply crashes because it is an invalid pointer. Thus, I first tried to test the return of this Log::getInstance() method by checking if it is NULL/nullptr. It does not work either. I have tried to reference a global extern pointer to this object :
extern Log* ptr;
ptr = Log::getInstance();
I have checked whether the Log class constructor was being called by logging it.
This log class is used by 3 ISAPI DLLs and a server. So far the error has happened within the execution of the service.
I cannot use VS Debugger to check this, I actually cannot run this system into my workstation.
I am actually trying to learn how to debug the whole thing using Windbg that seems to run on the test workstation ( not my own ) but I am thinking about re writing this Log class without the singleton pattern as the prospect of seeing this class being accessed by IIS through the ISAPI extensions and the windows registry access this log class needs to do might do things work weirdly.
Edit:
Following suggestions from people here I have isolated the singleton and tried to test it locally.
I have created a simple Singleton class:
#include <string>
class LogAlerta
{
public:
static LogAlerta *getInstance();
void Simple(std::string&) const ;
private:
LogAlerta();
static LogAlerta *logAlerta;
};
#include "LogAlerta.h"
#include <exception>
#include <iostream>
using namespace std;
LogAlerta *LogAlerta::logAlerta = NULL;
LogAlerta *LogAlerta::getInstance()
{
if(logAlerta==NULL)
{
try
{
logAlerta = new LogAlerta();
}catch ( std::exception& e )
{
cout << "NULL Pointer" << endl;
return NULL;
}
cout << "LogAlerta instanciated and returned fine!" << endl;
return logAlerta;
}
cout << "LogAlerta was already initiated" << endl;
return logAlerta ;
}
LogAlerta::LogAlerta()
{
cout << "Constructor called" << endl;
}
void LogAlerta::Simple(std::string& _s ) const
{
cout << "Simple method called " << _s << endl;
}
Then, I created a dummy class to try to use it using the external pointer reference:
class Dummy1
{
public:
Dummy1(void);
virtual ~Dummy1(void);
void DummySimple()const;
};
#include "Dummy1.h"
#include "LogAlerta.h"
#include <exception>
#include <iostream>
using namespace std;
extern LogAlerta* Logptr;
Dummy1::Dummy1(void)
{
Logptr = LogAlerta::getInstance();
}
Dummy1::~Dummy1(void)
{
}
void Dummy1::DummySimple() const
{
std::string s("DummySimple");
try
{
}
catch( std::exception& e )
{
Logptr->Simple(s);
cout << e.what() << endl;
}
}
So I wrote a simple console application to test it, calling it in the code and using threads.
In the dummy class if I remove the comments that generate an exception the program crashes, otherwise it works:
#include "stdafx.h"
#include "LogAlerta.h"
#include "Dummy1.h"
#include <iostream>
#include "process.h"
#include "windows.h"
using namespace std;
LogAlerta* Logptr = NULL;
unsigned int WINAPI Thread1(LPVOID lpParam);
unsigned int WINAPI Thread2(LPVOID lpParam);
unsigned int ctrlId[2];
HANDLE ctrlHandle[2];
void UsingThreads();
HANDLE Mutex = NULL;
int main(int argc, _TCHAR* argv[])
{
cout << "First test: calling it indirectly--------------------------" << endl;
Dummy1* _d = new Dummy1();
_d->DummySimple();
delete _d;
cout << "---------" << endl ;
cout << "Second test: calling it directly--------------------------" << endl;
Logptr = LogAlerta::getInstance();
Logptr->getInstance();
string _s("Straight first time");
Logptr->Simple( _s );
Logptr = NULL;
_s.clear();
_s.append("Straight second time");
Logptr = LogAlerta::getInstance();
Logptr->getInstance();
Logptr->Simple( _s );
cout << "---------" << endl ;
cout << "Third test: calling it indirectly again---------------------" << endl;
Dummy1* d = new Dummy1();
d->DummySimple();
delete d;
d = NULL;
cout << "---------" << endl ;
cout << "Fourth test: calling it indirectly after dummy object is deleted:" << endl;
d->DummySimple();
cout << "---------" << endl ;
cout << "Sixth test: calling it indirectly after Dummy object is deleted and Logptr made NULL" << endl;
Logptr = NULL;
d->DummySimple();
cout << "---------" << endl ;
_s.clear();
_s.append("7th: Calling it direcly");
LogAlerta::getInstance()->Simple(_s);
cout << "---------" << endl ;
cout << "---------" << endl ;
cout << "Test calling it within threads, no syncronizations..." << endl;
UsingThreads();
return 0;
}
unsigned int WINAPI Thread1(LPVOID lpParam)
{
Dummy1* ptr = (Dummy1*)lpParam;
cout << "Test within first thread" << endl;
WaitForSingleObject( Mutex, INFINITE );
try
{
ptr->DummySimple();
}
catch( ... )
{
cout << "Exception raised on thread 1 " << endl;
}
ReleaseMutex( Mutex );
ExitThread(0);
return 0;
}
unsigned int WINAPI Thread2(LPVOID lpParam)
{
Dummy1* ptr = (Dummy1*)lpParam;
cout << "Test within second thread" << endl;
WaitForSingleObject( Mutex, INFINITE );
try
{
ptr->DummySimple();
}
catch( ... )
{
cout << "Exception raised on thread 2 " << endl;
}
ReleaseMutex( Mutex );
return 0;
}
void UsingThreads()
{
Dummy1 StaticObject1;
Dummy1 StaticObject2;
ctrlHandle[0] = (HANDLE)_beginthreadex(NULL, 0,Thread1,&StaticObject1,0,&ctrlId[0]);
ctrlHandle[1] = (HANDLE)_beginthreadex(NULL, 0,Thread2,&StaticObject2,0,&ctrlId[1]);
WaitForMultipleObjects(2,ctrlHandle,false,INFINITE);
CloseHandle(ctrlHandle[1]);
CloseHandle(ctrlHandle[0]);
}
The output is:
Second test: calling it directly--------------------------
LogAlerta was already initiated
LogAlerta was already initiated
Simple method called Straight first time
LogAlerta was already initiated
LogAlerta was already initiated
Simple method called Straight second time
---------
Third test: calling it indirectly again---------------------
LogAlerta was already initiated
---------
Fourth test: calling it indirectly after dummy object is deleted:
---------
Sixth test: calling it indirectly after Dummy object is deleted and Logptr made
NULL
---------
LogAlerta was already initiated
Simple method called 7th: Calling it direcly
---------
---------
Test calling it within threads, no syncronizations...
LogAlerta was already initiated
LogAlerta was already initiated
Test within first thread
Test withinPressione qualquer tecla para continuar. . .
Edit: The above test scheme was not really relevant to find the root cause of the issue.
The problem was caused by an exception thrown somewhere in the code. With the suggestion from @nv3 I have created the above simple test program, but it did not give me much information. Except that it was nothing related to threads.
I was looking around the solution directory ( a huge set of C++, C#, ASP, SQL, projects and I missed a single issue that turned to be crucial to the problem: Someone had created a test project for this Logging class. They have even created a small VB 6 project to test it. This person has create this test project as a DLL, but I converted it to a simple console application and then it was a matter of debug it. In fact I think I would have found the problem more easily if I was locally debugging, which is something I have made for 100% of my Windows applications and DLLs I had developed so far. Anyway, after having a local app to look at it, it was rather simple:
In the past they have created buffers that could not hold complete chars, they were not big enough. They have also not initiated anything. Someone before me had converted all strcpy and strncat, to their safe formats ( _s suffix appended ) and when I was trying to run them the old buffers were not working with these safer versions.
Before it was something like:
strcpy(stPrefixoMensagem.szPref01, " - ");
And then, someone updated it to
strcpy_s(stPrefixoMensagem.szPref01, " - ");
With the old compiler ( VS 2003 ) and system ( Windows 2000 Server/IIS 5 ) nothing was happening, but when the update was created a bug was inserted. In fact, I still don't know if I will meet more bugs like this, in fact I had met many before it and corrected them. At this time the system entered a non usual condition which called the logging class. I am thankful to everybody who has helped me with this, apologize for inaccuracies and misconceptions. I would definitely mention that nv3 has given me the crucial part of the solution so should nv3 post a solution to this I would like to credit nv3. Thanks again, and I apologize.