S h o r t S t o r i e s

// Tales from software development

Posts Tagged ‘Deployment

Checking if a .NET assembly is compiled for 32 bit or 64 bit execution

leave a comment »

The traditional way of checking whether a program or dll is compiled as 32 bit or 64 bit is to use the Microsoft DumpBin utility with the /HEADERS switch. However, this doesn’t work for .NET assemblies as the first part of the file is a bootstrap for the .NET runtime and DumpBin will always indicate that the file is 32 bit regardless of whether the .NET code is 32 bit or 64 bit.

The solution is to use the CorFlags utility that ships as part of the Microsoft Platform SDK and the .NET Framework SDK. The output includes a 32BIT value that is set to either 0 to indicate the program is not specifically compiled as 32 bit only (i.e. it’s 64 bit capable) or 1 to indicate it is 32 bit only. For example, this is the output for an assembly compiled using a Platform Target of AnyCPU:

Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 9
ILONLY : 1
32BIT : 0
Signed : 1

And this is the output for an assembly compiled with a Platform Target of x86:

Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 11
ILONLY : 1
32BIT : 1
Signed : 1

Advertisements

Written by Sea Monkey

July 15, 2013 at 8:00 pm

The service did not respond to the start or control request in a timely fashion.

leave a comment »

I deployed a Windows Service based application last week and hit a problem that demonstrates that you just can’t take anything for granted…

I copied the installer to the customer’s test server and it installed without any problems. I changed a couple of settings in the service executable’s .config file according to what was required for the environment and then tried to start the service.

It was immediately obvious that something was wrong as the service normally starts without any delay. After about 20 seconds the Services console displayed an error message:

The service did not respond to the start or control request in a timely fashion.

The service writes to a log file so I opened Windows Explorer and navigated to the installation folder to view the log. I expected to see a newly created Logs folder and a log file for the current date but neither had been created. This customer is very focused on security and I wondered if they had changed the permissions on the service account so that it could not write the the file system.

I enabled the service application’s tracing feature and started SysInternals’ DbgView to see how far the application was getting during its startup. The DbgView window remained blank while I tried to start the service. This really had me scratching my head – it was almost as if the service executable wasn’t being executed at all.

Out of desperation I opened a command line window and typed “sc start service-name“. Thankfully I got a useful error message at last indicating that the .NET 2.0 Framework was not installed.

Yep, that would explain it.

Written by Sea Monkey

August 1, 2010 at 10:00 am

Posted in Debugging, Deployment

Tagged with ,

WiX: Registering a .NET COM Interop assembly

leave a comment »

I needed to register a .NET COM Interop assembly as part of a deployment but the WiX documentation doesn’t give explicit details on how to do this. Searching for an answer shows that a few people are using custom actions to invoke RegAsm at install time. This approach is wrong for several reasons but let’s stick with the idea that the Windows Installer won’t know it needs to unregister the assembly on an uninstall as being one of the more significant.

The solution is fairly simple: generate the TLB for the interop assembly using RegAsm and then run Heat against the assembly and the TLB:

RegAsm.exe InteropAssembly.dll /tlb
Heat.exe file InteropAssembly.dll /out asm_fragment.wxs
Heat.exe file InteropAssembly.tlb /out tlb_fragment.wxs

Copy the two WiX fragments in the .wxs files to your WiX source and edit them as required.

Written by Sea Monkey

December 24, 2009 at 8:00 am

Posted in Deployment

Tagged with

Reading values in an XML configuration file from a batch file.

leave a comment »

The last couple of projects that I’ve worked on have included a couple of batch files as part of the deployment. These use some values such as file paths and database connection strings that are specific to the deployment environment and, ideally, should be configured by the installer. Although the WiX Util namespace includes a couple of tasks to modify an XML file it doesn’t offer any means to modify a text file such as a batch file.

So, I wondered, could I put the configurable values in an XML file and access them from the batch files ? It seemed unlikely but it’s actually surprisingly easy. It’s not pretty though…

In the following example the value to be retrieved is the text between a <connectionString> element and its matching end element in an XML file called config.xml:

FOR /f "tokens=2 delims=><  " %%a IN ('TYPE config.xml ^| FIND "<connectionString>"') DO SET CONNECTION_STRING=%%a

 
What isn’t obvious in the code above is that the delims argument is set to four characters – the greater than sign, less than sign, space, and tab.

The value is placed in an environment variable called CONNECTION_STRING.

It really is as simple as that!

This can be made a lot more sophisticated by using some of the features that Microsoft has added to batch processing over the past few iterations of Windows. For instance, put the code after a label and then call the label passing in a set of arguments such as the name of the file, the name of the value to be read, and the name of the environment variable to place the value in:

:GET_CONFIG_VALUE
REM Must be called using this syntax:
REM CALL :GET_CONFIG_VALUE cmd-filename config-filename config-value-name variable-name
REM where
REM   cmd-filename is the name of this file, e.g. use %~f0
REM   config-filename is the name of the XML config file
REM   config-value-name is the XML element name of the value in the config file
REM   variable-name is the name of the environment variable that should be set to the value found in the config file
SET GCV_CONFIG_FILE=%2
SET GCV_VALUE_NAME=%3
SET GCV_VARIABLE_NAME=%4
REM Note that the delims arguments used below include the tab and space characters.
FOR /f "tokens=2 delims=><  " %%a IN ('TYPE %GCV_CONFIG_FILE% ^| FIND "<%GCV_VALUE_NAME%>"') DO SET %GCV_VARIABLE_NAME%=%%a
GOTO :EOF

 
It’d be useful to have a generic routine for missing values too:

