Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Writing to disk inside a TThread


This question is answered.


Permlink Replies: 8 - Last Post: Jan 31, 2018 10:39 AM Last Post By: Jayme Jeffman
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Writing to disk inside a TThread  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 9:38 AM
Hello,

I am a newbie on the use of the TThread.
I am not succeeding on creating a file on disk from within the Thread.
It is strange that in another TThread it works. I am calling the TStrings::SaveToFile method.
My client asked me to change the name of the Windows service I have made, so I have created a new windows service with the name he asked me to and added the old TThread as a unit of it. In the old service almost everything works fine and the log file is created. The new one does not create the log file.

void __fastcall Sto2MinLoader::AddLogEntry( String Texto, TNivelLogger LogNiv )
{
  String linha;
 
  if( LogNiv < iNivelLog ) return;
 
  linha = Now().FormatString("dd/mm/yyyy hh:nn:ss.zzz")+ String(" - ") +Texto ;
  if(Logger)
  {
    Logger->BeginUpdate();
    Logger->Add(linha);
    while( Logger->Count > NumLogLinha ) { Logger->Delete(0); }
    Logger->EndUpdate();
    if(Logger->Count > 0 )
    {
      if( iNivelLog < 2 && Logger->Count % 10 == 0)
      {
        try{ Logger->SaveToFile( sLogFile ); }
        catch(...){}
      }
      else if( iNivelLog > 1 )
      { // Erros e mensagem de nivel sistema devem ser salvos logo se nivel de log é maior do que evtWarning (aviso )
        try{ Logger->SaveToFile( sLogFile ); }
        catch(...){}
      }
    }
  }
}
//---------------------------------------------------------------------------


The code above is the one which works in the first Windows service I have made, Sto2MinLoader is a TThread, Logger is a TStringList object and NumLogLinha is a integer value which default value is 3000.

Do I need to use Syncronize for that ? What is the best way of creating a logger from a TTHread ? Is the first one a lucky one Windows service which is Thread can write to disk without calling Synchronize() ?

Thank you very much.

Best regards.

Jayme Jeffman

Edited by: Jayme Jeffman on Jan 30, 2018 4:10 PM to add the code snippet
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Writing to disk inside a TThread [Edit]
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 10:30 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

I am not succeeding on creating a file on disk from within the
Thread.

And what exactly is failing for you? You need to be more specific.

Are you getting a runtime error? How would you know? You are catching
and discarding all exceptions when calling SaveToFile(). You are also
not protecting the (Begin|End)Update pair of calls from unexpected
exceptions.

Is the code simply not saving the file where you are expecting? Where
and how is Logger initialized? Where and how is sLogFile initialized?
Are you using an absolute file path, or a relative file path?

Also, this code is NOT thread-safe, as you are not synchrozing access
to the TStringList at all. Do you call AddLogEntry() in multiple
threads at all?

Do I need to use Syncronize for that ?

If AddLogEntry() is called by multiple threads, then yes.

What is the best way of creating a logger from a TTHread ?

Since the code is inside a service, is there a reason why you are not
simply using TService's own built-in logging to the Windows Event Log?

Is the first one a lucky one Windows service which is Thread can write
to disk without calling Synchronize() ?

Probably. Hard to say without seeing the code that is actually calling
AddLogEntry().

--
Remy Lebeau (TeamB)
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 11:26 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

Thank you very much for answering me.

Jayme Jeffman wrote:

I am not succeeding on creating a file on disk from within the
Thread.

And what exactly is failing for you? You need to be more specific.

Are you getting a runtime error? How would you know? You are catching
and discarding all exceptions when calling SaveToFile(). You are also
not protecting the (Begin|End)Update pair of calls from unexpected
exceptions.

Even though I have forced an exception using throw Exception("text") the service starts and no error is logged to Windows events.

I was trying to get the service executable folder using GetModuleFileName, as it was being called inside the secondary thread I suspected that it might be the reason for not getting the log file saved to that folder.

