We have an existing wix script that is pretty complex & long. All the CustomActions are performed with inline vbscript.
I want to switch some of those actions from vbscript to C#. All the examples everywhere start with "create a wix project in VisualStudio...". Is there any example out there about how to add in C# code to an existing wix project? One where it is built using the old school wix command line apps?
A shameless promotion of C++ custom actions first! :-).
And: "WiX Quick Start" (some pointers to good WiX and MSI resources).
Step-By-Step: I'll give it a try, please try this (you might want to skip to the bottom source if you are sort of done with these preliminary steps - this is step-by-step for real and very slow to get to the action - you might get what you need straight from the WiX source):
In WiX Visual Studio solution, right click solution node at top => Add => New Project...
Expand WiX Toolset node, select v3 (provided that is the version of WiX you use)
Double click "C# Custom Action Project for WiX v3"
Right click "References" in WiX project (not in C# project) => Add Reference...
Go "Projects" and add a reference to the C# project (double click and OK)
Do a test build. Provided there were no errors before there should be none now.
You should see something like "CustomAction1.CA.dll" in the build Output window. The suffix *.CA.dll is added to a win32 wrapper dll for the original managed code dll. All of this is handled by WiX itself - or actually the Votive Visual Studio integration for WiX - just know the difference:
"CustomAction1.dll" - managed code dll.
"CustomAction1.CA.dll" - native win32 wrapper dll containing the native one and several other components. Include this version in your MSI.
Add the following snippet:
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
The above should compile the actual C# dll into the MSI. You can open the MSI in Orca and see in the Binary table.
It is not great, but I like to add a reference to System.Windows.Forms and use a MessageBox.Show to show a dialog from within the custom action to ensure it is running as expected. I also add the application debugger launch command for dlls built in debug mode. That way Visual Studio will be automatically invoked (if all works correctly) so the code can be stepped through.
Add the reference to "System.Windows.Forms" by right clicking the C# project's Reference node and then add "System.Windows.Forms". Also add "using System.Windows.Forms;" to the top of the source file - see full source below. The key is to remember to reference "System.Windows.Forms" at a project level.
Now add this as test code to the custom action project's "CustomAction1" custom action code snippet (see code section towards bottom for full source):
// will launch the debugger for debug-build dlls
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
MessageBox.Show("hello world");
To get a standard setup GUI (for others who read this), add a reference to WiXUIExtension as explained here (that is a step-by-step for creating a basic WiX project that compiles and has a GUI), and then inject this into your source:
<UIRef Id="WixUI_Mondo" />
I like to change <MediaTemplate /> to <MediaTemplate EmbedCab="yes" /> to avoid external source cab files (with this change cabs are compiled into the MSI).
If you don't have any components added, you can add this to include notepad.exe in your MSI for test installation under directory INSTALLFOLDER (just a trick to install something without having source files available - a source path that should resolve on any machine) - replace the whole "TODO" section - see full source below:
<Component Feature="ProductFeature">
<File Source="$(env.SystemRoot)\notepad.exe" />
</Component>
Now we need to declare the actual custom action and insert it into an installation sequence. Let's add this underneath the previous <Binary> element:
<CustomAction Id="CA1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
<InstallUISequence>
<Custom Action="CA1" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="CA1" After="CostFinalize" />
</InstallExecuteSequence>
Now build and test run the MSI. You should get numerous "hello world" messages.
That is the overall "heartbeat" of a C# / managed code custom action - the way I sometimes use them.
WiX Source Actual: And now, the synthesis - remember to replace all GUIDs!:
The construct: $(env.SystemRoot) - in the WiX source below -
gets the environment variable %SystemRoot% - which resolves to
C:\ on most systems (to list environment variables open a cmd.exe
and type set and press Enter). The below source should hence
compile on all systems without modifications:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="DemoCA" Language="1033" Version="1.0.0.0" Manufacturer="test" UpgradeCode="0adf972a-5562-4a6f-a552-dd1c16761c55">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<UIRef Id="WixUI_Mondo" />
<!-- START CUSTOM ACTION CONSTRUCTS -->
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="CA1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
<InstallUISequence>
<Custom Action="CA1" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="CA1" After="CostFinalize" />
</InstallExecuteSequence>
<!-- END CUSTOM ACTION CONSTRUCTS -->
<Feature Id="ProductFeature" Title="AddingCSharpCustomActions" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="AddingCSharpCustomActions"/>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Feature="ProductFeature">
<File Source="$(env.SystemRoot)\notepad.exe" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Steps-in-brief: short summary of changes needed:
Set manufacturer field to something.
Add and modify the custom action constructs as indicated above.
Add the Component / File elements towards bottom replacing the "TODO" section there.
Set the MediaTemplate to use embedded cabs as described above (optional, not necessary for the sample to work).
Custom Action Code: And finally the actual C# custom action test code - updated with Debugger.Launch which will launch the debugger for a debug-build DLL. You can then attach the debugger to the correct source project and step-through the code:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.Windows.Forms;
namespace CustomAction1
{
public class CustomActions
{
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
MessageBox.Show("hello world");
session.Log("Begin CustomAction1");
return ActionResult.Success;
}
}
}
Links:
Debugging MSI Custom Actions
C++ Custom Action and Common Problems
WiX resource links of all kinds
Forgot this one from earlier - simpler and might be clearer
C# custom actions - Windows Installer (Advanced Installer video)
Related
I wrote a simple wix installer with gui, which installs well. But when I run the same .msi file a second time, it takes me through the normal installation process in the gui, but uninstalls my app at the end.
Then if I run this same .msi file a third time, the installer still does through the installation gui normally, but ends up doing "uninstallation".
I don't understand why it doesn't behave like every other installer and handle installation and uninstallation gracefully.
here is part of my product xml
<Product Id="*" Language="1033" Codepage="1252" Name="..."
Version="$(var.ProductVersion)" Manufacturer="..." UpgradeCode="BDF9E310-5897-48D4-AB08-889D405F9X56">
<Package InstallerVersion="300" Platform="x64" Compressed="yes" InstallScope="perMachine" Manufacturer="..."
Comments="..." Description="..." Keywords="..."/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Product Id="*" Name="..." Version="" Manufacturer="..." UpgradeCode="...">
Auto-GUID: The Id="*" section means "auto generate product code" (the use of the * means auto-generate). When you do that
every build or rebuild of your setup gets a new product code. This
amounts to a major upgrade in MSI-terms if you also change the
ProductVersion (in one of the first 3 digits) AND you have a MajorUpgrade element such as the one you are using in the source (which is standard by the way).
Solution: You can hard code a product code if you like to be able to control when it changes.
Note: You might be in a dirty state on the system with many "overlapping" installations. Look for duplicate installations of your product by opening the Add / Remove Programs applet: WinKey + Tap R => appwiz.cpl => Enter. I would uninstall all instances, and maybe prefer to test setups on virtuals henceforth? (this also helps to discover hidden runtime depenencies - provided the virtual is saved without most runtimes).
Links: Some links with some background information on major upgrades.
MSI SDK:
https://learn.microsoft.com/en-us/windows/win32/msi/major-upgrades
Flexera / Installshield:
Major Upgrade vs. Minor Upgrade vs. Small Update
About component, package, product and upgrade codes in Windows Installer
Creating MSI Update Packages
Designing an Update-Friendly MSI Installation
https://www.flexerasoftware.com/install/products/installshield/installshield-tips-tricks.html
I have the WIX script:
<Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
<Property Id="ALLUSERS" Value="1" />
<Icon Id="icon.ico" SourceFile="{MY PATH}"/>
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<Property Id="FINDPUBLICDIR">
<RegistrySearch Id="Registry" Type="raw" Root="HKCU" Key="Software\$(var.Manufacturer)\$(var.ProductName)" Name="Location"/>
</Property>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<FeatureRef Id="Feature" />
</Product>
Everything works fine, but my Icon is still default at the Add/Remove Programs.
(Path is OK because, if i set wrong/not existed path i get compilation error)
(i tried to change InstallerVersion but nothing was happend)
Does anyone know what can be wrong ?
Typo: A common mistake is a typo in the property name, as you can see here: Wix icon in Add/Remove programs, but this is not the case here (this info added for others who experience the same problem and find your question).
Ico File: As to this case, is that a hard coded path to the icon file? Is the path to an EXE file containing an icon? As I recall you need a proper *.ico file, and not an executable to point to. Haven't worked with this in a while and can't test at the moment, but give that a go: make sure you point to a real *.ico file. You can extract the ico file using Visual Studio. Open the file as a resource (File => Open => now specify resource). Video.
Bundle: Are you installing via a bundle? If so, the ARP icon is specified for the bundle and not (just) for the MSI: Bundle Element. IconSourceFile attribute (see links - to the bundle element and also below).
Some links for safekeeping:
WIX Installer not displaying the custom image of WixUI Dialog correctly (links to good bundle samples)
How to customize icon for Wix custom bootstrapper (what ARP icons are shown? The one for the bundle or the one for the MSI?)
For a simple test I created a Wix installer app for a simple Winform app as follows. But when I run the msi created with the installer it runs for just one second and exits without installing the Winform app. Question: What could be the issue here? It seems something is missing in my Product.wxs file. Note: I'm using VS2017
Steps to produce the issue
Installed Wix Toolset Visual Studio 2017 Extension from here and followed their instructions to install WiX 3.11 RC2 from here
Created a default Winform project [just one single form nothing added to it]
Created a Wix Setup project by using Toolset\v3\Setup Project template in the same solution
In WiX Setup project Added a reference to Winform project
Built the entire solution.
Right clicked the Setup project and re-built it that created an .msi file in its \..bin\Debug folder
Double clicked the .msi file from step 6. File ran for one second, windows 10 installation dialog appeared (as it appears for any installation asking you if you want to install this program). I clicked Yes. Installer ran for one second again then exited. But the Winform app was not installed.
Default Product.wxs file [I did not add anything here except for adding a value to Manufacturer attribute]
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupProject1" Language="1033" Version="1.0.0.0" Manufacturer="WiX_test_4_Winfrm" UpgradeCode="e69fe67b-5c28-4764-8196-a6613b840eff">
<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>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupProject1" />
</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"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</ComponentGroup>
</Fragment>
</Wix>
WiX Resources: A couple of links first:
WiX quick start resources.
Hello WiX C# Custom Actions.
"Hello WiX" (transparent aluminum please)
I think what you need is the "Hello World" of WiX from CodeProject. This is quite old now, but still good at showing you the very basics of getting a working MSI compiled.
UPDATE: Below I have added a step-by-step description of how to compile an MSI from a fresh WiX 3 Visual Studio project.
Here is another answer from way back with some context information for what WiX really is: MSI vs nuget packages: which are is better for continuous delivery?. You will find the link to "hello world" here as well, but also several other links.
In summary: check the first link to get the "hello world" of WiX. Then update your source with a couple of test components and recompile. You should get hold of Orca (SDK tool) to be able to view the compiled MSI files. Since you have Visual Studio installed, try searching for Orca-x86_en-us.msi and install it (this is Microsoft's own, official MSI viewer and editor). Then find Orca in the start menu. Technically Orca is installed as part of Windows SDK (not Visual Studio), but Windows SDK is bundled with the Visual Studio install. Once you have a compiled MSI file, just right click it and select Edit with Orca.
Some experience will be needed before such a file really makes sense. In essence it is a primitive MS SQL database stored in a COM structured storage file (OLE). Essentially a file system in a file with streams for different content (such as files, and datatables, etc...). Just think of it as a database with normal referential integrity and focus on the core tables such as File and Component at first.
Minimal WiX MSI Compile - Step-By-Step
Let me try a step-by-step description of what you can do in a freshly made WiX 3 project to make it compile with a default WiX GUI. You can see these changes "merged" into a complete sample in the last section of the answer, but do read this step-by-step so it makes sense.
Create a new WiX3 project. You know how to do that, I won't waste time with the details.
Set the Manufacturer attribute to your company name. Now set a new name of your choosing to the Name attribute. In this sample it is set to MinimalTester - use something else please.
Change <MediaTemplate /> to <MediaTemplate EmbedCab="yes" /> to use embedded cab files in the MSI. This way only the MSI is created and there is no additional, external CAB file.
Directly after the MediaTemplate element, add this: <UIRef Id="WixUI_Mondo" />. This will add a default WiX dialog set to your MSI so it has the basics of what is needed to be more generically useful. You can now run repair, and modify and you get a wizard for the original install along the lines of what most MSI files provide from Installshield or Advanced Installer or other professional tools. And crucially: your administrative installation will have a dialog where you can specify where files should be extracted to.
We will add our own License Agreement to the WiX setup (or else you will get an mumbling default one). Directly following <UIRef Id="WixUI_Mondo" /> add this element: <WixVariable Id="WixUILicenseRtf" Value="TestLicenseAgreement.rtf" />. Now create the file TestLicenseAgreement.rtf and place it in the same folder as your main WiX source file (quick way: in Visual Studio, right click project and "Open Folder in File Explorer", now create the RTF file with right click => New => RTF file
. And maybe open the RTF and enter some test text). Further customization of the dialogs (bitmaps and more).
The WiX dialog set is defined in a dll, we need to reference it. In your Visual Studio WiX project: Right click References => Add Reference... => Navigate to C:\Program Files (x86)\WiX Toolset v3.11\bin\. Double click WixUIExtension.dll and finally click OK.
Now add the absolute minimal component possible in WiX with an absolute path specified as source. This is to be sure you can compile the MSI. Afterwards you can make the path relative or a variable (just add this directly under the INSTALLFOLDER directory element for now):
<Component Feature="ProductFeature">
<File Source="C:\Users\someone\SourceControl\MyProject\CoreApp.exe" />
</Component>
Finally right click the WiX project in your solution and select Build. And you can quickly test run the MSI by right clicking the WiX project and clicking Open Folder in File Explorer. Now double click on bin and then Debug (or Release if you switched to a release build - not sure what "default differences" are between the two configurations). You should see your own license agreement in the second dialog in the dialog sequence.
The later WiX versions have great defaults for attributes that are almost always set to "template values" or where two attributes essentially are redundant. Just leave them out of your source and let the WiX compiler add them with default values. Much less fuss.
As an example: The above element lacks a Component Id. During compilation it defaults to the Id of the File element it contains. The File element Id in turn, is also missing and will default to the file name specified by the Source attribute (which is a mandatory attribute).
Maybe look at this answer for a longer description and a concrete example towards the bottom: Syntax for guids in WIX? See how simple your WiX source files can be once you eliminate all the redundancy and duplication of certain attributes? Less text, less bugs.
Sample Minimal WiX Source - Inline Comments
In the below sample the component has been moved into the default ComponentGroup - hence there is no need to specify what feature it belongs to (unlike above).
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!--CHANGE 0: Set Manufacturer attribute to something, you must also specify a full GUID for UpgradeCode -->
<Product Id="*" Name="MinimalTester" Language="1033" Version="1.0.0.0" Manufacturer="My Company" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<!--Standard: <MediaTemplate />-->
<!--CHANGE #1: Enable embedded cab files, so there is only one file generated, the MSI file -->
<MediaTemplate EmbedCab="yes" />
<!--CHANGE #2: Add the following elements to link one of the default WiX GUI sequences and show the specified license agreement. The RTF file must be created and placed next to your WiX source file -->
<UIRef Id="WixUI_Mondo" />
<WixVariable Id="WixUILicenseRtf" Value="TestLicenseAgreement.rtf" />
<!--CHANGE #3: Add WiX dll reference. In Visual Studio WiX project: Right click References => Add Reference... => Navigate to C:\Program Files (x86)\WiX Toolset v3.11\bin\. Double click WixUIExtension.dll. Click OK -->
<Feature Id="ProductFeature" Title="MinimalTester" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MinimalTester" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!--CHANGE #4: Remove TODO elements, add the most basic component possible as illustrated below -->
<Component>
<File Source="C:\Users\someone\SourceControl\MyProject\CoreApp.exe" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Try to compile and test install. Should install to C:\Program Files (x86)\MinimalTester on a normal system.
Maybe see further links for WiX tutorials here: WIX Installer not displaying the custom image of WixUI Dialog correctly.
Try this:
https://github.com/iswix-llc/iswix-tutorials
https://www.youtube.com/watch?v=nnV_OU6fk8c
Disclaimer: I'm the maintainer of IsWiX, a FOSS WiX accelerator that provides enhanced project templates (scaffolding) and graphical designers to do the majority of the WiX XML heavy lifting for you. As you can see from the video, this is easily only a few minutes of work.
I have a Windows Application class where I have defined my Windows Service, and I need to generate a .msi (installer) from it.
What I have done so far for this is: create a new project in Visual Studio Enterprise 2017 - the project is of type Setup Project for Wix v3 (from Wix Toolset); inside this project I have by default References and Product.wxs. From Add References, Projects, I added the Service project.
One of the sources that I found says all that's needed is to add
Source="$(var.MyApplication.TargetPath)" />
as seen here:
http://wixtoolset.org/documentation/manual/v3/votive/authoring_first_votive_project.html
...but this doesn't work for me because:
undefined preprocessor variable $(var.MyApplication.TargetPath)
I don't know where to define this variable and what is the meaning of this path.
Excerpt here:
<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. -->
<File Source = "$(var.MyApplication.TargetPath)"/>
</Component>
</ComponentGroup>
Any ideas?
Thanks.
This is all autoenerated code except for the File Source line. Don't know what I should add for INSTALLFOLDER either and what the syntax should be.
The purpose is to generate the .msi from my windows service
The Wix documentation for this step is broken as of at least version 3.11.
Instead of creating two separate solutions (app and Wix) you need to add the Wix setup as a second project in your windows forms solution. In the app Solution Explorer pane right-click on the solution then choose Add > New Project. Choose a name like WixSetup.
Next, click on the WixSetup project > References and choose Add New Reference. The projects list should show your app since they are in the same solution.
Next, add the entry to the in Product.wxs but the documentation is incorrect there too, you need to wrap it in a component tab. (Replace MY-APPLICATION-NAME with the name of your windows forms app project.)
<Component Id="ProductComponent">
<File Source="$(var.MY-APPLICATION-NAME.TargetPath)" />
</Component>
You also need to edit line 3 of the .wsx to include a non-empty company name or to remove that attribute:
<Product Id="*" Name="WixSetup" Language="1033" Version="1.0.0.0" Manufacturer="MY-COMPANY"
Finally, you must have a release build in your main application before building the Wix MSI.
I just inherited a c# application. It currently has an entry in it's app.manifest to enable UAC
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Every time I do a debug build it inside visual studio, I get prompted that "This task requires the application to have elevated permissions". (I have an admin account but I don't logon with it when developing.)
Is there a ways to either apply a xml transformation to it (like on web.configs) or making a app.manifest for release mode?
Using the SlowCheetah NuGet package and accompanying Extension you will get the same behavior on all xml files as you have for web.config.
Be sure to install/activate the NuGet package as well as the Visual Studio Extension. Also, there are a number of Slow Cheetah versions in NuGet - I would suggest using the latest that is released by Microsoft - Microsoft.VisualStudio.SlowCheetah.
Read more on this: https://github.com/Microsoft/slow-cheetah
Edit: I had a long struggle actually getting the transform to work for App.Manifest.xml for my sharepoint add-in project. Turns out the files created for you when you use "Add transform" lack some details that if not included will cause the transform to fail (give no result). This is what I concluded:
<!-- Mandatory block in AppManifest transform files: -->
<App xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xmlns="http://schemas.microsoft.com/sharepoint/2012/app/manifest"
Name="Not transformed"
ProductID="{12345678-0000-0000-0000-123456789123}"
Version="0.0.0.0"
SharePointMinVersion="0.0.0.0"
>
<Properties>
<Title>Not transformed</Title>
<StartPage>Not transformed</StartPage>
</Properties>
<AppPrincipal>
<RemoteWebApplication ClientId="*" />
</AppPrincipal>
</App>
<!--
This block as it is written will cause no transformation whatsoever, but all elements above must be present for any transformation to be applied.
To transform an entire element along with its text content, add the attribute xdt:Transform="Replace" to the element. This will also replace all
child elements.
-->
Hope this is of help!