My issue is that clearly windows has issues accessing the dll InstallTools.dll since I'm calling a function inside OpenExeUrl
Here is my dll content
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.Configuration;
using System.Diagnostics;
namespace InstallTools
{
public class InstallHelper
{
[CustomAction]
public static ActionResult OpenExeUrl(Session session)
{
try
{
var exeUrl = InstallTools.Properties.Settings.Default.EXEURL;
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
int exitCode = 0;
// Enter in the command line arguments, everything you would enter after the executable name itself
//start.Arguments = arguments;
// Enter the executable to run, including the complete path
start.FileName = exeUrl;
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Maximized;
start.CreateNoWindow = true;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
proc.WaitForExit();
// Retrieve the app's exit code
exitCode = proc.ExitCode;
}
}
catch (Exception e)
{
var errorMessage = "Cannot open exe file ! Error message: " + e.Message;
session.Log(errorMessage);
}
return ActionResult.Success;
}
}
}
The Wix file content :
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MySetup" Language="1033" Version="1.0.0.0" Manufacturer="Sofar" UpgradeCode="c151e7ab-b83a-445f-93b2-2ab7122ea34b">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="MySetup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<Binary Id="InstallTools" SourceFile="$(var.SolutionDir)InstallTools\bin\$(var.Configuration)\InstallTools.dll"/>
<Binary Id="NotepadPlus" SourceFile="C:\Program Files (x86)\Notepad++\notepad++.exe"/>
<CustomAction Id="OpenExe" BinaryKey="InstallTools" DllEntry="OpenExeUrl" Execute="deferred" Impersonate="yes" />
<!--<CustomAction Id="OpenExe" Return="ignore" Directory="ProgramFilesFolder" ExeCommand="C:\Program Files (x86)\Notepad++\notepad++.exe" Impersonate="yes" Execute="deferred"/>-->
<InstallExecuteSequence>
<Custom Action="OpenExe" Before='InstallFinalize'/>
</InstallExecuteSequence>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MySetup" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="myAppFile">
<File Source="$(var.MyApplication.TargetPath)" />
</Component>
</DirectoryRef>
<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"> -->
<ComponentRef Id="myAppFile" />
<!-- </Component> -->
</ComponentGroup>
</Fragment>
</Wix>
You can notice that I'm calling the dll by building the custom action and add it to execution sequence.
<CustomAction Id="OpenExe" BinaryKey="InstallTools" DllEntry="OpenExeUrl" Execute="deferred" Impersonate="yes" />
<!--<CustomAction Id="OpenExe" Return="ignore" Directory="ProgramFilesFolder" ExeCommand="C:\Program Files (x86)\Notepad++\notepad++.exe" Impersonate="yes" Execute="deferred"/>-->
<InstallExecuteSequence>
<Custom Action="OpenExe" Before='InstallFinalize'/>
</InstallExecuteSequence>
My issue is that when I launch the installer I get this error shown in the following screenshot.
Could you please advice ?
1) Make your CustomAction class public: public class InstallHelper.
2) Make sure that you're referencing the correct DLL, it should end up with *.CA.dll extension. Your bin folder has supposedly two dll files: InstallTools.dll, InstallTools.CA.dll.
Related
I'm trying to build a WIX setup but it keep failing.
Error The system cannot find the file 'C:\Work\Test\CustomActionForm\bin\Debug\CustomActionForm.CA.dll'.
My product.wxs file
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupProject4" Language="1033" Version="1.0.0.0" Manufacturer="MSPmate" UpgradeCode="b9c48ec5-2f0a-4c74-abc6-0c98119861d4">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="SetupProject4" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<InstallExecuteSequence>
<Custom Action='CustomActionFormId' Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
</Product>
<Fragment>
<Binary Id="CustomActionBinary" SourceFile="$(var.CustomActionForm.TargetDir)$(var.CustomActionForm.TargetName).CA.dll" />
<CustomAction Id="CustomActionFormId" Impersonate="no" BinaryKey="CustomActionBinary" DllEntry="ShowLicenseInfo" Return="check" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupProject4" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent">
<File Source="$(var.WindowsFormsApp1.TargetPath)" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
This is my custom action.
namespace CustomActionForm
{
public class CustomAction
{
[CustomAction]
public static ActionResult ShowLicenseInfo(Session session)
{
try
{
session.Log("Custom Action beginning");
MessageBox.Show("Yo hoooooooooo");
// Do Stuff...
//if (cancel)
//{
// session.Log("Custom Action cancelled");
// return ActionResult.Failure;
//}
session.Log("Custom Action completed successfully");
return ActionResult.Success;
}
catch (SecurityException ex)
{
session.Log("Custom Action failed with following exception: " + ex.Message);
return ActionResult.Failure;
}
}
}
}
However, when I build the custom project, it build successfully but cannot even see a *.CA.dll file is generated.
What am I missing here?
Download Sample Project: Most likely there is something wrong with the compilation of that zip / win32 dll (as opposed to the
managed code dll) - please see if you can compile this project
outright - "right out of the box":
https://github.com/glytzhkof/WiXCustomActionsTesting
Causes?: There could be a simple build failure for the CA dll. There could be something wrong with your Visual Studio - maybe. Might be something completely different. Please just start testing with that sample project.
Build Output: The Visual Studio build output should look something like this:
------ Build started: Project: CustomAction1, Configuration: Debug x86 ------
Searching for custom action entry points in CustomAction1.dll
Loaded dependent assembly: C:\Program Files (x86)\WiX Toolset v3.11\SDK\Microsoft.Deployment.WindowsInstaller.dll
CustomAction1=CustomAction1!CustomAction1.CustomActions.CustomAction1
Searching for an embedded UI class in CustomAction1.dll
Modifying SfxCA.dll stub
Copying file version info from E:\Testing\CA\obj\x86\Debug\CustomAction1.dll to E:\Testing\CA\obj\x86\Debug\CustomAction1.CA.dll
Packaging files
CustomAction1.dll
Microsoft.Deployment.WindowsInstaller.dll
CustomAction1.pdb
CustomAction.config
MakeSfxCA finished: E:\Testing\CA\obj\x86\Debug\CustomAction1.CA.dll
CustomAction1 -> E:\Testing\CA\obj\x86\Debug\CustomAction1.dll
MakeSfxCA.exe: For the record, the building of the CustomAction1.CA.dll file involves zipping up the managed code dll version CustomAction1.dll and also its dependencies in a Win32 binary. The file MakeSfxCA.exe (Firegiant's documentation page) is a DTF file (Deployment Tools Foundation). More details here.
DTF.chm: There is more documentation in the DTF.chm help file located in the WiX installation directory's "doc" sub-folder (normally: "%ProgramFiles(x86)%\WiX Toolset v3.11\doc\DTF.chm") - search for MakeSfxCA.exe.
I have a C# application with a wix setup. I have customized the ExitDialog with 2 checkboxes (following this), on used to run my application, the other one to run an optional install (for uEye camera).
The first checkbox is :
<!-- Set checkbox for launch my application -->
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch $(var.product)"/>
<CustomAction Id="SetExecVR3" Property="WixShellExecTarget" Value="[#MyApplication.exe]"/>
<CustomAction Id="DoExec" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" Return ="ignore"/>
The second :
<!-- Set checkbox for launch install uEye -->
<Property Id="WIXUI_EXITDIALOGUEYECHECKBOXTEXT" Value="Launch install uEye"/>
<CustomAction Id="SetExecUEye" Property="WixShellExecTarget" Value="./Resources/uEye64_47100_WHQL.exe"/>
And there is my Wix UI (this helped me):
<UI>
<UIRef Id="WixUI_Custom"/>
<Publish Dialog="MyExitDialog"
Control="Finish"
Event="DoAction"
Value="SetExecVR3">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
<Publish Dialog="MyExitDialog"
Control="Finish"
Event="DoAction"
Value="DoExec">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
<Publish Dialog="MyExitDialog"
Control="Finish"
Event="DoAction"
Value="SetExecUEye">WIXUI_EXITDIALOGUEYECHECKBOX = 1 and NOT Installed</Publish>
<Publish Dialog="MyExitDialog"
Control="Finish"
Event="DoAction"
Value="DoExec">WIXUI_EXITDIALOGUEYECHECKBOX = 1 and NOT Installed</Publish>
</UI>
There is my Setup :
The check bock for MyApplication.exe works good, the other one didn't. The generation didn't copy uEye64_47100_WHQL.exe in local directory and when I check the option nothing append.
I'm beginning with WiX, what did I miss ?
Edit:
Now I have a component with the .exe. The file is copied but I'm unable to run it. In log with msiexec I Have :
MSI (c) (C4:B8) [12:45:35:109]: Note: 1: 2228 2: 3: Error 4: SELECT Message FROM Error WHERE Error = 1721
Info 1721.There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: SetExecUEye, location: C:\uEye64_47100_WHQL.exe, command:
I don't understand this error, and I don't know why the file is in C:\ (I used SourceDir to locate it)
Edit2:
The component created:
<Component Id="uEye64_47100_WHQLexe" Directory="TARGETDIR" Guid="{1BD47632-42D5-4C56-B207-1E6B1005488C}">
<File Id="uEye64_47100_WHQLexe" Source="./Resources/uEye64_47100_WHQL.exe" KeyPath="yes" Checksum="yes" Compressed="no" Vital="no"/>
</Component>
And the directory:
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="$(var.compagny)"/>
</Directory>
<Directory Id="DesktopFolder" SourceName="Desktop"/>
<Directory Id="ProgramFilesFolder">
<Directory Id="PRODUCTFOLDER" Name="$(var.compagny)">
<Directory Id="INSTALLFOLDER" Name="$(var.product)">
<Directory Id="fr" Name="fr"/>
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
How can define uEye64_47100_WHQLexe to be copied only in my release folder ? TARGETDIR is set to C:\
You must create another component for your uEye64_47100_WHQL.exe as for your main .exe, if you want to copy it and run on installation. If it is located only in Resource folder, it can be referenced only at the time of compilation as File source, because it is not added to installer itself. So create component like
<Component Id="uEye64_47100_WHQLexe" Directory="APPLICATIONFOLDER" Guid="*">
<File Id="uEye64_47100_WHQLexe" Source="./Resources/uEye64_47100_WHQL.exe" KeyPath="yes" Checksum="yes" />
</Component>
and then you can use it in custom action using WixShellExec like for MyApplication.exe. But I would advise to define custom action for both files like
<CustomAction Id="RunuEye64_47100_WHQLexe" FileKey="uEye64_47100_WHQLexe" ExeCommand="" Return="ignore" Impersonate="yes" />
because it can be used directly without messing with WixShellExecTarget property ;-)
Publish part of UI will than be
Event="DoAction"
Value="RunuEye64_47100_WHQLexe">
I have following Wix Code that is supposed to send the value of property to Custom Action Written in C#. Basically what I want is when the MSI is installed, I want to write the path of Folder where Wix installed the program in text file. I referred to this site and created code accordingly, but my Custom Action isn't working.
Following is my Wix File:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupInstallFolder" Language="1033" Version="1.0.0.0" Manufacturer="LP" UpgradeCode="9e10a7d8-4ffb-493c-8318-c44ba4bc0c4c">
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="SetupInstallFolder" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="SetupInstallFolder" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="SomeRandomEXE">
<File Source ="G:\SarVaGYa\myworkspace\LatestLpa\lpa\lpa_c\here\src\lpa\Release\lpa.exe" />
</Component>
</ComponentGroup>
<Binary Id="SetupCA2" src="G:\visual studio stuffs\SetupCAInstallFolder\SetupCAInstallFolder\bin\Release\SetupCAInstallFolder.CA.dll"/>
<CustomAction Id="INSTALLFOLDERFINDER" Execute="immediate" Property="INSTALLEDPATH" Value="[INSTALLFOLDER]" />
<InstallExecuteSequence>
<Custom Action="INSTALLFOLDERFINDER" Sequence="2"></Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>
I have also given my C# code that is supposed to get the value and write it in file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
namespace SetupCAInstallFolder
{
public class CustomActions
{
[CustomAction]
public static ActionResult InstallFolderFinder(Session session)
{
session.Log("Here is the SetupCAInstallFolder");
string path = session["INSTALLEDPATH"];
session.Log("Installed Path is " + path);
System.IO.File.WriteAllText("F:\\pathgenerated.txt", path);
//System.IO.File.WriteAllText(path + "installed.txt", "sdkasdkasdlkasdk");
return ActionResult.Success;
}
}
}
The Wix file compiles and gives MSI that doesn't get the value of INSTALLEDPATH . If I add DllEntry="InstallFolderFinder" in CustomAction tag, it fails with error The CustomAction/#DllEntry attribute cannot coexist with a previously specified attribute on this element. The CustomAction element may only have one of the following target attributes specified at a time: DllEntry, Error, ExeCommand, JScriptCall, Script, Value, or VBScriptCall
How do I pass the value of INSTALLEDPATH to C# Custom Action?
I have fixed the issue after stumbling around some more site. I have added the code in gist. The Wix File Code is here and the C# Custom action code is here . Basically I added
two Custom tags in InstallExexuteSequeunce that first loads the dllentry and the second passes the parameter to Custom Action Written in C#.
MSI is determining the paths between the actions CostInitialize and CostFinalize.
Using hardcoded sequences is very rarely to recommend, and maybe you have chosen the wrong sequence number for this.
Try:
<InstallExecuteSequence>
<Custom Action='INSTALLFOLDERFINDER' After='CostFinalize'></Custom>
</InstallExecuteSequence>
I hope you are sure, INSTALLDEDPATH is your the correct property. The MSI base property for paths is `TARGETDIR.
If it still doesn't work, try a custom action type 51 with setting a property MYDUMMY on the value of [INSTALLEDPATH]. Now you can see, if at least the value is correctly written in a standard custom action not programmed.
I was looking for answers but not found one, so I'm posting this.
I have build a windows service, which I have to install on customer server. I have successfully tested it localy (installed with installutil.exe through Visual Studio cmd prompt). Now, I'm trying to do installation using Wix Toolset 3.7. The service installs and starts ok, but I don't get anything from it. It doesn't do anything, no calls to database (as it should) and nothing. It is there, it lives, but it doesn't do sqat. A bit lazy service it is.
I can not figure ot what am I doing wrong. Here is my Wix code:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="NeoSrvKrka" Language="1033" Version="1.0.0.0" Manufacturer="Neolab d.o.o." UpgradeCode="04f2a5be-92e1-4c53-8e45-7ae2740a9098">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Manufacturer="Neolab d.o.o." />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="NeoSrvKrka" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="NeoSrvKrka" />
</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 Id="NeoSrvExe" Name="SalusWindowsService.exe" Source="..\SalusWindowsService\bin\Debug\SalusWindowsService.exe" Vital="yes" KeyPath="yes"></File>
<File Id="NeoSrvExeConfig" Name="SalusWindowsService.exe.config" Source="..\SalusWindowsService\bin\Debug\SalusWindowsService.exe.config" Vital="yes" KeyPath="no"></File>
<ServiceInstall
Id="ServiceInstaller"
Type="ownProcess"
Vital="yes"
Name="SalusKrkaService"
DisplayName="Salus Krka Service"
Description="Windows service za prenos narocil iz Salusa v Krko"
Start="auto"
Account="LocalSystem"
ErrorControl="ignore"
Interactive="no"
>
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="SalusKrkaService" Wait="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
In my app I have also created ProjectInstaller file and all the magic around it (maybe I shoudln't use this with wix?). Oh, I'm using Visual Studio 2012, .NET 4.5, c#, windows 7 (will be windows server 2012 in production).
I am adding some code that should be executing:
namespace SalusWindowsService
{
public partial class Krka : ServiceBase
{
public Krka()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
Timer timer = new Timer();
timer.Interval = 3000; //5 minut
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Enabled = true;
GC.KeepAlive(timer);
}
catch (Exception exc)
{
//NeoException.Handle(exc);
}
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{ //do something, but it is not doing anything...}}}
I put some breakpoints in the code and even in the Main function of service (which just initializes base class), but I never get there.
(Answered by the OP in the question. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
It turned out i wasn't adding enough dlls to installation.
So, nothing wrong with Wix or the code. As a reminder for anyone out there doing the same mistake. You have to add ALL dll-s under wix . So, if you have multiple ddl-s in your Debug/bin folder of application, you have to add all (or at least many) of them into . That means, many tags inside one tag. Be careful, only one tag has KeyPath attribute set to "yes".
I am trying to create a service using VS2012 express that will be installed with WiX. This is done without the templates provided in the full version of VS. I had my class derive from ServiceBase. I assumed (perhaps incorrectly) that if the program was installed using WiX that a class derived from ServiceInstaller was not necessary. When I run the MSI that is created by WiX, no errors are flagged, but no new service shows up.
I have Google searched for an answer, but didn't find an example of the miniumum C# code needed to create a service. Links to a good tutorial or pointing out the area where either the C# or WiX code is lacking would be appreciated.
The code for the template service is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
namespace WixInstalledServiceTeamplate
{
class BasicService : ServiceBase
{
static void Main(string[] args)
{
}
public BasicService()
{
this.AutoLog = true;
this.ServiceName = "MY Service Template";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
}
Wix Code:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="786F7069-9C7F-4E15-A721-6B3B4D300FD9" Name="WixEditText" Language="1033" Version="0.0.0.1" Manufacturer="3M Automated Inpsection and Measurement" UpgradeCode="31956530-98A2-4C83-B3A9-5FB6B7A7AE07">
<Package Description="Test file in a Product" Comments="Simple test" InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="RELEASE" Name="Release">
<Component Id="WIXINSTALLEDSERVICETEAMPLATE.EXE" DiskId="1" Guid="B0AEF920-4EF0-478C-9B5A-0B13F23F7E73">
<File Id="WIXINSTALLEDSERVICETEAMPLATE.EXE" Name="WixInstalledServiceTeamplate.exe" Source="bin\Release\WixInstalledServiceTeamplate.exe" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="Complete" Title="Install Everything" Level="1" Display="expand" ConfigurableDirectory="TARGETDIR">
<Component Id="MYServiceTemplate" Guid="1BD8DA93-86A6-4DC4-8CE9-B59525DDFB89" Directory="TARGETDIR">
<ServiceInstall Name="myservicetemplate" Type="ownProcess" Start="demand" ErrorControl="normal" Account="LOCAL SYSTEM" Description="test service install with wix" DisplayName="MY Service Template" Id="serviceInstall">
</ServiceInstall>
</Component>
<ComponentRef Id="WIXINSTALLEDSERVICETEAMPLATE.EXE" />
</Feature>
<UI />
<UIRef Id="WixUI_Minimal" />
</Product>
</Wix>
Your assumption that only ServiceBase is needed is correct. However you only need 1 component not 2 components in WiX. The ServiceInstall doesn't reference a file, it implicitly applies to the keyfile of the parent component.
If you need the ability to install the EXE and a console app and/or a service (variation point) that gets more complicated. The easiest is to factor into a DLL and create 2 EXE's with a total of 3 components.