The value of iNivelLog is 2, so the log file should be saved for each call of AddLogEntry using LogNiv equal or greater then 2.

Is the code simply not saving the file where you are expecting? Where
and how is Logger initialized? Where and how is sLogFile initialized?
Are you using an absolute file path, or a relative file path?

Logger is initialized on the secondary TThread create event.
I am actually trying to check if Log File is being initialized.

Also, this code is NOT thread-safe, as you are not synchrozing access
to the TStringList at all. Do you call AddLogEntry() in multiple
threads at all?
How can I change it to thread-safe ?
I am calling AddLogEntry just from inside the secondary thread.

Do I need to use Syncronize for that ?

If AddLogEntry() is called by multiple threads, then yes.

What is the best way of creating a logger from a TTHread ?

Since the code is inside a service, is there a reason why you are not
simply using TService's own built-in logging to the Windows Event Log?
First of all I do not know how to log events to Windows, and also as I am logging messages to tell the service is effectively running and saving data to the database, I think it is not a good pratice to send all this stuff to windows logs.

Is the first one a lucky one Windows service which is Thread can write
to disk without calling Synchronize() ?

Probably. Hard to say without seeing the code that is actually calling
AddLogEntry().

AddLogEntry is being called like AddLogEntry("Serviço Iniciando", evtSystem); where evtSystem = 3