:MISSING_CONFIG_VALUE
ECHO ERROR: No value was found in the '%GCV_CONFIG_FILE%' file for '%GCV_VALUE_NAME%'.
GOTO EXIT

 
This code can now be called from the body of the batch file like this:

SETLOCAL
REM Get the configuration values from the config file:
SET CONFIG_FILE=%~f0.config
CALL :GET_CONFIG_VALUE "%~f0" "%CONFIG_FILE%" connectionString CONNECTION_STRING
IF "%CONNECTION_STRING%"=="" GOTO MISSING_CONFIG_VALUE
CALL :GET_CONFIG_VALUE "%~f0" "%CONFIG_FILE%" connectionTimeout CONNECTION_TIMEOUT
IF "%CONNECTION_TIMEOUT%"=="" GOTO MISSING_CONFIG_VALUE
REM Do some stuff here...
ENDLOCAL
GOTO :EOF

 
Most of this is obvious but it might be worth pointing out that %~f0 evaluates as the filename of the batch file that’s executing.

Written by Sea Monkey

December 2, 2009 at 10:00 am

Posted in Deployment, Development

Tagged with ,

Could not start the service on Local Computer, Error 193: 0xc1

leave a comment »

During deployment testing of a new project that’s implemented as a Windows Service I got an error message that I’ve never seen before when I tried to start the service: 

Could not start the Vitality PASMDI Service service on Local Computer
Error 193: 0cx1

I knew the service worked as I’d been running it on my development PC. I’d also successfully started the service after installing an earlier version of the installer so why wouldn’t the service start after installing using the latest version of the installer ?

The last change that I’d made to the installer was to add an XML data file that the service loads when it starts. I reviewed the WiX source code and realised that I’d added the new file incorrectly. The WiX ServiceInstall element is associated with the first File element in a component or with the File element that specifies KeyPath=”yes”. I was using the ‘first File element in the component’ method but had added the new file as the first File element in the component and so had inadvertantly replaced the association of the ServiceInstall element and the Windows Service executable with an association to the XML data file.

I confirmed this by checking the service properties in the Services console. As expected, the ‘Path to executable’ value was the path for the XML data file and not the service excutable.

The problem was resolved by moving the File element for the new file so that the first File element in the component was the one for the service executable.

The point is, the inscrutable “Error 193: 0cx1” error message simply indicates that the file that the service definition references is not an executable.

Written by Sea Monkey

November 23, 2009 at 8:00 am

Posted in Deployment

Tagged with

WiX: Creating Scheduled Tasks

with 2 comments

The SCHTASKS command line program provides functions for creating and deleting scheduled tasks. It’s the quickest and easiest means to create a scheduled task as part of an install and the option I chose for the installer for the project I’m currently working on.

However, I’ve found that while creating a scheduled task is easy enough, deleting it during an uninstall is a little bit more complicated.

I defined two custom actions in the WiX file for the installer:

<CustomAction Id=”CreateFolderCleanerScheduledTask” Directory=”CLEANER_DIR” ExeCommand=””[SystemFolder]schtasks.exe” /Create /RU “[ScheduledTaskUsername]” /RP “[ScheduledTaskPassword]” /SC DAILY /MO 1 /ST 01:00:00 /TN “VitalData Folder Cleaner” /TR “\”[CLEANER_DIR]DailyClean.cmd\””” Execute=”commit” Return=”ignore” />

<CustomAction Id=”DeleteFolderCleanerScheduledTask” Directory=”CLEANER_DIR” ExeCommand=””[SystemFolder]schtasks.exe” /Delete /F /TN “VitalData Folder Cleaner”” Execute=”commit” Return=”ignore” />

The first custom action executed successfully during the install to create the scheduled task but the second failed with an odd message indicating that the program, presumably SCHTASKS, could not be found.

I got lucky when I guessed it was something to do with the Execute attribute. I changed it to “deferred” (on the second custom action only) and then the program executed successfully.

Now I just have to work out why that fixed the problem!

Written by Sea Monkey

October 8, 2009 at 6:00 pm

Posted in Deployment

Tagged with

WiX ALLUSERS=1

leave a comment »

I created an installer from scratch last week using WiX for the project I’m currently working on. Earlier this week I went to my client’s offices and ran the installer on one of their servers.

After the install completed successfully I quickly checked that the deployed software was functioning correctly. Everything looked OK until I manually ran a scheduled task that the installer created and it failed. It appeared that a COM component that the program called was not registered but when I ran the program in a command window it ran without any problems.

It took me a few minutes to realise what the problem was and how I’d caused it.

One of the installation actions is to install and register a COM library used by the program that the scheduled task calls. When I ran the program in a command Window it executed successfully but the scheduled task runs under a service account and this failed with a ‘Class not found’ error message.

The problem was that because I hadn’t specified the ALLUSERS property in the WiX file for the installer, the default value of “” (empty string) was used which creates a ‘per user’ install. One of the differences between a per-machine and a per-user installation is that COM Registration information is written under the HKCU (current user) registry key instead of the HKLM (local machine) key.

So, the COM component was only registered in the context of the account I was logged in with when the installer ran and it wasn’t registered in the context of the service account that the scheduled task runs under.

All that’s required to fix this is to set the ALLUSERS property to 1 by including a Property element for it under the Product element, e.g.

<!– Set ALLUSERS=1 to create a per machine install rather than a per user install. –>
  <Property Id=”ALLUSERS”><![CDATA[1]]></Property>

There’s a useful explanation of the differences between a per-machine and a per-user install on the MSDN website.

Written by Sea Monkey

September 29, 2009 at 6:00 pm

Posted in Deployment

Tagged with