How wix uninstaller(exe) can remove itself - c#

I have mine custom installer and uninstaller, which install MSI and other resources to PC. Uninstall process works within the lines below:
<DirectoryRef Id="TARGETDIR">
<Component Id="AddRemovePrograms" Guid="*" KeyPath="yes">
<RegistryValue Id="ARPEntry1" Type="string" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="DisplayName" Value="$(var.ProductName)"/>
<RegistryValue Id="ARPEntry2" Type="string" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="DisplayVersion" Value="$(var.ProductVersion)"/>
<RegistryValue Id="ARPEntry3" Type="string" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="Publisher" Value="$(var.Manufacturer)"/>
<RegistryValue Id="ARPEntry4" Type="integer" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="NoModify" Value="1"/>
<RegistryValue Id="ARPEntry5" Type="string" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="UninstallString" Value="[CommonAppDataFolder]\[Manufacturer]\[ProductName]\Uninstaller.exe"/>
<RegistryValue Id="ARPEntry6" Type="string" Action="write" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.ProductCode)" Name="InternalVersion" Value="$(var.ProductVersion)"/>
</Component>
<Directory Id="CommonAppDataFolder">
<Directory Id="UninstallCompanyDir" Name="$(var.Manufacturer)">
<Directory Id="UninstallProductDir" Name="$(var.ProductName)">
<Component Id="UninstallerExe" Guid="*">
<File Id="UninstallerExeFile" Name="Uninstaller.exe" Source="..\Uninstaller.exe" Vital="yes" KeyPath="yes">
</File>
</Component>
</Directory>
</Directory>
</Directory>
</DirectoryRef>
In Uninstaller.exe i copy itself to TEMP folder and run it from there, but problem is my uninstaller left there (in TEMP).
Question:
How I can remove my executable file(from TEMP or original) with wix scripts ?

You can do this with batch!
something like
cmd.exe /C TIMEOUT 10 && del "{your uninstaller path}"
You run it at the uninstaller closing event. This will spawn a new cmd process and execute the delete command after 10seconds.

There is a documented "how to" for this scenario that doesn't require you to have an executable, using msiexec.exe instead of you own executable:
How To: Create an Uninstall Shortcut
You don't say whether your exe does anything other than call the uninstall, but IMO it's perfectly acceptable to copy to a temp folder and leave the executable there (and it doesn't need to be an exe because you can call CreateProcess on it as a .tmp file). There are standard tools that clear out temp folders (Disk Cleanup, server scripts) so don't worry about it.
In general you don't need an uninstall on the start menu from Windows 10 onwards. A right-click on an installed app brings up an uninstall anyway, and it may even suppress yours.

Create following batch file. This will run uninstaller, when uninstaller is done, it will delete uninstaller and batch file both.
START /WAIT YourUninstaller.exe
Del YourUninstaller.exe
Del ThisBatchFile.bat

Related

Wix 3.11 - Installer Can't Start Service

Can anybody help me find out why my Windows service won't start.
When I install it with installutil, it works perfectly. I decided to use wix to create an installer for the end user but it won't start.
Here's my code
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="IWErpnextPoll" Manufacturer="IWW" Language="1033" Version="1.0.0.0" UpgradeCode="ccc3c2fe-d20f-45ce-b978-4dc7c84ce6c8">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="IWERPNextPoll_Setup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="IWErpnextPoll" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<Component Id="ProductComponent">
<File Source="$(var.IWErpnextPoll.TargetPath)" />
<ServiceInstall Id="ServiceInstaller" Name="IWErpnextPoll" Type="ownProcess" Vital="yes" DisplayName="ERPNext2Sage" Description="A background service." Start="auto" Account=".\LocalSystem" ErrorControl="normal" Interactive="no" />
<ServiceControl Id="StartService" Name="IWErpnextPoll" Stop="both" Start="install" Remove="uninstall" Wait="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
The installer throws this error:
Service 'IWErpnextPoll' (IWErpnextPoll) failed to start. Verify that you have sufficient privileges to start system services
I ran the following in the command line:
msiexec /i IWERPNextPoll_Setup /l*v log.txt
but (my untrained eyes) didn't find anything that seemed off in the very very log file.
The service I wrote is my first forray into C#. I'll be very happy to get any pointers.
99.99% of the time this is a problem with the service.
A couple of pro tips.
1) Don't author the ServiceControl element for the first few builds until you know the thing is solid. Test the service after installation.
2) If you do author it, let it sit on the failed to start dialog and start profiling. Run the EXE from a command prompt and see if it is missing dependencies or throws errors. Have lots of logging code in the service to understand what is wrong.
ServiceInstaller is a form of self registration anti pattern. Perhaps you had some code in there doing something like creating an EventSource or a registry key and without it your service is throwing an exception.
Only profiling/debugging will tell for sure.
The answer by #Christopher Painter and the comments on my question led me towards the problem.
The problem was that I did not include my project dependencies in product.wsx. I had to add like so...
...
<Component Id="Serilog.dll">
<File Source="$(var.IWErpnextPoll.TargetDir)Serilog.dll" />
</Component>
<Component Id="Serilog.Settings.AppSettings.dll">
<File Source="$(var.IWErpnextPoll.TargetDir)Serilog.Settings.AppSettings.dll" />
</Component>
<Component Id="Serilog.Sinks.File.dll">
<File Source="$(var.IWErpnextPoll.TargetDir)Serilog.Sinks.File.dll" />
</Component>
<Component Id="RestSharp.dll">
<File Source="$(var.IWErpnextPoll.TargetDir)RestSharp.dll" />
</Component>
...
After this, things started working as expected

