1

Hi there I have piece of legacy (VS2010) Windows Service code that I have imported into VS2017 and is causing me severe frustration. This code has worked well for about the last 6 years, however when I carry out the install and attempt to start the service the SCM comes back with a timeout error. The OnStart code is as follows:

Protected Overrides Sub OnStart(ByVal args() As String)
    'Instaniate the timer for the service
    _serviceTimer = New Threading.Timer(New Threading.TimerCallback(AddressOf Tick), Nothing, 60000, 60000)
End Sub

The call back is:

Private Sub Tick(ByVal state As Object)

    'Switch off the timer event whilst the code executes
    _serviceTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite)

    If Not _started Then
        Startup()
        _started = True
    End If

    Call ServerProcess()

    'Re-enable the timer now that application code has completed
    _serviceTimer.Change(_longInterval, _longInterval)
End Sub

I originally had the Startup process in the OnStart method, however removed it as an attempt at resolving this issue, however it has not made any difference. Method Startup is as follows:

Public Sub Startup()

    Try
        'Source these settings from the local config file
        _appDataFolder = Utilities.GetSetting("AppDataRoot")
        _configPathMapped = _appDataFolder & Utilities.GetSetting("ConfigPathMapped")
        _logPath = _appDataFolder & "\" & utl.GetSetting("LogPath")

        'Instaniate the timer for the service - Commented out after moving startup code from OnStart method
        ' _serviceTimer = New Threading.Timer(New Threading.TimerCallback(AddressOf Tick), Nothing, Timeout.Infinite, Timeout.Infinite)

        'Initialise logging architecture
        _logger = New aslLog.Logger(_configPathMapped & "nlog.config", _logPath, My.Application.Info.ProductName, My.Application.Info.Version.ToString)
        _logger.SendLog("Started PSALERTS Schedule Server Service", NLog.LogLevel.Info, _serviceTimer, _checkInterval, Nothing)

        'Determine if the cloned config files exists in the mapped config file folder
        'We clone these files to a writable destination to allow us to overcome write restrictions ot the C: drive on the SPEN PTI Desktop model
        If Not System.IO.File.Exists(_configPathMapped & "psaservermachine.config") Then
            'Clone the app.config file in the config folder as psaservermachine.config
            Utilities.CloneFile(_programFileLocation & "PSALERTSScheduleServer.exe.config", _configPathMapped & "psaservermachine.config")
        End If

        If Not System.IO.File.Exists(_configPathMapped & "nlog.config") Then
            'Clone the nlog.config file
            Utilities.CloneFile(_programFileLocation & "PSALERTSScheduleServer.exe.config", _configPathMapped & "nlog.config")
        End If

        'Determine the Oracle TNS Environment
        'Check for the existence of the environment variable 'TNS_ADMIN'
        If Environment.GetEnvironmentVariable("TNS_ADMIN") IsNot Nothing Then

            'If TNS_ADMIN exists then we can continue with the application session 

        Else
            Dim oraTnsPath As String = ""
            'If it doesn't exist then we need to determine the Oracle information from the PATH environment variable
            oraTnsPath = GetOraTnsPath()

            If oraTnsPath <> "" Then
                'Then create the TNS_ADMIN environment variable
                Environment.SetEnvironmentVariable("TNS_ADMIN", oraTnsPath)
            Else

                'If no oracle client information exists then raise an error to this effect and exit the app
                'informing the user that they need to install the Oracle client in order to use PSALERTS

                Beep()
                Throw New PSALERTSOracleConfigException(
                                                "PSALERTS Oracle Configuration Error. PSALERTS Did not find a valid Oracle Client." & vbCrLf & vbCrLf &
                                                "Please install a valid Oracle Client and try again." & vbCrLf & vbCrLf &
                                                "If a valid Oracle Client is installed then ensure that the PATH environment variable contains an entry for the Oracle Client." & vbCrLf & vbCrLf &
                                                "For example - TNS_ADMIN=C:\oracle\12.1.0\Client_lite\NETWORK\ADMIN"
                                                )

            End If

        End If

        'Register the application
        If Not Registered() Then
            'Register the application
            Register()

        End If


        If Registered() Then
            'Clean/close any stray Excel processes from previous debug session
            If _debugModeOn Then
                CleanUpRedundantProcesses("EXCEL", "PSALERTS")
            End If

            'instantiate fresh excel session
            _myXLApp = New Excel.Application

            'Get the timer interval settings
            _longInterval = CType(utl.GetSettingServerMachine(_configPath, "appSettings", "LongIntervalMillis"), Integer)
            _initInterval = CType(utl.GetSettingServerMachine(_configPath, "appSettings", "InitialIntervalMillis"), Integer)
            _refreshInterval = CType(utl.GetSettingServerMachine(_configPath, "appSettings", "InitialIntervalMillis"), Integer)

            'Re-start the timer with periodic signalling as per the specified check interval
            _serviceTimer.Change(_initInterval, _initInterval)

        Else
                _started = False
        End If
    Catch ex As Exception
        _logger.SendLog("PSALERTS Schedule Server startup failure.", NLog.LogLevel.Error, ex)
    Finally

    End Try
