Click here to Skip to main content
15,897,226 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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
C++
class Log
{
public:
	static Log *getInstance();
        void _dummy(){};
        // lots of methods here
private:
	Log();
	static Log *log;
};


The implementation follows the pattern requirements:

log.cpp
C++
Log *Log::log = NULL;
Log *Log::getInstance()
{	
	if(log==NULL)
	{		
		log = new Log();
		log->AtualizarInfoRegistro();// Access windows register here
		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:

C++
Log::getInstance()->_dummy();// called this here as an example


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 :

C++
extern Log* ptr;

// next everytime I want to use it I call 

ptr = Log::getInstance();

// then I check if it is NULL


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:


C++
#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:

C++
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
	{
		// Remove comments and things do not work
/*		int i = 0;
		int j = 0;
		int a = i/j;	*/	
	}
	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:

C++
#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]);
	
	// Esperando encerramento das thread
	WaitForMultipleObjects(2,ctrlHandle,false,INFINITE);
	
	// Fechando handles
	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:
C++
/* monta o prefixo do registro */
strcpy(stPrefixoMensagem.szPref01, "  - ");

And then, someone updated it to
C++
/* monta o prefixo do registro */
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.
Posted
Updated 24-Jun-13 7:31am
v8
Comments
JackDingler 20-Jun-13 15:07pm    
When does your first call to the class occur?

Perhaps it is being called before dependent libraries are loaded?
zlogdan 20-Jun-13 15:16pm    
I will try to check this possibility. I have already tried to call it for the first time in the log.cpp file and also during the main function of the service. I am going to try your suggestion! THANKS! Edit: I was compiling 4 projects at once and all of them include the log class. And I had forgot to call the class within the ISAPI calls. 3 DLLs and one windows service. I am going to test it! But looks you nailed it!
nv3 20-Jun-13 15:57pm    
What is "return (logAlerta); doing in the if-block. I would have expected: "return log;"
zlogdan 20-Jun-13 16:06pm    
Sorry, I had edited the code to post here, changed logAlerta to log ( Alerta == Warning in Portuguese )you are correct, going to fix it!
nv3 20-Jun-13 16:41pm    
Ok, now when you say:
"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. "
What exactly do you mean by, "it does not work either"? Does it return NULL? Or does it return an address that can not be properly dereferenced?

I would start out by including your Log class in a little project on your workstation and see if it works there. If that's the case, then what is different to the server environment?

And have you considered that your function "AtualizarInfoRegistro()" might throw an exception?

Moved my comment here. If it fixes your issue you can mark it as the solution...

When does your first call to the class occur?

Perhaps it is being called before dependent libraries are loaded?
Be sure that your app has fully loaded before you begin logging using any calls that aren't native Win32.
 
Share this answer
 
Comments
zlogdan 20-Jun-13 15:42pm    
Thanks, I have just made the call during the service initialization then will use! I am still waiting the system engineer to test it :-/
zlogdan 20-Jun-13 19:46pm    
It did not work, but I have only tested trying to call the class during service initialization.
JackDingler 21-Jun-13 10:40am    
That was my point. During initialization, your service may not have the libraries needed to support this call.

I don't know what's in you log class, what dependencies it has. But it sounds like it might be failing because it's trying to make a call to a service that isn't available yet.

You could try a two tiered initialization. Have it log using basic WIN32 functions until your app is fully initialized, then initialize and use your more complex logging functions.

Is there no way to use your logging functions in a simpler application that you can test and debug?
zlogdan 21-Jun-13 15:51pm    
This logging class is quite complex and has lots of dependencies. I tried to isolate just the basic part of it but did not get good results. The original code runs on a windows service which is also a web server, that access a database and receives data from ISAPIs and web services. To be quite honest I am far from an expert on this system, just trying to make it work on Windows 2008 Server. It was compiled under VS 2003 and run on Windows 2000 server, but after recompiling it on VS 2010 and trying to run it, it simply does not work. Due to my complete ignorance on programming windows services I may be missing the point where to initialize the class. But as I said, this code used to work on an old server and compiled with 2003.
JackDingler 21-Jun-13 16:21pm    
You might consider piggybacking a more rudimentary logging system to watch your logger. At least until you isolate the issue. Create one that uses only simple file services to log info to a file. This should function no matter the state of initialization of the app, as the base C library is initialized before main().

As I think you know, you're probably getting an exception thrown at some point. Create a macro that calls your logging function with __FILE__, __LINE__, __FUNCTION__ and place this at the start of every function in your logger.

Then after it's run and failed, you should see the last successful function to run. This may give you a clue as to what's going on.

you should be able to use fopen and fprintf but be sure and call fflush after each entry is logged.
Several thoughts:
(1) your getinstance() API is not thread safe. If you have 2 or more threads calling it at the same time, you will get 2 or more objects created.
(2) Is there just 1 copy of the .obj file linked somewhere? If the .cpp -> .obj file is linked into 2 or more places you effectively have 2 copies of the code that can be called.
(3) return is not a function. If you were maintaining my code base I would make you get rid of the parens on your return statement. ie. "return log;" not "return(log);"
 
Share this answer
 
Comments
zlogdan 21-Jun-13 10:55am    
Thanks, I am without web access while at work. Only at lunch time... I am going to check all possible solutions indicated. I did not write the return (log) thing, but yeah I should have removed it.
zlogdan 21-Jun-13 15:54pm    
(1) I think this might be the case. But how would it work if compiled on VS 2003 and not at VS 2010 ? I have been given this question many times, my answer is: VS 2003 permits lots of unsafe code and make it easier to bugs to arise but I am really kind of desperate now.:-)
zlogdan 24-Jun-13 13:12pm    
@H.Brydon It was something simpler, but a sum of issues, please see my update above.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900