Relative path for SQLite not working with WIX Toolset

I'm using SQLite Database and create installer of WPF application with WIX Toolset. The problem is, The below relative path works fine when I directly run from Visual Studio but does not work when I create installer with WIX and after install this installer run program then it gives fatal error for Database file.
In Project directory I've made a Database folder, in which the database files reside as you can see in below picture:
After creating installer by WIX Toolset, installed files as below:
inventory_control.db file path:
dbConnectionString path:
I've writen code for relative path connection string as under:
Relative Path:
string relativePath = #"Database\inventory_control.db";
string currentPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
//string path = currentPath.Substring(0, currentPath.Length - 21);
string path = Path.GetDirectoryName(currentPath);
string absolutePath = System.IO.Path.Combine(path, relativePath);
string dbConnectionString = string.Format("Data Source={0};Version=3;Pooling=True;Max Pool Size=100;", absolutePath);
//string dbConnectionString = "Data Source=inventory_control.db";
sQLiteConnection = new SQLiteConnection(dbConnectionString);
dbConnectionString gives correct current path.
The above relative path works fine when I directly run from Visual Studio but does not work when I create installer with WIX. It gives a fatal error. How to resolve?
WIX File:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"><?define Inventory Control_TargetDir=$(var.Inventory Control.TargetDir)?>
<Product Id="f941ba49-4369-44d4-aa0c-b77f20aa41db" Name="Inventory Control" Language="1033" Version="1.0.0.0" Manufacturer="devtros.com" UpgradeCode="ce092371-53cc-4be9-ab5d-c7a2685af970">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<Icon Id="app_icon.ico" SourceFile="$(var.ProjectDir)app_icon.ico" />
<Property Id="ARPPRODUCTION" Value="app_icon.ico" />
<WixVariable Id="WixUIBannerBmp" Value="Images\background.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="Images\background.bmp" />
<WixVariable Id="WixUILicenseRtf" Value="$(var.ProjectDir)License.rtf" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<UIRef Id="WixUI_InstallDir" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="Inventory Control" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="ApplicationShortcutDesktop" />
<ComponentGroupRef Id="Database_files" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Inventory Control">
<Directory Id="Files" Name="Files" />
<Directory Id="Database" Name="Database" />
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="Inventory Control" />
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="9bd13330-6540-406f-a3a8-d7f7c69ae7f9">
<Shortcut Id="ApplicationStartMenuShortcut" Name="Inventory Control" Description="Inventory Control" Target="[INSTALLFOLDER]Inventory Control.exe" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="RemoveApplicationProgramsFolder" Directory="ApplicationProgramsFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\Inventory Control" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</DirectoryRef>
<DirectoryRef Id="DesktopFolder">
<Component Id="ApplicationShortcutDesktop" Guid="cde1e030-eb64-49a5-b7b8-400b379c2d1a">
<Shortcut Id="ApplicationDesktopShortcut" Name="Inventory Control" Description="Inventory Control" Target="[INSTALLFOLDER]Inventory Control.exe" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="RemoveDesktopFolder" Directory="DesktopFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\Inventory Control" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
<Component Id="Inventory_Control.exe" Guid="0a7e7061-201b-4d49-adeb-4449e9c4da3e">
<File Id="Inventory_Control.exe" Name="Inventory Control.exe" Source="$(var.Inventory Control_TargetDir)Inventory Control.exe" />
</Component>
<Component Id="Inventory_Control.exe.config" Guid="28323615-8159-4116-b1ac-e29a70bf2593">
<File Id="Inventory_Control.exe.config" Name="Inventory Control.exe.config" Source="$(var.Inventory Control_TargetDir)Inventory Control.exe.config" />
</Component>
<Component Id="System.Windows.Controls.Input.Toolkit.dll" Guid="7d678201-767a-416b-b645-b2cb7d514893">
<File Id="System.Windows.Controls.Input.Toolkit.dll" Name="System.Windows.Controls.Input.Toolkit.dll" Source="$(var.Inventory Control_TargetDir)System.Windows.Controls.Input.Toolkit.dll" />
</Component>
<Component Id="System.Data.SQLite.dll" Guid="178a5aef-c027-4215-81ae-f148ab6cd472">
<File Id="System.Data.SQLite.dll" Name="System.Data.SQLite.dll" Source="$(var.Inventory Control_TargetDir)System.Data.SQLite.dll" />
</Component>
<Component Id="Zen.Barcode.Core.dll" Guid="20e34fc3-0066-4ffd-b401-518bc1177098">
<File Id="Zen.Barcode.Core.dll" Name="Zen.Barcode.Core.dll" Source="$(var.Inventory Control_TargetDir)Zen.Barcode.Core.dll" />
</Component>
<Component Id="WPFToolkit.dll" Guid="8d974e65-defb-4675-b9e0-ff617e5ab1da">
<File Id="WPFToolkit.dll" Name="WPFToolkit.dll" Source="$(var.Inventory Control_TargetDir)WPFToolkit.dll" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<ComponentGroup Id="Database_files" Directory="Database">
<Component Id="Database_inventory_control.db" Guid="0104b919-0aa9-4dc5-9492-14c474d97cf1">
<File Id="Database_inventory_control.db" Name="inventory_control.db" Source="$(var.Inventory Control_TargetDir)Database\inventory_control.db" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
SQLite.Interop.dll: The file SQLite.Interop.dll needed to be installed along with the rest of the runtime files in order for SQLite
to function properly.
There are two flavors of the file, x86 and x64 format. It is
probably advisable to install both files in their respective folders:
Your installation folder hierarchy - mock-up:
YourBinary.exe
x86\SQLite.Interop.dll
x64\SQLite.Interop.dll
System.Data.SQLite.dll
Etc...
Read-Write DB Location: And then your database should be stored in a writeable path (or you need to make the path writeable for regular users using custom ACL permissioning - which is never a great idea).
Exceptions: Obviously try - catch your database connection, update and access code to detect these kinds of issues.
Folder Confusion: Is there a database file inside the Database folder? It sort of looks like the inventory_control.db file is installed to the main application folder, and not that Database sub-folder?
Maybe that file has been generated by the application.exe in the wrong folder?
Or maybe you have duplicated the file in the main folder for testing purposes?
Hard-Coded Dev-Box Sins?: What does it say in Inventory Control.exe.config?
Are there relevant settings in there that could override your code's values?
Could there be a hard-coded dev-box sin in there?
Path Builder: I assume you have "message boxed" the paths from the application during launch, in order to ensure that they are correct? I like to copy the path and do a Start => Run and paste the path to see that it opens. Press CTRL + C when the message box shows. Paste into Notepad. Extract the path and try it in Start => Run.
string path = currentPath.Substring(0, currentPath.Length - 21);. It is not very robust to hard code the number of characters in the file name to get the parent directory path?
Could you improve it by using Path.GetDirectoryName(currentPath)?
Or maybe even: string dir = currentPath.Substring(0,currentPath.LastIndexOf('\\'));
Attach Debugger & Debug Binaries?: Maybe you could install debug binaries and attach to them for debugging as described here: wix c# app doesn't launch after installing. Just to get a real step-through debugging session.
Path Spaces: That database connection string. Does it need quotes around its path? As in "path with spaces"? You may not have any spaces in the paths for your visual Studio project, but when installed there are spaces in the paths.
This constructs in your source does look weird, why is it necessary?:
<?define Inventory Control_TargetDir=$(var.Inventory Control.TargetDir)?>

