How can I automate registration of an ATL service? - com

I have a project that includes an ATL service. Currently, our installer registers the service with custom actions that run the installed service executable with the command:
MyService.exe /server
and unregisters it with the command
MyService.exe /unregservice
This has caused headaches in some situations where the command has failed and the installation becomes stuck in a state where it can neither be completed or fully rolled back, leaving the application unusable.
We would like to replace these custom actions by using the ServiceInstall tag to do the registration completely within the MSI file, but we can't get it to work. Our initial code looked like this:
<Component Id="c.MyService.exe" Guid="{PUT-GUID-HERE}">
<File Id="f.MyService.exe" Name="MyService.exe" KeyPath="yes"
DiskId="1" Source="$(var.MyService.TargetPath)" Vital="yes" />
<ServiceInstall Id="svci.MyService" Name="MyService" Type="ownProcess"
Start="demand" ErrorControl="normal" DisplayName="MyService"
Description="My Service" Account="LocalSystem" Interactive="no"
Vital="yes">
<ServiceDependency Id="RPCSS"/>
</ServiceInstall>
<ServiceControl Id="svcc.MyService" Stop="both" Remove="uninstall"
Name="MyService" Wait="yes" />
</Component>
The install would complete, but running the program failed with this message:
Retrieving the COM class factory for component with CLSID {...} failed due to the following error: 80040152 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))
One suggestion found via google was to add in the AppId tag inside the Component tag as follows:
<Component ... />
...
<AppId Id="$(var.MyServiceGUID)" LocalService="MyService" Description="MyService" />
...
</Component>
However this didn't have any effect.
Any ideas how to replicate the ATL's self-registration in the MSI itself?
For reference, I believe I am having the same problem as in this ancient, unanswered post: http://social.msdn.microsoft.com/Forums/vstudio/en-US/eb33cf47-628a-4fbf-a740-f81afe2f2b43/atl-service-install-and-registration-issue-com-server-vs-windows-service?forum=vcgeneral
Thanks very much

WiX has a program called Heat that can be used to harvest the COM metadata for your ATL server. Run this program to generate a wxs fragement, migrate the COM elements to your wxs fragement then rebuild and test on a clean snapshotted VM. Rinse and repeat if needed as COM/ATL is a little tricky at times.
Harvest Tool (Heat)

Related

WIX: Installing a service - AppId entries ignored/too late?

I have been attempting to create an installer using WIX (3.10.1.2213).
I am installing a Windows Service that uses DCOM.
I have a component that uses ServiceInstall and ServiceControl to do what I require.
However I am having issues when it comes to the AppId entries.
If I use the following...
<AppId Id="{REALGUID}" Advertise="no" Description="MyDescription" LocalService="MyService" ServiceParameters="-Service" />
...My installation fails when attempting to start the service - with the ever helpful "...failed to start. Verify that you have sufficient privileges to start system services".
However, if I add the registry entries manually - outside of WIX (even by exporting the entries that WIX creates - so they are definitely the same) - the installation succeeds.
Does anyone know what could be happening here?
In summary, WIX creates the entries I expect, but installation fails.
If I export these entries and start from scratch, merge the entries into the registry and rerun the installer, it succeeds.
I have attempted to move the AppId entry to other locations in the wxs file. I have also attempted to manually set the registry entries using <RegistryValue...>, but it appears to have the same result (I see the entries get created, but the service fails to start).
I'm not sure if this is relevant, but this service requires another entry under HKCR\AppID\{GUID} which I do create using <RegistryValue...>. I'm not sure if that is somehow causing a conflict and I can't currently debug the service as it doesn't get far enough.
Also, from here, I see...
If this element is nested under a Fragment, Module, or Product
element, it must be advertised.
But I don't fully understand it. Does it mean if 'directly' nested under...?
Or could this be my problem and my wxs file could be ordered badly, because I need registry entries instead of being Advertised?
It is currently something like this...
<Fragment>
<Directory...>
<Component...>
<File...>
<AppId...>
<RegistryValue...>
<ServiceInstall...>
<ServiceControl...>
I am very confused - but hopefully missing something obvious.
Hopefully someone can shed some light on it for me.
Regards,
Ads.
If it helps, here is an edited extract of the component in question...
<Component Id="ServiceControl" Guid='REALGUID' >
<File Id="MyApplication.exe" Name="MyApplication.exe" KeyPath="yes" Vital="yes" Source="$(var.MyApplication.TargetDir)\MyApplication.exe" />
<AppId Id="{REALGUID}" Advertise="no" Description="MyApplication" LocalService="MyApplication" ServiceParameters="-Service" />
<RegistryValue Root="HKCR" Key="AppID\{REALGUID}" Name="Group" Value="[GROUP]" Type="string" Action="write" />
<ServiceInstall
Id="ServiceInstaller"
Type="ownProcess"
Name="MyApplication"
DisplayName="My Application"
Description="Some Description"
Start="auto"
Account="LocalSystem"
Interactive="yes"
ErrorControl="normal"
Vital="yes" >
<ServiceDependency Id="[FTPSERVICE]" />
</ServiceInstall>
<ServiceControl Id="MyApplication" Name="MyApplication" Start="install" Stop="uninstall" Remove="uninstall" Wait="yes" />
</Component>

