Click to See Complete Forum and Search --> : Windows Service, Threads and fstream


links
August 18th, 2008, 02:09 PM
I've written an application which writes some data to disk every few seconds to keep the hard drive awake.

Since this application must run all the time I decided to convert it to a Windows Service.

After hours of studying, coding and testing it finally looks as though I've got it right. My service installs, starts, stops etc. My only problem is that the code that writes to disk doesn't work anymore, e.g the file doesn't get written to disk. I really have no idea what the problem could be.

The code is contained in a thread:

unsigned __stdcall ServiceWorkerThread(void * pVoid)
{
endWorkerThread = ::CreateEvent(0, TRUE, FALSE, 0);

long data = 1000;

serviceRunning = true;

while (serviceRunning == true)
{
std::wofstream dfile;
dfile.open(L"data.txt", std::ios::out);
data = data + 1;
dfile << data;
dfile.close();
Sleep(25000);
}
::SetEvent(endWorkerThread);
return 0;
}

The thread is started as follows:

void StartWorkerThread()
{
hThread = reinterpret_cast<HANDLE>(::_beginthreadex(NULL,
NULL,
ServiceWorkerThread,
NULL,
NULL,
NULL));
return;
}

I've tried debugging the service and I can see that the thread gets called and I can step through it, but wofstream looks extremely complex and I can't make out whats going on inside it.

So what's causing the problem? Is it the thread or the service part, or both?

If anyone can shed some light on this it will be greatly appreciated.

links
August 18th, 2008, 02:15 PM
I just want to add that I have the following piece of code which only runs when the service is installed / removed and it works 100% which further leads me to believe that its got something to do with the service / thread code.

void WriteLog(std::wstring text)
{
std::wofstream file;
file.open(L"log.txt", std::ios::out | std::ios_base::app);
file << text << std::endl;
file.close();
return;
}

kirants
August 18th, 2008, 06:40 PM
How are you concluding that the data is not being written ? Are you sure you are looking at the right location for the file ? HAve you tried using a hardcoded absolute path for example ?

jithinpg007
August 19th, 2008, 01:07 AM
test with condition :

if( dfile.is_open() ) { } to confirm whether the file is opened sucessfully or not.

and why dont you try...

std::ofstream file;
file.open("C:\\sample.txt",file.out);
if( !file.is_open() )
{
cout<<"File open failed!"<<endl;
}

think this may helps.. cheers:)

links
August 19th, 2008, 10:15 AM
Thanks guys, you saved me! It never occurred to me that I can provide the full path name. When I hardcode the absolute path e.g C:\\sample.txt it works 100%. So I guess it gets written to disk in some unknown location when I do not provide the full path.

Why is this? As I said in my previous post the following code generates the .txt file in the same location as the executable:

void WriteLog(std::wstring text)
{
std::wofstream file;
file.open(L"log.txt", std::ios::out | std::ios_base::app);
file << text << std::endl;
file.close();
return;
}

But this code which runs in another thread gets written somewhere else:

while (serviceRunning == true)
{
std::wofstream dfile;
dfile.open(L"D:\\data.txt", std::ios::out);
data = data + 1;
dfile << data;
dfile.close();
Sleep(25000);
}

I'm just trying to figure out why this is so. Any further insight is welcome!

kirants
August 19th, 2008, 11:30 AM
You mean, even after specifying absolute path, you don't find it? Why not search and see where it is being created? Perhaps you can see some pattern. Also, is D driver a mapped drive by any chance ?

Arjay
August 19th, 2008, 11:37 AM
I'm just trying to figure out why this is so. Any further insight is welcome!The currentdirectory is used when you don't supply a complete file path in a file operation. This current directory can depending on how the app was started (e.g. specifying a different start up folder when using create process) or if the application internally changes it (using SetCurrentDirectory). In the case of services, I believe the current directory is set to the location of the SCM. If you want to know what the current directory is use GetCurrentDirectory (http://msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx).

That all being said, rather than writing to a file to keep the harddrive on, how about turning off the power saving options?

Igor Vartanov
August 20th, 2008, 03:05 AM
So I guess it gets written to disk in some unknown location when I do not provide the full path.Your "some unknown location" most likely is %WINDIR%\system32 :D

links
August 20th, 2008, 07:27 AM
Thanks for all the replies.

And indeed, I found the text file in Windows\System32! So, as Arjay stated, the current directory gets changed to the location of the SCM. GetCurrentDirectory led me to SetCurrentDirectory so I'm going to adapt my code to change the directory back to my service's exe directory.

Unfortunately merely changing the power saving settings isn't an option in my case. Its a HP laptop with an extremely limited BIOS. After a hard disk replacement the disk is shut down every minute or so irrespective of the power settings in Windows. All I can think is that the BIOS is set to a much more aggresive setting and overrides the Windows settings.

I wouldn't have gone through all the trouble to write this service if it weren't absolutely necessary!

Arjay
August 20th, 2008, 08:19 AM
IMO opinion Set[Get]CurrentDirectory usually causes issues because unless you immediately set the currentdirectory, you can't rely that GetCurrentDirectory will return the correct value. I say this because any [3rd party] dll may change the current directory. Instead use GetModuleFileName(...) at the start of the service and store the value for use later.

jithinpg007
August 21st, 2008, 06:08 AM
The GetSystemPowerStatus function retrieves the power status of the system. The status indicates whether the system is running on AC or DC power, whether the battery is currently charging, and how much battery life remains.

BOOL GetSystemPowerStatus(LPSYSTEM_POWER_STATUS lpSystemPowerStatus);

cheers:)

kirants
August 21st, 2008, 02:53 PM
GetModuleFileName is a better alternative. But, depending on where the exe is placed, the location might be forbidden to be written into. For e.g. if the exe were to be in Program Files folder and the service were to be per user, it probably wouldn't work unless the user has privileges to write to the location. Just something to keep in mind.

Arjay
August 21st, 2008, 04:07 PM
Agreed, I didn't mention this earlier but you can use SHGetFolderPath (http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx) api to retrieve valid areas to write to. Check out the FolderID_Documents and FolderID_LocalAppData constants.

links
August 22nd, 2008, 06:39 AM
I've used GetModuleFileName and SetCurrentDirectory and it works great.
Thanks for all the help.

Arjay
August 22nd, 2008, 09:09 AM
I've used GetModuleFileName and SetCurrentDirectory and it works great.
Thanks for all the help.You missed the point about SetCurrentDirectory.