Delphi Windows Services command line arguments - delphi

I have a Deplhi based Windows Service that, on installation, parses some command line arguments. I want those arguments to be added to the services command line (ImagePath value on the registry) so that the service is always started with them.
How can I accomplish this?
I want the regedit look like this:
at registry key HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\MyService
ImagePath = C:\Path\to\my\service.exe -some -arguments
Thanks
Update: The installation is done with >MyService.exe /install -some -arguments. Those arguments are the ones I want to persist in the command line.
Update: I found a solution by writing directly to the registry (see here), but I'd still like a more elegant solution, like using some TService property or something of that sort. Thanks!

Ok, after some research, I gave up on an elegant approach, and took the straight-forward path of writing directly to the registry.
To make things simple, I did this: I store the arguments I wanted to pass in variables on my TService. Then, I set the AfterInstall event to write directly into the registry (using a TRegistry object) the exact command line I wanted.
uses Registry;
procedure MyService.AfterInstall(Sender: TObject) ;
var
reg:TRegistry;
begin
reg := TRegistry.Create;
try
reg.RootKey := 'HKEY_LOCAL_MACHINE';
if reg.OpenKey('SYSTEM\CurrentControlSet\Services\MyService', True) then
begin
reg.WriteExtendString ('ImagePath', ParamStr(0) + ' -some -arguments') ;
reg.CloseKey;
end;
finally
reg.Free;
end;
end;
Not the elegant solution I was looking for, but it does the job.
Thanks for the other answers through!

Service arguments can be passed in the lpBinaryPathName argument to the CreateService function. In Delphi's TService, this is called inside TServiceApplication.RegisterServices.InstallService, which you cannot override (easily).
Therefore, I suspect the easiest way to do this is going to be to handle the TService.AfterInstall event and update the registry yourself via ChangeServiceConfig.

I Just found out something little surprising, could be worthful to share.
You can specify parameters for Windows services in (at least) following ways:
Enter it in the Service Manager GUI as "Startparameter".
Pass it as arguments to: sc.exe YourService param1 param2
Enter it in registry in entry ImagePath=..\YourService.exe param1 param2
In Delphi there are 2 ways to query application/service parameters:
System.ParamCount/System.ParamStr: This way you get the parameters of 3. above.
TService.ParamCount/TService.Param[]: This way you get the parameters of 1. and 2. above.

You should use the SCM API to properly install the service and configure it. SC.EXE is a command line interface to the SCM API. You should not manipulate the registry directly in this situation, it works but it also depends which permission the service account has.
Anyway I wonder why you need command line arguments if you embed them in the code - usually those arguments are set outside the service (by an installer, configurator, whatever) to change the service behaviour.

I don't think you can make the service start with them, but if you store those parameters in the registry you can modify the program to on future starts always go and grab them. (i.e. if ParamCount = 0 then retrieve params from registry)

