Wednesday, May 28, 2008

Creating a service using a batch file

Sometimes we need a quick and dirty method of installing a windows service.

The official method via the DOS prompt is uses the SC command. This command is one of the most unusual command line tools I have seen for a while ... the syntax for the parameters is "xxx= yy" - the space after the = and the lack of space before the = sign is mandatory. Adding quotes around pathnames requires escaping a quote after the initial quote. SC CREATE also appears not to set the ERRORLEVEL parameter for the batch file.

This all makes SC a highly infuriating tool.

Because of this, some users prefer hacking the registry directly while others create WIX install packages (the preferred method, but a but hard for a quick and dirty test).

The code sample below shows a batch file that can easily be modified to add a service with a dependancy on the TCPIP service that will automatically start up when Windows cranks up. There's another one below that to delete the service, but unfortunately, you seem to have to reboot to really get rid of the service.

It uses a nifty method to get the path of the batch file.

It really needs to be rewritten to use Windows PowerShell.

@echo off

REM Installs A service
REM -----------------------------------------
REM Created by MaxSoft Group KB
REM (c) 2008

REM -----------------------------------------
REM CHANGE THESE VARIABLES BELOW TO REFLECT
REM THE SERVICE PARAMETERS
REM Set up the specific variables
REM Assumes that the service is from the same directory as the batch file unless specified as the first parameter

SET PROGRAM_NAME=BroadcastEventService.exe
SET SERVICE_NAME=SP_BroadcastEvents
SET DISPLAY_NAME=StrataPay Broadcast Events
SET INSTALL_DIR=%1

REM -----------------------------------------
REM DO NOT CHANGE ANY TEXT BELOW THIS LINE

SET OLD_PROMPT=%PROMPT%
PROMPT $f$f$f$f

IF "%INSTALL_DIR%"=="" GOTO GET_CURRENTPATH
GOTO INSTALL

:GET_CURRENTPATH
REM Get the directory of the install without destroying the current directory with the new PUSHD command
pushd %0\..
rem cd /d %0\..
SET INSTALL_DIR=%CD%
popd

:INSTALL
REM Now install the service
REM SC does not seem to set the errorlevel, resulting in commands
REM being executed regardless of the success of the SC CREATE.

@echo on
SC CREATE %SERVICE_NAME% binpath= "\"%INSTALL_DIR%\%PROGRAM_NAME%\"" displayname= "%DISPLAY_NAME%" depend= Tcpip start= auto
@IF %ERRORLEVEL% NEQ 0 GOTO ERROR

NET START %SERVICE_NAME%
@IF %ERRORLEVEL% NEQ 0 GOTO ERROR

:GOOD
GOTO END

:ERROR
@echo off
echo ))))-----------------------------------------------
echo ))))An error occurred.
echo ))))-----------------------------------------------

:END
PROMPT %OLD_PROMPT%


The command to delete a service is shown below (Less generic but just as easy):


@echo off
REM Delete the service

NET STOP SP_BroadcastEvents
SC delete SP_BroadcastEvents

echo
echo You will have to restart the machine to action this command

SQL Error handling

SQL Server is a great tool, but when you are handling large volumes of queries that run automatically, you need a good framework wrapped around each query to ensure that if something out of the ordinary happens, you not only know about it, but it does not adversely imact your data.

Below is the an excerpt from our SQL Server wiki

Critical Errors can cause unexpected results within a transaction. Actions taken before the critical error will be rolled back and the transaction closed. Remaining actions will be performed without a transaction wrappper.

To overcome this we have developed a template for scripts that provide a reasonable expectation of all or nothing output for scripted updates.

This template is recommended for changes to existing schema particularly if data is present in the table being modified.

The template consists of a generic header and footer for the transaction handling. Each command is followed by a generic code block to ensure a transaction remains in effect.



-----------------------------------------------------------------
-- STANDARD ERROR HANDLING - HEADER START
-----------------------------------------------------------------
IF EXISTS (SELECT *
FROM tempdb..sysobjects
WHERE id = Object_id('tempdb..#tmpErrors'))
DROP TABLE #tmperrors

GO

CREATE TABLE #tmperrors (
error INT)

GO

SET xact_abort on

GO

SET TRANSACTION isolation LEVEL serializable

GO

BEGIN TRANSACTION

GO

-----------------------------------------------------------------
-- STANDARD ERROR HANDLING - HEADER END
-----------------------------------------------------------------

-- INSERT YOUR FIRST STATEMENT HERE
GO

IF @@ERROR <> 0
AND @@TRANCOUNT > 0
ROLLBACK TRANSACTION

GO

IF @@TRANCOUNT = 0
BEGIN
INSERT INTO #tmperrors
(error)
SELECT 1

BEGIN TRANSACTION
END

GO

-- INSERT YOUR SECOND STATEMENT HERE AND REPEAT THE BELOW BLOCK AFTER EACH SUBSEQUENT STATEMENT
GO

IF @@ERROR <> 0
AND @@TRANCOUNT > 0
ROLLBACK TRANSACTION

GO

IF @@TRANCOUNT = 0
BEGIN
INSERT INTO #tmperrors
(error)
SELECT 1

BEGIN TRANSACTION
END

GO

-----------------------------------------------------------------
-- STANDARD ERROR HANDLING - FOOTER START
-----------------------------------------------------------------
IF EXISTS (SELECT *
FROM #tmperrors)
ROLLBACK TRANSACTION

GO

IF @@TRANCOUNT > 0
BEGIN
PRINT 'The database update succeeded'

COMMIT TRANSACTION
END
ELSE
PRINT 'The database update failed'

GO

DROP TABLE #tmperrors

GO

IF @@TRANCOUNT > 0
BEGIN
PRINT 'CRITICAL PROBLEM - TRANSACTION STILL OPEN - ' + CONVERT(VARCHAR,@@TRANCOUNT) + ' STILL ACTIVE'
END

GO

-----------------------------------------------------------------
-- STANDARD ERROR HANDLING - FOOTER END
-----------------------------------------------------------------