Wix - How to get a relative path

I need to reference some dlls files in my wix project, and i need to user relative path. If i use absolute path like this
C:\Users\MyUser\Documents\any\other\folder
it works perfectly, but i need a relative path like this:
../bin/dll
but it is unable to find the folder.
This is the "ComponentGroup" section where i need to get the dll folder
<ComponentGroup Id="DllsComponent" Directory="INSTALLFOLDER" Source="../bin/dll">
<Component Id="EntityFramework.dll">
<File Name="EntityFramework.dll" />
</Component>
<Component Id="EntityFramework.SqlServer.dll">
<File Name="EntityFramework.SqlServer.dll" />
</Component>
<Component Id="EntityFramework.SqlServerd.xml">
<File Name="EntityFramework.SqlServer.xml" />
</Component>
<Component Id="EntityFramework.xml">
<File Name="EntityFramework.xml" />
</Component>
<Component Id="ParodosService.exe.config">
<File Name="ParodosService.exe.config" />
</Component>
</ComponentGroup>
and the wix project structure is this:
ParodosService.Setup
|_bin
|_dll
|_EntityFramework.dll
|_EntityFramework.SqlServer.dll
|_other files...
|_Debug
|_Release
Thanks in advance...
You can use predefined directory properties from the list
System Folder Properties
Set the closest in the directory table and than use it in the source attribute.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder">
</Directory>
</Directory>
Source attribute:
Source="$(var.DesktopFolder)../bin/dll

