Watch, Follow, &
Connect with Us

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


Welcome, Guest
Guest Settings
Help

Thread: Problem with windows services!!!


This question is answered.


Permlink Replies: 3 - Last Post: Feb 2, 2017 11:27 AM Last Post By: Remy Lebeau (Te...
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Problem with windows services!!!  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2017 11:25 AM
Hi all,

I am trying to create a windows service app,
the TService module name is "MainService"
TMainService and i am installing it using this cmd

MainDynService.exe /install MyService "Test Main Service"
 
void __fastcall TMainService::ServiceBeforeInstall(TService *Sender)
{
Name = ParamStr(2);
DisplayName = ParamStr(3);
}
//---------------------------------------------------------------------------


The service gets installed fine with "Test Main Service"
is displayed in SCM but when i try to start it it won't
at all.

BUT. if installed the service without changing its name or
display name which will appear in SCM as "MainService".
The service works fine without any problems.

I tried it in C++ Builder and Delphi both have the same problem
if the name or display name of the service changes.

How can i fix this issue? is it a bug?

I am hoping to install multiple instances of the same service
with different names and make them all work.

Also, how can i debug services in the first place i have seen
this article before but i just don't get it what process should i
attache my service to?

http://docwiki.embarcadero.com/RADStudio/Seattle/en/Debugging_Service_Applications

Any help will be appreciated.
--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Problem with windows services!!!
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2017 12:47 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

I am trying to create a windows service app, the TService module name
is "MainService" TMainService and i am installing it using this cmd

MainDynService.exe /install MyService "Test Main Service"
 
void __fastcall TMainService::ServiceBeforeInstall(TService *Sender)
{
Name = ParamStr(2);
DisplayName = ParamStr(3);
}


The service gets installed fine with "Test Main Service" is displayed
in SCM but when i try to start it it won't at all.

You need to set the Name property in the TService's constructor instead of
in the OnBeforeInstall event. At runtime, when the service is started by
the SCM, the TService.Name property must match the same name that is used
during installation, otherwise TServiceApplication will not be able to dispatch
SCM requests to TService correctly.

This means that you will have to use the OnAfterInstall event to manually
update the TService's Registry key so you can inject your custom TService.Name
value into the ImagePath that the SCM uses to launch your executable. TService
does not allow you to specify custom command-line parameters during installation.

For example:

__fastcall TMainService::TMainService(TComponent *Owner)
    : TService(Owner)
{
    String NewName;
 
    if (FindCmdLineSwitch("install") || FindCmdLineSwitch("uninstall"))
        NewName = ParamStr(2);
    else
        NewName = ParamStr(1);
 
    if (!NewName.IsEmpty())
        Name = NewName;
}
 
void __fastcall TMainService::ServiceBeforeInstall(TService *Sender)
{
    String NewDisplayName = ParamStr(3);
    if (!NewDisplayName.IsEmpty())
        DisplayName = NewDisplayName;
}
 
void __fastcall TMainService::ServiceAfterInstall(TService *Sender)
{
    TRegistry *Reg = new TRegistry(KEY_SET_VALUE);
    try
    {
        Reg->RootKey = HKEY_LOCAL_MACHINE;
 
        if (!Reg->OpenKey(_D("\\SYSTEM\\CurrentControlSet\\Services\\") + 
Name), false)
            RaiseLastOSError(Reg->LastError);
 
        try {
            Reg->WriteString(_D("ImagePath"), QuotedStr(ParamStr(0)) + _D(" 
") + QuotedStr(Name));
        }
        __finally {
            Reg->CloseKey();
        }
    }
    __finally {
        delete Reg;
    }
 
    /* Alternatively:

    SC_HANDLE hSvcMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
    if (!hSvcMgr) RaiseLastOSError();

    try
    {
        SC_HANDLE hSvc = OpenService(hSvcMgr, Name.c_str(), SERVICE_CHANGE_CONFIG);
        if (!hSvc) RaiseLastOSError();

        try
        {
            if (!ChangeServiceConfig(hSvc, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 
SERVICE_NO_CHANGE, (QuotedStr(ParamStr(0)) + _D(" ") + QuotedStr(Name)).c_str(), 
NULL, NULL, NULL, NULL, NULL, NULL))
                RaiseLastOSError();
        }
        __finally {
            CloseServiceHandle(hSvc);
        }
    }
    __finally {
        CloseServiceHandle(hSvcMgr);
    }
    */
}


To make things a little easier and more flexible, consider using named command-line
parameters instead of relying on their fixed positions. FindCmdLineSwitch()
has an overload that supports named parameters, eg:

MainDynService.exe /install -name MyService -display "Test Main Service"
or
MainDynService.exe /install -name:MyService "-display:Test Main Service"


MainDynService.exe /uninstall -name MyService
or
MainDynService.exe /uninstall -name:MyService


__fastcall TMainService::TMainService(TComponent *Owner)
{
    String NewName;
    if (FindCmdLineSwitch("name", NewName))
        Name = NewName;
}
 
void __fastcall TMainService::ServiceBeforeInstall(TService *Sender)
{
    String NewDisplayName;
    if (FindCmdLineSwitch("display", NewDisplayName))
        DisplayName = NewDisplayName;
}
 
void __fastcall TMainService::ServiceAfterInstall(TService *Sender)
{
    ...
    Reg->WriteString(_D("ImagePath"), QuotedStr(ParamStr(0)) + _D(" -name 
") + QuotedStr(Name));
    ...
 
    /* Alternatively:
    ...
    ChangeServiceConfig(..., (QuotedStr(ParamStr(0)) + _D(" -name ") + QuotedStr(Name)).c_str(), 
...);
    ...
    */
}


BUT. if installed the service without changing its name or
display name which will appear in SCM as "MainService".
The service works fine without any problems.

SCM requests are dispatched to services by their names.

I tried it in C++ Builder and Delphi both have the same problem
if the name or display name of the service changes.

Yes.

How can i fix this issue?

See above.

is it a bug?

Yes, but in your code, not TService itself. You are modifying TService's
default behavior, but you are not modifying everything that is needed to
make that work correctly.

Also, how can i debug services in the first place i have seen this
article before but i just don't get it what process should i attache
my service to?

http://docwiki.embarcadero.com/RADStudio/Seattle/en/Debugging_Service_Applications

What is unclear about it? You start the service, then find its running process
and attach the debugger to that process.

Use Windows' Task Manager to know services are running and what their process
IDs are. You can then match those process IDs in the debugger's Attach UI.

--
Remy Lebeau (TeamB)
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Re: Problem with windows services!!!  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2017 11:52 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
I tried it using this and it gave me an error during starting
the service that it can't find the exe file:

Reg->WriteString(_D("ImagePath"), QuotedStr(ParamStr(0)) + _D(" -name ") + QuotedStr(Name));
 
ImagePath = 'D:\L-Development\Main Library\Services\Main Service\Win32\Debug\MainDynService.exe' -name 'MyService'


But when removed "QoutedStr" it worked fine:

Reg->WriteString(_D("ImagePath"), ParamStr(0) + _D(" -name ") + Name);
 
ImagePath = D:\L-Development\Main Library\Services\Main Service\Win32\Debug\MainDynService.exe -name MyService


What was wrong with the first example.

I checked other services "ImagePath" values and found
that they are different from mine.

MySQL57
ImagePath = "C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqld.exe" --defaults-file="C:\ProgramData\MySQL\MySQL Server 5.7\my.ini" MySQL57

Apache24
ImagePath = "D:\L-Development\Main Library\Servers\Apache Http Server\Apache24\bin\httpd.exe" -k runservice

Both EXEs full paths are enclosed with ( " ) instead of ( ' ).
So, was this the mistake? do i have to make my service exe
path look like this:

"D:\L-Development\Main Library\Services\Main Service\Win32\Debug\MainDynService.exe" -name MyService

to make work properly.

--
The limits of my language mean the limits of my world

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Problem with windows services!!! [Edit]
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 11:27 AM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

What was wrong with the first example.

Sorry, my bad. I meant to use AnsiQuotedStr() instead of QuotedStr():

Reg->WriteString(_D("ImagePath"), AnsiQuotedStr(ParamStr(0), #34) + _D(" 
-name ") + AnsiQuotedStr(Name, #34));


Command-line parameters inside of quotes need to use double-quotes, but QuotedStr()
uses single-quotes instead.

But when removed "QoutedStr" it worked fine:

Only if your EXE file is not on a path that has spaces in it, and your service
name does not have spaces in it. If spaces are present, quotes must be used,
otherwise the command line will not work correctly.

Both EXEs full paths are enclosed with ( " ) instead of ( ' ).

Exactly.

So, was this the mistake?

Yes.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02