How do you reference a file from within a heat.exe generated harvest from elsewhere in a WiX installer?

I've written a new installer using the WiX Toolset. It appears that the best practice is to use the heat.exe application to run through your built application files and auto-generate references for inclusion in your installer. This is great and all, but I find that by using this technique you pretty much block those files from being usefully in ANYTHING else except the act of copying them to the install directory. This is because I can't seem to "reference" these files for other things. For example. One of the files I would like to install as a service, but the ServiceInstall mechanism doesn't seem to let you use something referenced out of the harvest. Also, one of my files is an XML file that I would like to modify based on input from the user during install. Again, I can't seem to reference this file out of the harvest for use in the XmlConfig mechanism.
I've seen this post, but the chosen answer doesn't actually provide an example. And to boot, the WiX compiler does not let you put '#' symbols in the identifier field because it is "not a legal identifier" as the answer post claims. And if you use the plain IDs out of the harvest, it complains that they already exist.
Here is a snippet out my harvest .wxs file.
<!-- Xml file I'd like to modify during install -->
<Component Id="cmp78CF3591818BB6F883096F2C98654BA9" Guid="*">
<File Id="fil1532F0BC6EDCE81B25489D872A72339A" KeyPath="yes" Source="$(var.MyApplication.TargetDir)\log.config" />
</Component>
<!-- ... -->
<!-- Application I'd like to install as a service -->
<Component Id="cmp84F0EA671F93094E33AE84FA2A03BA2E" Guid="*">
<File Id="filD4A27A27D20D3D734B279B4F21754836" KeyPath="yes" Source="$(var.MyApplication.TargetDir)\MyService.exe" />
</Component>
For the service install, I feel like the intuitive way would be something like this:
<Component Id="cmp84F0EA671F93094E33AE84FA2A03BA2E">
<File Id="filD4A27A27D20D3D734B279B4F21754836" />
<ServiceInstall
Id="MyServiceID"
Name="MyService"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Interactive="no">
</ServiceInstall>
<ServiceControl Name="MyService" Id="MyServiceControl" Start="install" Stop="both" Remove="uninstall" Wait="yes"/>
</Component>
But of course this does not work because it claims the IDs are duplicated. Which I guess they are, but how can I say "You know that file 'X' inside the harvest... ya, I'd like to install that as a service." I have been able to get the install to work, but I had to filter MyService.exe out of the harvest and manually added it. Now if that is the case every time you'd actually like to do something with a particular file, then I think I may just forgot using the stupid heat.exe technique and manually input every file. So what exactly is the syntax that you would use to reference files from inside the heat.exe harvest?
Simplest answer: Don't use Heat.exe to generate authoring for files that require special handling, like services. Exclude those files from harvesting (using a staging directory if necessary).
Heat has generated component definitions for you. Put the ServiceInstall element inside the component in harvest.wxs. Your new snippet would look like this:
<!-- Application I'd like to install as a service -->
<Component Id="cmp84F0EA671F93094E33AE84FA2A03BA2E" Guid="*">
<File Id="filD4A27A27D20D3D734B279B4F21754836" KeyPath="yes" Source="$(var.MyApplication.TargetDir)\MyService.exe" />
<ServiceInstall
Id="MyServiceID"
Name="MyService"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Interactive="no">
</ServiceInstall>
<ServiceControl Name="MyService" Id="MyServiceControl" Start="install" Stop="both" Remove="uninstall" Wait="yes"/>
</Component>

How to wait for a file being installed to GAC before installing a service with wix

