Question

Can you advice me how to get "Log On As" parameter of specific windows service? I need to re-register service in our upgrade project and it needs to be run under the same account as it was set up originally. I've found QueryServiceConfig in advapi32.dll with lpServiceStartName in returned structure but I am not able to make it work from Inno Setup.

Was it helpful?

Solution

You cannot use QueryServiceConfig function from InnoSetup script. To use this function, you would have to allocate buffer from heap and that's impossible in InnoSetup. Instead you can use WMI, or to be more specific, the Win32_Service WMI class, which contains the StartName property, that you've asked for. In InnoSetup script it might look like this:

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program

[Code]
function GetServiceStartName(const AServiceName: string): string;
var
  WbemLocator: Variant;
  WbemServices: Variant;
  WbemObject: Variant;
  WbemObjectSet: Variant;  
begin;
  Result := '';
  WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  WbemServices := WbemLocator.ConnectServer('localhost', 'root\CIMV2');
  WbemObjectSet := WbemServices.ExecQuery('SELECT * FROM Win32_Service ' +
    'WHERE Name = "' + AServiceName + '"');
  if not VarIsNull(WbemObjectSet) and (WbemObjectSet.Count > 0) then
  begin        
    WbemObject := WbemObjectSet.Item('Win32_Service.Name="' + 
      AServiceName + '"');    
    if not VarIsNull(WbemObject) then
      Result := WbemObject.StartName;      
  end;
end;

procedure SvcStartNameTestButtonClick(Sender: TObject);
begin
  MsgBox(GetServiceStartName('Netlogon'), mbInformation, MB_OK);
end;

procedure InitializeWizard;
var
  SvcStartNameTestButton: TNewButton;
begin
  SvcStartNameTestButton := TNewButton.Create(WizardForm);
  SvcStartNameTestButton.Parent := WizardForm;
  SvcStartNameTestButton.Left := 8;
  SvcStartNameTestButton.Top := WizardForm.ClientHeight - 
    SvcStartNameTestButton.Height - 8;
  SvcStartNameTestButton.Width := 175;
  SvcStartNameTestButton.Caption := 'Get service start name...';
  SvcStartNameTestButton.OnClick := @SvcStartNameTestButtonClick;
end;

Quite easier (and probably faster) would be to make an external library and call it from the script. If you have Delphi or Lazarus, you can use the following function, which uses the QueryServiceConfig function to get the lpServiceStartName member, that you asked for:

function GetServiceStartName(const AServiceName: string): string;
var
  BufferSize: DWORD;
  BytesNeeded: DWORD;
  ServiceHandle: SC_HANDLE;
  ServiceManager: SC_HANDLE;
  ServiceConfig: PQueryServiceConfig;
begin
  Result := '';
  ServiceManager := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if ServiceManager <> 0 then
  try
    ServiceHandle := OpenService(ServiceManager, PChar(AServiceName),
      SERVICE_QUERY_CONFIG);
    if ServiceHandle <> 0 then
    try
      if not QueryServiceConfig(ServiceHandle, nil, 0, BufferSize) and
        (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
      begin
        ServiceConfig := AllocMem(BufferSize);
        try
          if QueryServiceConfig(ServiceHandle, ServiceConfig, BufferSize,
            BytesNeeded)
          then
            Result := ServiceConfig^.lpServiceStartName;
        finally
          FreeMem(ServiceConfig);
        end;
      end;
    finally
      CloseServiceHandle(ServiceHandle);
    end;
  finally
    CloseServiceHandle(ServiceManager);
  end;
end; 

OTHER TIPS

I didn't liked the idea of linking external library so I finally solved the problem this way:

function GetServiceLogonAs():string;
var
  res : Integer;
  TmpFileName, FileContent: String;
begin
  TmpFileName := ExpandConstant('{tmp}') + '\Service_Info.txt';
  Exec('cmd.exe', '/C sc qc "MyServiceName" > "' + TmpFileName + '"', '', SW_HIDE, ewWaitUntilTerminated, res);
  if LoadStringFromFile(TmpFileName, FileContent)  then
  begin    
    Result := Trim(Copy(FileContent,Pos('SERVICE_START_NAME', FileContent)+20,Length(FileContent)-(Pos('SERVICE_START_NAME', FileContent)+21)));
    DeleteFile(TmpFileName);
  end
  else
  begin
    ShowErrorMsg('Error calling: GetServiceLogonAs(" + MYSERVICE + ")', res);
    Result := '';
  end;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top