cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Kovalenko
Level 6

ServiceAddService returns -1

Hello,

ServiceAddService return -1. This is no >= ISERR_SUCCESS. And Format function gives no text message for this code. What does this code mean?
Labels (1)
0 Kudos
14 Replies
Christopher_Pai
Level 16

Can you tell us what project type it is and a code snippet? If MSI, how are you scheduling it?
0 Kudos
Kovalenko
Level 6

Yes, this is a Basic MSI type.

As far as I remember (I am now at home and will check it tomorrow and send a snippet) it is scheduled after InstallServices action.

And in fact it is installed despite -1 error code and successfully started.
0 Kudos
Kovalenko
Level 6

Here is the whole function that installs Windows Service with relevant comments:


//
// Installs QEF as Windows service.
// This CA is deferred and used after InstallServices and SetInstallWindowsServiceData actions.
//
function InstallWindowsService(hMSI)
LIST lCustomActionData; // CA data list
STRING svServiceName, // QEF Windows service name
svDisplayName, // QEF Windows service display name
svDescription; // QEF Windows service description
STRING svCustomActionData, // CA data string
svBinDir, // Bin directory
svAccountType, // QEF Windows service account type
svLogin, // QEF Windws service login
svPassword, // QEF Windws service password
svStartupType, // QEF Windws service startup type
svStartAfterInstall; // QEF Windws service start after insttallation value
STRING svScriptFile, // script file in which the error occurred
svError, // error code for the error
svErrorMessage; // error message
NUMBER nvSize, // size of buffer used to get MSI properties
nResult, // result code
nAccountType, // QEF Windows service account type
nStartupType; // QEF Windws service startup type
NUMBER nvLineNumber, // line number in which the error occurred
nvError; // error code for the error
BOOL bStartAfterInstall; // QEF Windws service start after insttallation value
begin
svServiceName = "QEF";
svDisplayName = "QualiWare Execution Framework";
svDescription = "Enables deployment of QualiWare modules, management of user and groups, security and licensing, logging and monitoring.";

// Check if QEF Windows service is already installed.
if(ServiceExistsService(svServiceName) = TRUE) then
MessageBox("Cannot install Windows Servcie, because Windows Service with name \"" + svServiceName + "\" already exists!", WARNING);

return NOTHING;
endif;

nvSize = 256;
nvError = -1;

// Get CA data as a string.
nResult = MsiGetProperty(hMSI, "CustomActionData", svCustomActionData, nvSize);
if(nResult != ERROR_SUCCESS) then
MessageBox("Cannot get CA data!", SEVERE);
endif;

// Convert CA data from string to list.
lCustomActionData = ListCreate(STRINGLIST);

if (StrGetTokens(lCustomActionData, svCustomActionData, ";") > 0) then
MessageBox ("Failed to get tokens from string!", SEVERE);
endif;

// Get Bin directory from CA data list.
nResult = ListGetFirstString(lCustomActionData, svBinDir);
if(nResult = -1) then
MessageBox("Cannot get Bin directory path!", SEVERE);
endif;

// Get QEF Windows service accoount type from CA data list.
nResult = ListGetNextString(lCustomActionData, svAccountType);
if(nResult = -1) then
MessageBox("Cannot get Windows service account type!", SEVERE);
endif;

// Get QEF Windows service login from CA data list.
nResult = ListGetNextString(lCustomActionData, svLogin);
if(nResult = -1) then
MessageBox("Cannot get Windows service login!", SEVERE);
endif;

// Get QEF Windows service password from CA data list.
nResult = ListGetNextString(lCustomActionData, svPassword);
if(nResult = -1) then
MessageBox("Cannot get Windows service password!", SEVERE);
endif;

// Get QEF Windows service startup type from CA data list.
nResult = ListGetNextString(lCustomActionData, svStartupType);
if(nResult = -1) then
MessageBox("Cannot get Windows service startup type!", SEVERE);
endif;

// Get QEF Windws service start after insttallation value from CA data list.
nResult = ListGetNextString(lCustomActionData, svStartAfterInstall);
if(nResult = -1) then
MessageBox("Cannot get Windows service start after install value!", SEVERE);
endif;

// Destroy CA data list.
ListDestroy (lCustomActionData);

// Convert QEF Windows service account type from string to integer.
StrToNum(nAccountType, svAccountType);

// Set QEF Windows service login and password if account type is set to user account.
if(nAccountType = 1) then
SERVICE_IS_PARAMS.lpServiceStartName = &svLogin;
SERVICE_IS_PARAMS.lpPassword = &svPassword;
endif;

// Convert QEF Windows service startup type from string to integer.
StrToNum(nStartupType, svStartupType);