Custom Action in Wix to manipulate string and set property

Hi I am using Wix to create an Installer that has to write a registry value with the path of the file that the installer copies on the user's system. The problem is that the registry entry should be written in this format
file:///C:/Program Files/....
In the Wix code project I have the INSTALLFOLDER Directory Id which points to
C:\Program Files\....
I am really struggling to convert the latter notation into former. I created a Custom Action hoping to set a property so that I can use that. Following is the code
Custom Action (separate DLL for now, can it be inlined?)
public class CustomActions
{
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session.Log("Begin CustomAction1");
string origValue = session["INSTALLFOLDER"];
MessageBox.Show(origValue);
string retVal = origValue.Replace("\\", "//");
MessageBox.Show(retVal);
session["Custom_Prop"] = retVal;
return ActionResult.Success;
}
}
And the Product.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupProject1" Language="1033" Version="1.2.0.0" Manufacturer="nik" UpgradeCode="4a74ff86-49a9-4011-9794-e1c18077172f">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="SetupProject1" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<InstallExecuteSequence>
<Custom Action='FooAction' Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
</Product>
<Fragment>
<CustomAction Id='FooAction' BinaryKey='FooBinary' DllEntry='CustomAction1' Execute='immediate'
Return='check'/>
<Binary Id='FooBinary' SourceFile='MyCustomAction.CA.dll'/>
</Fragment>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupProject1" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<Property Id="Custom_Prop" Value="[ProgramFilesFolder]"></Property>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
<Component Id="cmp_Add_Mainfest_To_Registry" Guid="955A3A76-F010-4FCB-BCAF-B297AFD1C05B">
<RegistryKey Root="HKCU" Key="SOFTWARE\company">
<RegistryValue Name="LoadBehavior" Value="3" Type="integer" Action="write" />
<RegistryValue Name="Manifest" Value="[Custom_Prop]" Type="string" Action="write" KeyPath="yes"/>
</RegistryKey>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
However when I run this setup the value written in the registry is the literal string [ProgramFolder] and not its evaluation into either C:\ or C:/
Can someone help?
The reason why my code wasn't working was this line
<InstallExecuteSequence>
<Custom Action='FooAction' Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
On changing the value of Before attribute as below made this work
<InstallExecuteSequence>
<Custom Action='FooAction' Before='CostFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
However given my needs were very simple I decided to not have a separate DLL for CustomAction and instead went ahead with a Custom Action in vbscript within the Wix Project. So now the code looks like
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupProject1" Language="1033" Version="1.3.1.0" Manufacturer="nik" UpgradeCode="4a74ff86-49a9-4011-9794-e1c18077172f">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="SetupProject1" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<InstallExecuteSequence>
<Custom Action="VBScriptCommand" After="CostFinalize">NOT REMOVE</Custom>
</InstallExecuteSequence>
</Product>
<Fragment>
<CustomAction Id="VBScriptCommand" Script="vbscript">
<![CDATA[
value = Session.Property("INSTALLFOLDER")
origPath=Session.Property("INSTALLFOLDER")
If Right(webdir, 1) = "\" Then
value = Left(value, Len(value) - 1)
End If
Session.Property("SOME_PROPERTY") = Replace(origPath,"\","//")
]]>
</CustomAction>
</Fragment>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupProject1" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<Property Id="Custom_Prop" Value="[ProgramFilesFolder]"></Property>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="cmp_Add_Mainfest_To_Registry" Guid="955A3A76-F010-4FCB-BCAF-B297AFD1C05B">
<RegistryKey Root="HKCU" Key="SOFTWARE\something">
<RegistryValue Name="LoadBehavior" Value="3" Type="integer" Action="write" />
<!--<RegistryValue Name="Manifest" Value="[#FILE_VstoManifest]|vstolocal" Type="string" Action="write" />-->
<RegistryValue Name="Manifest" Value="file:///[SOME_PROPERTY]" Type="string" Action="write" KeyPath="yes"/>
</RegistryKey>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Perhaps the purists won't like this but why use a Shot gun to kill a fly?
Nikhil helped me with his answer. My installs all go to subfolders so when I find an old component I need the parent folder for install, so I came here for an answer.
Combined with this get parent
I found how to get the parent folder since I have a known fixed install sub path.
<!-- Set INSTALLFOLDER from SERVERINSTALLFOLDER without the \Server\ -->
<CustomAction Id="VBScriptInstallFolderFromFoundServer" Script="vbscript">
<![CDATA[
pathvalue = Session.Property("SERVERINSTALLFOLDER")
if pathvalue <> "" Then
Session.Property("INSTALLFOLDER") = Left(pathvalue,Len(pathvalue)-Len("\Server\"))
End If
]]>
</CustomAction>
Combined with Locate Installation directory of another product
<Property Id="SERVERINSTALLFOLDER">
<!-- Id="C_SERVER_SERVERHOST.EXE" Guid="{xxx GUID OF my exe component xxx}" -->
<ComponentSearch Id="ServerComponentSearch" Type="file" Guid="{xxx GUID OF my exe component xxx}">
<DirectorySearch Id="ServerComponentDirectorySearch" Depth="0" AssignToProperty="yes" />
</ComponentSearch>
</Property>
And with Wix remember property pattern
storing the INSTALLFOLDER path in registry.
I can now update old, or install new getting the correct install path of previous install as suggestion.
Not quite the answer to the question but as I was lead here to get this, my answer will help others on the same path...
My InstallUISequence and InstallExecuteSequence :
<!-- Save INSTALLFOLDER parameter to CMDLINE_INSTALLFOLDER -->
<Custom Action='SaveCmdLineValue' Before='AppSearch' />
<!-- Set INSTALLFOLDER from SERVERINSTALLFOLDER without the \Server\ -->
<Custom Action="VBScriptInstallFolderFromFoundServer" After="AppSearch">
SERVERINSTALLFOLDER
</Custom>
<!-- Set INSTALLFOLDER from parameter CMDLINE_INSTALLFOLDER -->
<Custom Action='SetFromCmdLineValue' After='VBScriptInstallFolderFromFoundServer'>
CMDLINE_INSTALLFOLDER
</Custom>
And lastly... in Product I to refer to the Fragment I put these in:
<!-- Install to previous install path From parameter, OR from found installation OR from registry -->
<CustomActionRef Id='SaveCmdLineValue' />
<PropertyRef Id='INSTALLFOLDER'/><!-- include Fragment -->
<PropertyRef Id='SERVERINSTALLFOLDER'/><!-- include Fragment -->
<CustomActionRef Id='VBScriptInstallFolderFromFoundServer' /><!-- include Fragment -->

Register dll for VB from C#: Regasm works, WiX does not: Runtime 430

I have a dll created by me in C#. It has an interface with guid and staff in order for it to support intellisense in Excel VB. When I register it either automatically (couple checkboxes in MSVS) or using regasm mydll.dll /tlb:mydll.dll - it works fine.
But I want to deploy it automatically with the rest of my program using WiX.
I used heat like described here Replicating Visual Studio COM registration with a WiX Installer
Quote:
Run: Regasm MyDLL.dll /tlb:MyDLL.tlb
Run: Heat file MyDLL.dll -out MyDll-1.wxs
Run: Heat file MyDll.tlb -out MyDll-2.wxs
MyDll-2.wxs contains a element that you will want to copy and nest inside the element that was generated in MyDll-1.wxs. This will give you a complete element that you can use in the installer project.
This way Excel says error loading dll and in the path correct path to my dll is shown.
If I simply put two separate components in WiX, dll is added ok to Excel VB, Intellisense works, also the path shown - is to tlb, not to dll. But it is the same for regasm, so I assume it is correct. But when I run the code in Excel VB I get the following error:
Runtime '430':
Class does not support Automation or does not support expected inteface
Again, if I directly use regasm, everything works fine.
So how should I do it in WiX?
Thanks in advance!
Update:
From heat after calling it with my dll as a parameter I get
<Component Id="cmp199C512A0B3C2ACB727633983C104D83" Guid="306F6849-3C35-4602-AB76-2FD993D675C4">
<Class Id="{97B04EDD-D0FC-4429-98FE-8D942E31CA98}" Context="InprocServer32" Description="Synthec.SynthecHandler" ThreadingModel="both" ForeignServer="mscoree.dll">
<ProgId Id="Synthec.SynthecHandler" Description="Synthec.SynthecHandler" />
</Class>
<File Id="filAEDB64435A66D04AC60E3377C956C115" KeyPath="yes" Source="Synthec.dll" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32\1.0.0.0" Name="Class" Value="Synthec.SynthecHandler" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32\1.0.0.0" Name="Assembly" Value="Synthec, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#filAEDB64435A66D04AC60E3377C956C115]" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32" Name="Class" Value="Synthec.SynthecHandler" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32" Name="Assembly" Value="Synthec, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
<RegistryValue Root="HKCR" Key="CLSID\{97B04EDD-D0FC-4429-98FE-8D942E31CA98}\InprocServer32" Name="CodeBase" Value="file:///[#filAEDB64435A66D04AC60E3377C956C115]" Type="string" Action="write" />
</Component>
and for tlb:
<Component Id="cmpB02C4AD21F21B1B50A662E8F59126370" Guid="ADBDF57B-8E1E-433D-B047-867DC71D5FB8">
<File Id="filB2F5CE36C4E03293677DE32B903A3DC7" KeyPath="yes" Source="mydll.tlb">
<TypeLib Id="{E7864806-81DA-46AC-881B-A5255CAE2D2E}" Description="Synthec" Language="0" MajorVersion="1" MinorVersion="0">
<Interface Id="{E31D9546-8BCC-4CA1-8B95-21EB6EFC0808}" Name="ISynthecHandler" ProxyStubClassId32="{00020424-0000-0000-C000-000000000046}" />
</TypeLib>
</File>
</Component>
So there is no Interface entry in dll component itself... What should I do?
RESULT:
In order to see a reference Excel wanted tlb.
To create tlb automatically by Visual Studio you should check Register for COM Interop checkbox and also run Visual Studio as administrator.
To get tlb automatically, I copied (using msbuild copy task) dll and tlb into separate directory (after build of the project where dll is created), then as suggested in an answer used heat task (installer project, before build), then referenced generated wxs (actually, componentgroup from it) in my main wxs (Product.wxs by default) file.
Also this generated wxs should then be added to the installer project in order to be seen by main wxs(so, the first time you get an error about componentgroupref not found). But then the contents of this file are updated automatically by heat task.
So, some extra tuning is required, but this way I just rebuild solution and get completely new installer with new dlls. Also this installer is capable of registering quite OK.
Hopefully this will help someone else.
I've included registry information for C# dlls using msbuild before with no problems. Here is an example of how I've done it using msbuild:
<HeatFile OutputFile="ExactaDatabaseAccess.wxs"
File="..\ExactaMobilePublish\bin\ExactaDatabaseAccess.dll"
DirectoryRefId="MOBILEBIN"
ComponentGroupName="ExactaDatabaseAccess"
SuppressUniqueIds="true"
SuppressCom="false"
SuppressFragments="true"
SuppressRegistry="false"
SuppressRootDirectory="true"
AutoGenerateGuids="false"
GenerateGuidsNow="true"
ToolPath="$(WixToolPath)"
PreprocessorVariable="var.ExactaMobileBinBasePath" />
The above is equivalent to the following heat command:
C:\Program Files (x86)\WiX Toolset v3.8\bin\Heat.exe file ..\ExactaMobilePublish\bin\ExactaDatabaseAccess.dll -cg ExactaDatabaseAccess -dr MOBILEBIN -srd -var var.ExactaMobileBinBasePath -gg -sfrag -suid -out ExactaDatabaseAccess.wxs

Categories

Resources