Click here to Skip to main content
15,308,835 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?
zlogdan 20-Jun-13 19:45pm
   
@nv3. It does not return a NULL pointer. The service is halted after that. I will check the hypothesis of AtualizarRegistro (update register ) throwing an exception. Is it me, or do you all also loathe visual c++ 7 compiler ? I am thinking that you and JackDingler might be both right.
nv3 21-Jun-13 7:28am
   
I have liked the Visual C++ compilers for the many years I have been working with them. They produce great code, the VC IDE is nice, and the debugger is great. Most of the troubles I had finally turned out to be my own errors, not the compiler's. But there are enough other things in the Windows environment that I don't like that much :-)
zlogdan 21-Jun-13 15:25pm
   
I have liked every Microsoft compiler: VS 6, 2005-2012, and all problems I had with them were also due to my own errors :-) Except 2002/2003. I have posted an update for the question.
nv3 22-Jun-13 5:41am
   
You did a great job creating that little test framework. Now tell us the following:

- Can you run that on your workstation? Then you could run it in the debugger ...
- When you uncomment the three lines that produce the divide by zero exception, in which test case does it crash? The cout outputs might be misleading as there might still be buffer contents that has not been flushed yet. So try to make it interactive or run each test case separately until you have found the case(s) in which it will crash.
- When your program crashed, what is the message? Or does it just quit silently?
zlogdan 22-Jun-13 10:24am
   
In fact I was so worried about the outputs and used to not being able to debug that I forgot that I can debug this at my workstation. I will try it this Monday at work, it crashes right at this line: _d->DummySimple(); So I am thinking, like you folks told me to focus at this, that the Log class is ok, and some exception is being raised making the service to crash.
nv3 22-Jun-13 10:37am
   
Ok, that makes the search a lot simpler. So it has not to do with multi-threading. My suspicion is that your Logptr is the cause. Perhaps you have a name duplicate somewhere that is not visible in the code you pasted to the website. I'd set a breakpoint into the catch clause and you will know the real cause in a minute.
zlogdan 24-Jun-13 9:00am
   
Yeah, I debugged it locally with my simple test code. The program halts at the division by zero code and the exception is not caught, I actually removed everything and just left the division by zero code. The actual Logging class is quite big, and I bet there is something on it that was improperly implemented...
nv3 24-Jun-13 9:31am
   
Sounds you are quite happy; but I didn't understand your last post. You said, the exception is NOT caught? I thought you were trying to force that exception in your test-code. And the exception not being caught could mean that floating point exceptions are switched off. I remember that you have to turn them on in VC++ by

_controlfp (0, _EM_INVALID | _EM_OVERFLOW | EM_ZERODIVIDE);

Or better yet, don't use a floating point exception as test case, but an access violation by dereferencing a pointer to 0.

I would be satisfied if your catch clause has been entered and the test exception correctly been logged. Or am I wrong?
zlogdan 24-Jun-13 9:46am
   
@nv3, I apologize for the misconceptions. I am not really happy. I think my test project kind of deviated me from the problem. I did not know about the floating point exceptions tip, I apologize again. I was digging around the whole directory and found out a small project that tests this Logging class, that is much more realistic than my test code. I apologize again. I am going to try the test project someone left it here. I think I should have dug this project directory better. Sorry for the lack of accuracy. And thanks a lot for the help.
zlogdan 24-Jun-13 13:15pm
   
@nv3, you were right. It was nothing thread related. Please see my update above. Also, please post a solution below, because IMO you have given me the solution to the issue and I would like to give the credit for it.
nv3 24-Jun-13 13:54pm
   
I am glad you were able to pinpoint the problem. Knowing that you have solved the problem is a good feeling and that is rewarding enough. Congradulations to your perseverance and for really drilling to the bottom of it.
zlogdan 24-Jun-13 13:56pm
   
Thanks a lot!

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.
   
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.
zlogdan 21-Jun-13 19:47pm
   
@JackDingler, I am going to do this! Thanks!
zlogdan 22-Jun-13 10:25am
   
I had tried a simple test app, and I am sure that this Log class is ok and an exception is being raised and it makes the service to crash...
JackDingler 24-Jun-13 8:38am
   
Time to start adding try/catch blocks and attempt to log where the exception is raised?

What do your event logs look like? Any clues there?
JackDingler 24-Jun-13 8:38am
   
Is there no chance that your co can put the server on a local VM for you for testing? Is that possible in your environment?
zlogdan 24-Jun-13 8:56am
   
I comment everything and just left the division by zero code, and the exception is caught but it causes the program to halt. I cannot locally access the event log on my local machine, the real system displays a generic error.
zlogdan 24-Jun-13 13:11pm
   
@JackDingler it ended up being a sum of issues, please see my update at the question.
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);"
   
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