Even a direct call to SaveToFile { Logger->SaveToFile("E:
Software
Apps_SSC
Sul_Sto2Min
bin
Sul_ Sto2Min.log"); } does not work.

I think some error is not being logged.

--
Remy Lebeau (TeamB)

Best regards

Jayme Jeffman
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Writing to disk inside a TThread [Edit]
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2018 1:13 PM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

Even though I have forced an exception using throw Exception("text")
the service starts and no error is logged to Windows events.

Only an uncaught exception would stop the service, and be logged
automatically, and even then only if the exception were thrown in the
TService::OnStart or TService::OnExecute event.

But, if you throw (and don't catch) an exception in a worker thread of
your own making, then nothing happens automatically. An uncaught
exception in a worker thread would simply terminate the thread, but the
service will still be running (unless you assign an OnTerminate event
handler to the thread, to signal the service to stop when the thread
terminates for any reason).

I was trying to get the service executable folder using
GetModuleFileName, as it was being called inside the secondary thread

That will work fine, provided you strip off the filename portion from
the end of the returned path (in VCL terms, you can use
ExtractFilePath(ParamStr(0)) to do the same thing).

I suspected that it might be the reason for not getting the log file
saved to that folder.

Did you remember to strip off the filename from the path that is
returned by GetModuleFileName()? If not, then you will have an invalid
path to save the log file to.

Also, make sure the user account that is running your service (SYSTEM
by default) has write access to the folder which you decide to save the
file in, and that the folder is not silently redirecting file accesses
to another folder if your service is a 32bit EXE running inside of the
WOW64 emulator on a 64bit machine.

Logger is initialized on the secondary TThread create event.

TThread does not have a create event. I assume you mean the
constructor instead.

How can I change it to thread-safe ?

Protect access to the TStringList using a synchronizaation object, such
as Syncobjs::TCriticalSection, Syncobjs::TMutex, or System::TMonitor.

I am calling AddLogEntry just from inside the secondary thread.

If the worker thread (not just the TThread object in general, but
specifically the actual thread context that is running inside of the
TThread::Execute() method) is the only thread context that ever
accesses the TStringList, then you don't need to synchronize access to
it.

First of all I do not know how to log events to Windows

TService has a public LogMessage() method. There is also a
TEventLogger class defined in the same unit as TService.

as I am logging messages to tell the service is effectively running
and saving data to the database, I think it is not a good pratice to
send all this stuff to windows logs.

Why not? That is exactly what it is meant for - a central location
for apps and services to log activities to, especially if those
activities are not being displayed to the user in some UI interface.
If the user wants to see what is happening on the system, simply open
the system logs and filter accordingly. You can even create your own
system log file for just your service to use, if you want.

AddLogEntry is being called like AddLogEntry("Serviço Iniciando",
evtSystem); where evtSystem = 3

But WHERE is it being called from exactly?

Even a direct call to SaveToFile {
Logger->SaveToFile("E:
Software
Apps_SSC
Sul_Sto2Min
bin
Sul_
Sto2Min.log"); } does not work.

Does that folder exist? Does the service have write access to that
folder?

I think some error is not being logged.

Probably not, since your thread code is catching and discarding all
errors, instead of logging them yourself, or letting your service log
them.

--
Remy Lebeau (TeamB)
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 4:04 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Jayme Jeffman wrote:

Remy Lebeau (TeamB)

Thank you very much for answering me.

I think I should restart from scratch and create a TThred which does nothing more than logging messages and sleep.

How could I call the service LogMessage method from whitin the secondary thread? Should I create a TService pointer inside the secondary thread or that methos is available in the secondary thread scope ?

Thank you very much.

Best regards.

Jayme Jeffman
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 4:51 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Well, I apologise for taking your time to answer to my questions while I have forgotten to associate the TService event handlers to their events, so although the methods were defined they was not being used.

I have succeded on log a event to windows events setting up a TService pointer in the secondary thread. Now I have a way of checking up debug information.

A question remains: The present Windows service application is almost a full copy of a previous one which had not the executable filename required by the client after I have made it.

The older windows service I've made, used to write perfectly to a log file in the same folder the application executable was placed. When it was placed on the machine running windows server 2012 it stoped to save the log file. Is it possible that the System user has not right to read/write files in its own machine ?

Thank you very much.

Jayme Jeffman
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 7:53 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Thank you very much Remmy.

I have got it to work on my laptop. Next step is to make it run on Windows server 2012.

At least now I know a way of logging messages without having to write to disk.

Best regards.
Jayme Jeffman
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 8:42 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

The older windows service I've made, used to write perfectly to a log
file in the same folder the application executable was placed. When
it was placed on the machine running windows server 2012 it stoped to
save the log file.

Then you need to debug your code. If saveToFile() fails, it throws an
exception. Catch and log its details. Or use another method to create
the file, such as calling the Win32 API CreateFile() function directly,
and then log its error code if it fails (so as to not completely
rewrite your current code, you could assign the returned HANDLE to a
THandleStream on success, and thenn call SaveToStream() instead of
SaveToFile()).

Is it possible that the System user has not right to read/write files
in its own machine ?

The SYSTEM account has full access to the local machine, but no access
to remote resources over a network.

--
Remy Lebeau (TeamB)
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: Writing to disk inside a TThread [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2018 10:37 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Jayme Jeffman wrote:

The older windows service I've made, used to write perfectly to a log
file in the same folder the application executable was placed. When
it was placed on the machine running windows server 2012 it stoped to
save the log file.

Then you need to debug your code. If saveToFile() fails, it throws an
exception. Catch and log its details. Or use another method to create
the file, such as calling the Win32 API CreateFile() function directly,
and then log its error code if it fails (so as to not completely
rewrite your current code, you could assign the returned HANDLE to a
THandleStream on success, and thenn call SaveToStream() instead of
SaveToFile()).

Is it possible that the System user has not right to read/write files
in its own machine ?

The SYSTEM account has full access to the local machine, but no access
to remote resources over a network.

--
Remy Lebeau (TeamB)

Hi Remmy,

I have put it to work on the server machine and it is working fine until now.
The log file is being saved as expected. I think a Synchronize() call might have made the trick.
As I have too many changes I am not sure which was the turning point.

Thank you very much for your help. :-)

Best regards.

Jayme Jeffman

Edited by: Jayme Jeffman on Jan 31, 2018 4:38 PM
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02