I'm using Wix to create my application installer and using it to install an assembly in the GAC and it works fine.
My issue is when I'm setting the assembly property 'copy local=false' and I'm executing the installation, then my services is not being installed cause it can't find this dll in the local folder and it's not being installed to GAC yet.
If I'll install another component from the EXE installation and will verify that the DLL is in the GAC I will be able then to install the service.
I'm using Paraffin.exe to go all over my application directory and generate a wix file and also using Mold file to add component not from this directory.
<DirectoryRef Id="Manager">
<Component Id="NlogGACRegisterComponent" Guid="1B224CD1-6EE8-46D3-9335-A84B7D8FB87B">
<File Id="NlogDLL" Name="Nlog.DLL" Source="..\Logging\Nlog.DLL" KeyPath="yes" Vital="yes" Assembly=".net"/>
</Component>
<Component Id="ManagerServiceComponent" Guid="EA31E161-4331-4A82-8F2B-7E26F62C96D6">
<File Id="StateManagerServiceEXE" Name="ManagerHostService.exe" DiskId="1" Source="..\ManagerHostService.exe" KeyPath="yes" Vital="yes" />
<ServiceInstall Id="ServiceInstaller" Type="ownProcess" Name="ManagerHostService" DisplayName="Manager Service" Description="Manager Service" Start="auto" Account="[SERVICEACCOUNT]" Password="[SERVICEPASSWORD]" ErrorControl="normal">
<util:PermissionEx User="Everyone" GenericAll="yes" ServiceChangeConfig="yes" ServiceEnumerateDependents="yes" ChangePermission="yes" ServiceInterrogate="yes" ServicePauseContinue="yes" ServiceQueryConfig="yes" ServiceQueryStatus="yes" ServiceStart="yes" ServiceStop="yes" />
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Name="ManagerHostService" Stop="both" Remove="uninstall" Wait="yes" />
</Component>
</DirectoryRef>
This in the Mold file which responsible to install the DLL to GAC and then the service.
How can I make sure it first install the DLL's and then the service?
All files and Dlls ARE installed by the time that services are started. Look in your MSI file with Orca at the InstallExecuteSequence (or look in a verbose log) and you'll see that InstallServices and StartServices are after InstallFiles.
The issue is that assemblies aren't installed and available in the GAC until InstallFinalize, this is described here:
https://msdn.microsoft.com/en-us/library/aa370063(v=vs.85).aspx
where it says "This means you cannot use the ServiceControl Table to start the service, instead you must use a custom action that is sequenced after InstallFinalize." which is what you'll need to do.

Windows service not getting installed on major upgrade-WIX

I have my service installed on the machine by an msi package(WIX). But when an major upgrade is done,service gets uninstalled but it doesnt get installed.
This is my code:
<Component Id="abc" Guid="{E64A8CDD-816F-4544-9ACD-A2E367F7758A}">
<File Id="EventTraceService.exe" Source="..\..\..\..\Products\abc.exe" Vital="yes" KeyPath ="yes"/>
<File Source="..\..\..\..\Products\abc.exe.config" Vital="yes" />
<ServiceInstall
Id="ServiceInstaller"
Type="ownProcess"
Vital="yes"
Name="abc"
DisplayName="abc"
Description="Monitoring and management of Trace"
Start="auto"
Account="LocalSystem"
Interactive="yes"
ErrorControl="normal"
/>
<ServiceControl Id="StartService" Start="install" Stop="uninstall" Remove="uninstall" Name="abc" Wait="yes" />
Thanks in advance!
One possible explanation is that you changed the GUID of the component. As a result, (the different versions of) the same resources are being managed by different components.
From the windows installer documentation about what happens when the component rules are broken:
An author includes the same resource in two different components.
If two components have a resource under the same name and location and both components are installed into the same folder, then the removal of either component removes the common resource, which damages the remaining component.
Uninstalling either component removes the resource and breaks the other component.
The component reference-counting mechanism is damaged.
This seems to match your symptom.
If the service EXE has the same version in both packages, this happens because of the file versioning rules. Here is an article with more details: http://setupanddeployment.com/windows-installer-bugs/missing-files-upgrade/

ServiceInstall or ServiceControl Problem

During Installation, I'm installing my service using ServiceInstall and ServiceControl tags. But, my service is not running. I'm getting error message "Please check you have sufficient privilege to start service". But, I'm in Administrators group. I'm using Wix ver 3.0.
code snippet is here,
<File Id='myexe' Name='myexe.exe' DiskId='1'
Source='myexe.exe' Vital='yes'>
</File>
<ServiceInstall Id='myService' DisplayName='MySampleService'
Name='MySampleService'
ErrorControl='normal' Start='auto'
Type='ownProcess' Vital='yes' />
<ServiceControl Id="StartService"
Name="MySampleService" Start="install" Wait="yes" />
<ServiceControl Id="StopService" Name="MySampleService"
Stop="both" Wait="yes" Remove="uninstall" />
Please help me.
That is the generic error you get when the service fails to install or start (if you are telling it to do that) for any reason. It is very frustrating. The only way to debug is slowly remove dependencies until things finally work. More often than not, the service requires some code (an assembly in the GAC?) that isn't fully configured until later.
I usually debug by looking at the Services.msc and trying to start the service while the error message is up. That typically provides better error messages than the Windows Installer does.
do not try to start .NET Services depending on Components being installed into the Global Assembly Cache GAC, ServiceStart comes too early for that
You could also examine Window Eventlog to identify the problem. If the problem is a missing binary the you can use Depends to find out what is missing.
Try to use util:User element
for example:
<util:User Id="myServiceUser" Name="[USERNAME]" LogonAsService="yes" UpdateIfExists="yes" CreateUser="no"
FailIfExists="no" />