Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
606 views
in Technique[技术] by (71.8m points)

windows installer - Wix - ServiceControl start takes four minutes to fail, should be 30 sec

My service automatically starts during install...

<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="HeskaGateway" Wait="yes" />

And it works fine if I provide the service with a valid connection string. If I provide a bad connection string the service starts and stops very quickly... I see this when I go to Services and do a manual start. According to the documentation on MSI ServiceControl Table, a Wait value of "yes" turns into a 1 which means it should wait for 30 seconds and then fail. It takes 4 minutes and 7 seconds. Why so long?

MSI (s) (6C:78) [16:36:41:932]: Executing op: ServiceControl(,Name=HeskaGateway,Action=1,Wait=1,)
StartServices: Service: Heska Gateway
MSI (s) (6C:78) [16:40:48:862]: Note: 1: 2205 2:  3: Error 
MSI (s) (6C:78) [16:40:48:862]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1920 
Error 1920. Service 'Heska Gateway' (HeskaGateway) failed to start.  Verify that you have sufficient privileges to start system services.

EDIT: I never got to find out what my real problem was. I also had an installation sequencing error because my CustomAction (deferred) which would edit the connection string in the JSON file was triggering AFTER the ServiceStart. Trying to move the ServiceStart after that deferred custom action was awful. So I killed off the start from the ServiceControl entry and then added another custom action which silently ran "SC.EXE start HeskaGateway". I'll document that below as a solution.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The installer has a custom UI which asks the user to copy-paste their connection string given to them from support department. The installer edits a JSON file in the app folder using a deferred CustomAction. It is deferred because it needed to be after the files are written to the disk and it also needed to have elevated permissions. This all worked great until I decided to have the service start itself "at the end" of the installation. My first attempt was to use a

<ServiceControl Id="StartService" Start="install" ...>

But that was taking 4 minutes to fail. Troubleshooting showed that the service was being started BEFORE the custom action which writes the connection string into the JSON file. I needed the service start to be delayed till after the custom action. I looked at adding a second ServiceControl entry into its own component that could be scheduled much later but that gave me an uncomfortable feeling that I was going to break uninstall and repair installs. So, I just added another deferred custom action sequenced right after the JSON file edit. That new action executes "SC.EXE start MyServiceName". SC.EXE is a non-blocking way to start services so succeed or fail, it will finish quickly.

My final solution:

 <Component Id="MyCloudSync.exe" Guid="{generate-your-own-guid}">
    <File Id="MyCloudSync.exe.file" KeyPath="yes" Source="$(var.RELEASEBINARIES)MyCloudSync.exe" />
    <ServiceInstall Id="MyCloudSync.exe"
          Type="ownProcess"
          Name="MyGateway"
          DisplayName="My Gateway"
          Description="Synchronizes laboratory data with Cloud"
          Start="auto"
          ErrorControl="normal" />
    <!--Start is performed by a customer action that calls SC.EXE so it can be delayed after the custom action that writes the JSON file -->
    <ServiceControl Id="StartService" Stop="both" Remove="uninstall" Name="MyGateway" Wait="yes" /> 
  </Component>

You should note that the ServiceControl entry above does not have a "start=" The DLL that GetConnectionString and SetConnectionString calls is one of my own making. Wix has its own custom action for running command lines quietly... WixQuietExec

 <CustomAction Id= "GetConnectionString"
                  BinaryKey="MyCustomActions"
                  DllEntry="GetConnectionString"
                  Execute="immediate"/>
    <CustomAction Id= "SetConnectionString"
                  BinaryKey="MyCustomActions"
                  Impersonate="no"
                  DllEntry="SetConnectionString"
                  Execute="deferred"/>
    <CustomAction Id="SetConnectionStringDeferredParams"
                  Property="SetConnectionString"
                  Value="&quot;[INSTALLFOLDER]&quot;&quot;[CONNECTIONSTRING]&quot;" />
    <Property Id="QtExecStartService" Value="&quot;SC.EXE&quot; start MyGateway"/>
    <CustomAction Id="QtExecStartService" 
                  BinaryKey="WixCA" 
                  DllEntry="WixQuietExec" 
                  Impersonate="no"
                  Execute="deferred" 
                  Return="ignore"/>

Starting the service is just a convenience so for the installer to prevent going to Services.msc to perform the start or requiring a reboot. So I used Return="ignore". Also SC.EXE just puts the service into "Start Pending" so it probably can't return much of an error unless your service doesn't exist.

NOTE: WixQuietExec is documented here. Make sure to quote the EXE and give your property the same Id as your CustomAction that uses WixQuietExec. That info is under "Deferred Execution" but I still got it wrong on my first try.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...