End Sub

I use a similar technique for a number of similar services and they are running fine. Would appreciate some insight from any Windows Service gurus out there. Oh, I use WiX to carry out the install, again this is a well worn template for a number of similar such applications.

Kind Regards Paul J.

Mary
  • 14,926
  • 3
  • 18
  • 27
Paul Johnson
  • 213
  • 3
  • 14
  • Have you tried attaching a debugger to your service to find out exactly where it's timing out? You didn't mention if this is happening on your dev machine, or only after production deployment. Was the old VS2010 service running on this same machine? Have you given your service high enough permissions to run? – Hursey Apr 10 '20 at 21:43
  • No service guru, but a deployment guy. First want to verify that you have tried to start this service manually after copying all files in place manually and you have seen it fail then as well? You can also disable the service start inside the MSI and install the MSI and then try to start the service manually via the service applet - of course. – Stein Åsmul Apr 10 '20 at 22:19
  • @Hursey - Unable to attach the debugger, its as if the OnStart method of the service isn't launching. This is on a development machine. The current production version is a previous release, although the OnStart code is identical to the current version under development. The production code is on a Win7 machine. – Paul Johnson Apr 13 '20 at 08:26
  • @Stein Asmul - The service installs manually without any issue. My first approach to debugging the issue was to disable the service control element in the product.wsx file. – Paul Johnson Apr 13 '20 at 08:28
  • Guessing from your description the logging in the catch isn't happening? What would happen if you where to take your startup method and put it in a console app. Just to rule out if it's a problem with the code, or the services – Hursey Apr 13 '20 at 08:42
  • I have a UnitTest project as part of the solution. The unit test of the Startup method works perfectly, the issue would apear to be a disconnect either between the SCM and the OnStart method, or the OnStart method and the Startup method. – Paul Johnson Apr 14 '20 at 16:11

2 Answers2

1

Core: The very most typical errors:

  • Config problems: connection strings, faulty paths, etc...
  • Boot startup problem (good list - from FAQ)
  • Wrong password / login account when running as a real user with password.
  • Files missing or runtimes missing.
  • Permission problems (ACL / NT Privilege missing).

Maybe check this answer before the below.


UPDATE: Maybe have a look at this previous answer. Service startup timing issue. Also check my ad-hoc answer there in the same page.

Debugger: Other than that - nothing like stepping through the code with a debugger. I haven't done that in a long time. Deploy debug binaries and try? Windows 10 now hides messages from services - not sure how that affects debuggers: No more switching to Session 0.


I am not a service guru, but a deployment specialist. I'll just provide some links and see if that helps. Maybe I have not fully understood the whole problem. I tend to focus on the deployment side and not so much development side.

Ideas List / Debugging Check List: These are "ideas lists" for what could be wrong for applications in general - not just services (two first lists are similar - created some time apart):

  1. Crash on launch
  2. Desktop application won't launch
  3. General purpose WiX / MSI links

Yes, these lists are very generic - too large to digest. Just skim the first two I think.

Debugging Tools: Also a reminder of the most useful service debugging tools: Event Viewer, Task Manager, Services.msc, Process Explorer (system internals), The NET command and SC.exe.

Good Service FAQ: https://www.coretechnologies.com/WindowsServices/FAQ.html

Community
  • 1
  • 1
Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
  • Ok, as a possible route to resolution I changed from using system.threading.timer to system.timers.timer. The service now responds with The service on local computer started and then stopped. Some service stop automatically if they are not in use by other services or programs. – Paul Johnson Apr 14 '20 at 16:02
  • Apologies for using comment to pass new code, couldnt work out how to add to the original post. Revised OnStart is: Protected Overrides Sub OnStart(ByVal args() As String) _serviceTimer = New System.Timers.Timer AddHandler _serviceTimer.Elapsed, New Timers.ElapsedEventHandler(AddressOf Tick) _serviceTimer.Interval = _longInterval _serviceTimer.Enabled = True 'Ensure that the timer is not collected as garbage GC.KeepAlive(_serviceTimer) End Sub Call to the Startup method happens in the Delegate Tick. – Paul Johnson Apr 14 '20 at 16:23
0

Your startup method should fire up a background worker and quickly return to the SCM that it has started. There is a system wide default setting of 30 seconds but honestly a proper service should respond in a few seconds.

Looking though your code, your connection to the database is probably the long pole causing the problem.

Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
  • See comment @Stein Asmul - The OnStart method only contains the instantiation of the timer object now. The SCM comes back immediatley now. – Paul Johnson Apr 14 '20 at 16:21