// Set QEF Windows service startup type.
switch(nStartupType)
case 0:
SERVICE_IS_PARAMS.dwStartType = SERVICE_AUTO_START;
case 1:
SERVICE_IS_PARAMS.dwStartType = SERVICE_DEMAND_START;
case 2:
SERVICE_IS_PARAMS.dwStartType = SERVICE_DISABLED;
endswitch;

// Convert QEF Windows service start after insttallation value from string to integer.
StrToNum(bStartAfterInstall, svStartAfterInstall);


nvError = ServiceAddService(svServiceName, svDisplayName, svDescription, svBinDir ^ "Qef.exe /RunAsService", bStartAfterInstall, "");
NumToStr(svError, nvError);
MessageBox("Exit code: " + svError, INFORMATION);
NumToStr(svError, ISERR_SUCCESS);
MessageBox("ISERR_SUCCESS code: " + svError, INFORMATION);
// Add QEF Windows service to the list of services to install.
if(nvError < ISERR_SUCCESS) then
svErrorMessage = "Windows sevice \"" + svServiceName + "\" cannot be installed!";
GetExtendedErrInfo(svScriptFile, nvLineNumber, nvError);
NumToStr(svError, nvError);
if(nvError = 1057) then // 1069 - wrong password
svErrorMessage = svErrorMessage + " The account name is invalid or does not exist!";
elseif(nvError = 1069) then
svErrorMessage = svErrorMessage + " The service did not start due to a logon failure!";
elseif(nvError = 2) then
svErrorMessage = "";
else
svErrorMessage = svErrorMessage + " Error code: " + svError;
endif;

MessageBox(svErrorMessage, WARNING);
endif;
end;
0 Kudos
Christopher_Pai
Level 16

If your using Basic MSI, what problem are you trying to solve that requires you to roll all of this code instead of just using the ServiceInstall table?
0 Kudos
Not applicable

This probably means that the service did not start in the time alloted by the function. Try calling GetExtendedErrInfo, this may give you more error information.

Devin Ellingson
Software Developer
Acresso Software
0 Kudos
Not applicable

ServiceAddService returns a generic success/failure message and the error message can be gathered by calling GetLastError().

More info can be found here:
http://helpnet.acresso.com/robo/projects/installshield15langref/serviceaddservice.htm
0 Kudos
Kovalenko
Level 6

The reason for using this script is to give user notifications and it is not flexible enough to run it either under System account or user account depending on user settings during installation.

GetExtendedErrInfo gives empty string.

GetLastError is a Windows API function invoked by GetExtendedErrInfo.
0 Kudos
Christopher_Pai
Level 16

If you mean to say that you give the user a choice as to what account to use to install the service, the ServiceInstall table's StartName and Password columns are formattable. You could create a dialog that collects the information into properties and then use that to create the service.

There are a couple ugly spots in MSI when it comes to authenticating the provided credentials and assining the LogonAsService right however.
0 Kudos
Not applicable

Correct.

So you're saying that the GetExtErrorInfo's 3rd parameter is returning 0?

Based on how those APIs are invoked, I can't imagine a situation in which that's really possible, but if that's what you're seeing then it sounds like maybe an exception is being thrown.
0 Kudos
Kovalenko
Level 6

I actually use a custom dialog to collect information from user. And then install the service with specified parameters with the help of ServiceAddService function. I have not come across any other documented way to do this (except using Services view that is I cannot use).

ServiceAddService function returns -1. And if I use GetExtendedErrInfo I get the same error number: -1. And when I try to use format function I get empty string.
0 Kudos
Not applicable

-1 is ISERR_GEN_FAILURE, FormatMessage only handles some errors, so you probably won't get any text for this error.

Do the svScriptFile and nvLineNumber parameters return anything?

Devin Ellingson
Software Developer
Acresso Software
0 Kudos
Kovalenko
Level 6

Here is what script file and line number values are set to:

---------------------------
*** - InstallShield Wizard
---------------------------
Script file: C:\CodeBases\isdev\Script\ISRT\Src\Service.rul
Line number: 561
---------------------------
OK
---------------------------
0 Kudos
Not applicable

This indicates that the we did not detect that the service started in the time alloted (which is INFINITE by default).

Check the values in the public 'SERVICE_IS_STATUS' structure instance, we populate this structure with the final values that we got (check dwCurrentState especially).

One possibility is that the service is failing to report SERVICE_START_PENDING during the startup time, in this case the service code will fail in this way.

Devin Ellingson
Software Developer
Acresso Software
0 Kudos
chenggj
Level 3

DevinEllingson wrote:
This indicates that the we did not detect that the service started in the time alloted (which is INFINITE by default).

Check the values in the public 'SERVICE_IS_STATUS' structure instance, we populate this structure with the final values that we got (check dwCurrentState especially).

One possibility is that the service is failing to report SERVICE_START_PENDING during the startup time, in this case the service code will fail in this way.

Devin Ellingson
Software Developer
Acresso Software


My case is this one, the service is in service_start_pending state.
0 Kudos