Pablo,
I had the same question and couldn't find anything and went the route of editing the registry as well.
In addition, I use Custom Actions also to create additional install directories and copy files (in C#).
Your suggestion was helpful and I stopped looking for an "elegant" way as well.
thanks

Related

How to pass parameter to Jenkins service

I have a Jenkins service which I run like sudo service jenkins start|stop. Now, I want to pass a parameter --prefix=/jenkins to this service. I tried sudo service jenkins --prefix=/jenkins but this param is ignored. How can I pass this additional param?
You should edit /etc/default/jenkins. The quick and dirty way is to find the variable JENKINS_ARGS="..." at the end of the file. Simply add --prefix=/jenkins there.
Let's say that you had:
JENKINS_ARGS="--webroot=/var/cache/$NAME/war --httpPort=$HTTP_PORT"
it should be:
JENKINS_ARGS="--webroot=/var/cache/$NAME/war --httpPort=$HTTP_PORT --prefix=/jenkins"
The nice way is to edit the variable PREFIX which should be located a few lines above, in my case it was PREFIX=/$NAME, change that to PREFIX=/jenkins and then, similar to before, you edit JENKINS_ARGS and add --prefix=$PREFIX.
You need to extend your init script. According to the service manual page,
service passes COMMAND and OPTIONS it to the init script unmodified
It depends on your distribution, but most likely your init script does not handle any additional options. If you fix that, then you will be able to pass parameters.
Having said this, the proper way to establish settings in a more permanent way is the one described by Jon S.

How to ShellExecute a program AND THEN send it command line text

I can open an external program fine with ShellExec
ret := ShellExecute(handle, 'open', PChar(filename), nil, nil, SW_NORMAL);
but then I would like to send it commands like:
msg := 'open ftp://MyUser:MyPass#www.website.com';
Is this possible?
It's possible I guess, but it's not the best way to do this. The best way is to use CreateProcess. It's a more involved API, but it will make what you are attempting simpler.
The procedure goes like this:
Create one or two pipes. You need one for the child's standard input. You need another if you wish to capture its output.
Call CreateProcess to create the child. Attach the read end of the first pipe to the child's standard in. Attach the write end of the second pipe to the child's standard output.
When you wish to send commands, write to the first pipe. When you wish to read output, read from the second pipe.
This can be daunting if you are not familiar with such coding. You might do well to find a library that makes it easy.
This MSDN article demonstrates how:
Creating a Child Process with Redirected Input and Output
Since you seem to be wanting to do FTP, you'd be better off avoiding an external process. Use a library such as Indy.

Dropwizard: customize health check address and format

Is it possible to customize Dropwizrd's healthcheck output so that, e.g.: /health for healthchecks instead of /healthcheck and some output like {“status”: 200}.
I realise I could simply write a new resource that does what ever I need, I was just wondering if there is a more standard way to do this.
From what I have read on the 0.7.1 source code it's not possible to change the resource URI for healthchecks unfortunately, I highly doubt you can change the healthcheck format. I also remember people complaining about not being able to add REST resources to admin page, only servlets. Maybe on 0.8.0?
Here are the details of what I've tracked so far on the source code. Maybe I have misread or misunderstood something, so somebody could fix it.
Metrics has actually written AdminServlet to add healtcheck servlet in a way that it checks the servlet config whether the URI is defined or not.
this.healthcheckUri = getParam(config.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI);
But dropwizard doesn't provide a way to inject this configuration in any way on AbstractServerFactory.
handler.addServlet(new NonblockingServletHolder(new AdminServlet()), "/*");
NonblockingServletHolder is the one which is providing the config to AdminServlet but is created by AbstractServerFactory with empty constructor and provides no way to change the config.
I've thought of and tried to access the ServletHolder from the Environment object on Application.run method but the admin servlets are not created until after run method is run.
environment.getAdminContext().getServletHandler().getServlets()[0].setInitParameter("healthcheck-uri", "/health");
Something like this in your run() function will help you control the URI of your healthchecks:
environment.servlets().addServlet(
"HealthCheckServlet",
new HealthCheckServlet(environment.healthChecks())
).addMapping("/health");
If you want to actually control what's returned you need to write your own resource file. Fetch all the healthchecks from the registery, run them and return whatever aggregated value you want based on their results.

Passing constants as arguments in INNO's Exec()

I have created an installer using Inno Setup in which I am executing an exe that I created to create a small service inside Windows XP. I need to pass two arguments to the exe - "-install" and the path of the installation directory. I have no way of expanding the constant {app} to pass the actual value inside of {app}. Is there some way of doing this?
Thanks
I do not really understand what you want, but maybe you are seeking the ExpandConstant function?
This should work:
[Run]
Filename: {app}\MyApp.exe; Parameters: "-install {app}";
I've done it before using InnoSetup and it puts the correct value for {app}.
If you are still having problems, please post your code.

How to call a stored procedure in IBM System i Access for Windows GUI Tool

I would like to test a DB2 stored procedure running on an AS400 system.
I have the IBM System i Access for Windows installed and can run SQL commands against the DB2 database.
My question is: What is the syntax to execute a stored procedure that takes in a parameter and returns a result as an output parameter and print the value to the screen?
Just to clarify: I am not asking how to call the proc in code. I want to execute the proc and see the results in the gui tool (which is similar to SQL Enterprise Manager).
use the keyword call and pass in the parameters.
call myStoredProc(parm1, parm2, ?);
for more details see here http://www.ibm.com/developerworks/data/library/techarticle/dm-0503melnyk/. The interesting part is Figure 5. Using the Command Editor to call an SQL procedure
What you want is possible. I have done it myself many times. Unfortunaly, I'm not at the office right now so it must be from the top of my head.
Start System i Access
Go to your iSeries icons and log on to the one where your stored procedure lives
Go to the databases icons and connect to the correct one (you've one local and probably one or more remotes)
Only then, you will see the option "run SQL script" at the bottom of your screen
Start that option and you will see a SQL editor (editor on top, viewer/messages at the bottom)
Remember that you are already connected to the correct iSeries but your JDBC request will get the *LIBL of the userprofile of your connection. Therefore you must know the schema (iseries library) of your stored procedure
Enter "call YOURSCHEMA.YOURSTOREDPROCEDURE(?,?);" and use the menu or shortcut to run that statement. Notice that - depending on your JDBC settings (see menu) - the correct syntax may be "/" instead of ".". Also, notice that you can replace the first question mark with a value.
On an additional note,
In iAccess, under every schema you will see icons for the tables, views and so on. Also an icon for stored procedures is available. You will see your SP there. Use the options to see the definition and so. This information includes detailed information about the parameters
If you want to check that on your iSeries, use the system catalog (this can be done from the SQL editor too) with "select * from qsys2.sysprocedures where procedure_name (sorry, not sure about the name of this column right now) = 'YOURSTOREDPROCEDURE';"
VERY IMPORTANT: I was never able to test the SP with the SQL editor (STRSQL) on the iSeries itself. Only the iAccess SQL editor did work correctly.
You should be able to run your SP like this:
DECLARE
usr_in YOUR_TABLE.YOUR_COLM%TYPE; --Gets the correct type by looking at column type
app_in YOUR_TABLE.YOUR_OTHER_COLM%TYPE;
BEGIN
usr_in:='some value';
app_in:='another_value';
YOUR_SP_NAME(usr_in, app_in);
END;
Or you can use EXECUTE, but it can't be dynamically prepared (not run in Java) and I think there's some other disadvantages.
EXECUTE myStoredProc(parm1, parm2, ?);

Resources