aboutsummaryrefslogtreecommitdiff
path: root/src/samples
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-31 11:56:14 -0700
committerRob Mensching <rob@firegiant.com>2022-03-31 18:01:06 -0700
commit47582b162368e8edf7a3b11c13b8e9dabc5f0a26 (patch)
tree2c4063eff325684bed39de0edacd7866a257ae02 /src/samples
parent167296c42497c4e95f0d5d71168542d747655981 (diff)
downloadwix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.gz
wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.bz2
wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.zip
Provide managed CA and Embedded UI DTF libraries via NuGet
Lots of refactoring to bring the SFX tooling back into the 'dtf' layer since they are (in the end) tightly coupled to some DTF assemblies. Also refactored the DTF tests into their own folder and added a couple integration tests to build using the new CA/UI NuGet package. Closes wixtoolset/issues#6080
Diffstat (limited to 'src/samples')
-rw-r--r--src/samples/Dtf/EmbeddedUI/AssemblyInfo.cs5
-rw-r--r--src/samples/Dtf/EmbeddedUI/EmbeddedUI.csproj56
-rw-r--r--src/samples/Dtf/EmbeddedUI/InstallProgressCounter.cs176
-rw-r--r--src/samples/Dtf/EmbeddedUI/SampleEmbeddedUI.cs132
-rw-r--r--src/samples/Dtf/EmbeddedUI/SetupWizard.xaml17
-rw-r--r--src/samples/Dtf/EmbeddedUI/SetupWizard.xaml.cs111
-rw-r--r--src/samples/Dtf/ManagedCA/AssemblyInfo.cs5
-rw-r--r--src/samples/Dtf/ManagedCA/ManagedCA.csproj33
-rw-r--r--src/samples/Dtf/ManagedCA/SampleCAs.cs127
-rw-r--r--src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.cs711
-rw-r--r--src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.csproj27
-rw-r--r--src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.exe.manifest20
-rw-r--r--src/samples/Dtf/Tools/MakeSfxCA/app.config10
-rw-r--r--src/samples/Dtf/Tools/SfxCA/ClrHost.cpp262
-rw-r--r--src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp281
-rw-r--r--src/samples/Dtf/Tools/SfxCA/EntryPoints.def140
-rw-r--r--src/samples/Dtf/Tools/SfxCA/EntryPoints.h162
-rw-r--r--src/samples/Dtf/Tools/SfxCA/Extract.cpp282
-rw-r--r--src/samples/Dtf/Tools/SfxCA/RemoteMsi.cpp629
-rw-r--r--src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h898
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxCA.cpp363
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxCA.rc10
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj67
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj.filters62
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxUtil.cpp209
-rw-r--r--src/samples/Dtf/Tools/SfxCA/SfxUtil.h31
-rw-r--r--src/samples/Dtf/Tools/SfxCA/packages.config4
-rw-r--r--src/samples/Dtf/Tools/SfxCA/precomp.cpp3
-rw-r--r--src/samples/Dtf/Tools/SfxCA/precomp.h18
-rw-r--r--src/samples/Dtf/Tools/Tools.proj15
30 files changed, 0 insertions, 4866 deletions
diff --git a/src/samples/Dtf/EmbeddedUI/AssemblyInfo.cs b/src/samples/Dtf/EmbeddedUI/AssemblyInfo.cs
deleted file mode 100644
index 7a2fa039..00000000
--- a/src/samples/Dtf/EmbeddedUI/AssemblyInfo.cs
+++ /dev/null
@@ -1,5 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System.Reflection;
4
5[assembly: AssemblyDescription("Sample managed embedded external UI")]
diff --git a/src/samples/Dtf/EmbeddedUI/EmbeddedUI.csproj b/src/samples/Dtf/EmbeddedUI/EmbeddedUI.csproj
deleted file mode 100644
index e4c52a26..00000000
--- a/src/samples/Dtf/EmbeddedUI/EmbeddedUI.csproj
+++ /dev/null
@@ -1,56 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <PropertyGroup>
5 <ProjectGuid>{864B8C50-7895-4485-AC89-900D86FD8C0D}</ProjectGuid>
6 <OutputType>Library</OutputType>
7 <RootNamespace>WixToolset.Dtf.Samples.EmbeddedUI</RootNamespace>
8 <AssemblyName>WixToolset.Dtf.Samples.EmbeddedUI</AssemblyName>
9 <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
10 <FileAlignment>512</FileAlignment>
11 </PropertyGroup>
12 <ItemGroup>
13 <Compile Include="AssemblyInfo.cs" />
14 <Compile Include="InstallProgressCounter.cs" />
15 <Compile Include="SampleEmbeddedUI.cs" />
16 <Compile Include="SetupWizard.xaml.cs">
17 <DependentUpon>SetupWizard.xaml</DependentUpon>
18 </Compile>
19 </ItemGroup>
20 <ItemGroup>
21 <Page Include="SetupWizard.xaml">
22 <Generator>MSBuild:Compile</Generator>
23 <SubType>Designer</SubType>
24 </Page>
25 </ItemGroup>
26 <ItemGroup>
27 <Reference Include="PresentationCore">
28 <RequiredTargetFramework>3.0</RequiredTargetFramework>
29 </Reference>
30 <Reference Include="PresentationFramework">
31 <RequiredTargetFramework>3.0</RequiredTargetFramework>
32 </Reference>
33 <Reference Include="System" />
34 <Reference Include="System.Core">
35 <RequiredTargetFramework>3.5</RequiredTargetFramework>
36 </Reference>
37 <Reference Include="System.Xml" />
38 <Reference Include="WindowsBase">
39 <RequiredTargetFramework>3.0</RequiredTargetFramework>
40 </Reference>
41 </ItemGroup>
42 <ItemGroup>
43 <ProjectReference Include="..\..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj">
44 <Project>{24121677-0ed0-41b5-833f-1b9a18e87bf4}</Project>
45 <Name>WixToolset.Dtf.WindowsInstaller</Name>
46 </ProjectReference>
47 </ItemGroup>
48
49 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
50<!--
51 <PropertyGroup>
52 <PostBuildEvent>"$(TargetDir)..\x86\MakeSfxCA.exe" "$(TargetPath)" "$(TargetDir)SfxCA.dll" "$(IntermediateOutputPath)$(TargetFileName)" "$(TargetDir)WixToolset.Dtf.WindowsInstaller.dll"</PostBuildEvent>
53 </PropertyGroup>
54-->
55
56</Project>
diff --git a/src/samples/Dtf/EmbeddedUI/InstallProgressCounter.cs b/src/samples/Dtf/EmbeddedUI/InstallProgressCounter.cs
deleted file mode 100644
index df77e106..00000000
--- a/src/samples/Dtf/EmbeddedUI/InstallProgressCounter.cs
+++ /dev/null
@@ -1,176 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Samples.EmbeddedUI
4{
5 using System;
6 using WixToolset.Dtf.WindowsInstaller;
7
8 /// <summary>
9 /// Tracks MSI progress messages and converts them to usable progress.
10 /// </summary>
11 public class InstallProgressCounter
12 {
13 private int total;
14 private int completed;
15 private int step;
16 private bool moveForward;
17 private bool enableActionData;
18 private int progressPhase;
19 private double scriptPhaseWeight;
20
21 public InstallProgressCounter() : this(0.3)
22 {
23 }
24
25 public InstallProgressCounter(double scriptPhaseWeight)
26 {
27 if (!(0 <= scriptPhaseWeight && scriptPhaseWeight <= 1))
28 {
29 throw new ArgumentOutOfRangeException("scriptPhaseWeight");
30 }
31
32 this.scriptPhaseWeight = scriptPhaseWeight;
33 }
34
35 /// <summary>
36 /// Gets a number between 0 and 1 that indicates the overall installation progress.
37 /// </summary>
38 public double Progress { get; private set; }
39
40 public void ProcessMessage(InstallMessage messageType, Record messageRecord)
41 {
42 // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#.
43
44 switch (messageType)
45 {
46 case InstallMessage.ActionStart:
47 if (this.enableActionData)
48 {
49 this.enableActionData = false;
50 }
51 break;
52
53 case InstallMessage.ActionData:
54 if (this.enableActionData)
55 {
56 if (this.moveForward)
57 {
58 this.completed += this.step;
59 }
60 else
61 {
62 this.completed -= this.step;
63 }
64
65 this.UpdateProgress();
66 }
67 break;
68
69 case InstallMessage.Progress:
70 this.ProcessProgressMessage(messageRecord);
71 break;
72 }
73 }
74
75 private void ProcessProgressMessage(Record progressRecord)
76 {
77 // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#.
78
79 if (progressRecord == null || progressRecord.FieldCount == 0)
80 {
81 return;
82 }
83
84 int fieldCount = progressRecord.FieldCount;
85 int progressType = progressRecord.GetInteger(1);
86 string progressTypeString = String.Empty;
87 switch (progressType)
88 {
89 case 0: // Master progress reset
90 if (fieldCount < 4)
91 {
92 return;
93 }
94
95 this.progressPhase++;
96
97 this.total = progressRecord.GetInteger(2);
98 if (this.progressPhase == 1)
99 {
100 // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase
101 // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress
102 // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this
103 // "close" and deal with the rest.
104 this.total += 50;
105 }
106
107 this.moveForward = (progressRecord.GetInteger(3) == 0);
108 this.completed = (this.moveForward ? 0 : this.total); // if forward start at 0, if backwards start at max
109 this.enableActionData = false;
110
111 this.UpdateProgress();
112 break;
113
114 case 1: // Action info
115 if (fieldCount < 3)
116 {
117 return;
118 }
119
120 if (progressRecord.GetInteger(3) == 0)
121 {
122 this.enableActionData = false;
123 }
124 else
125 {
126 this.enableActionData = true;
127 this.step = progressRecord.GetInteger(2);
128 }
129 break;
130
131 case 2: // Progress report
132 if (fieldCount < 2 || this.total == 0 || this.progressPhase == 0)
133 {
134 return;
135 }
136
137 if (this.moveForward)
138 {
139 this.completed += progressRecord.GetInteger(2);
140 }
141 else
142 {
143 this.completed -= progressRecord.GetInteger(2);
144 }
145
146 this.UpdateProgress();
147 break;
148
149 case 3: // Progress total addition
150 this.total += progressRecord.GetInteger(2);
151 break;
152 }
153 }
154
155 private void UpdateProgress()
156 {
157 if (this.progressPhase < 1 || this.total == 0)
158 {
159 this.Progress = 0;
160 }
161 else if (this.progressPhase == 1)
162 {
163 this.Progress = this.scriptPhaseWeight * Math.Min(this.completed, this.total) / this.total;
164 }
165 else if (this.progressPhase == 2)
166 {
167 this.Progress = this.scriptPhaseWeight +
168 (1 - this.scriptPhaseWeight) * Math.Min(this.completed, this.total) / this.total;
169 }
170 else
171 {
172 this.Progress = 1;
173 }
174 }
175 }
176}
diff --git a/src/samples/Dtf/EmbeddedUI/SampleEmbeddedUI.cs b/src/samples/Dtf/EmbeddedUI/SampleEmbeddedUI.cs
deleted file mode 100644
index 9b26bef5..00000000
--- a/src/samples/Dtf/EmbeddedUI/SampleEmbeddedUI.cs
+++ /dev/null
@@ -1,132 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Samples.EmbeddedUI
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Configuration;
8 using System.Threading;
9 using System.Windows;
10 using System.Windows.Threading;
11 using WixToolset.Dtf.WindowsInstaller;
12 using Application = System.Windows.Application;
13
14 public class SampleEmbeddedUI : IEmbeddedUI
15 {
16 private Thread appThread;
17 private Application app;
18 private SetupWizard setupWizard;
19 private ManualResetEvent installStartEvent;
20 private ManualResetEvent installExitEvent;
21
22 /// <summary>
23 /// Initializes the embedded UI.
24 /// </summary>
25 /// <param name="session">Handle to the installer which can be used to get and set properties.
26 /// The handle is only valid for the duration of this method call.</param>
27 /// <param name="resourcePath">Path to the directory that contains all the files from the MsiEmbeddedUI table.</param>
28 /// <param name="internalUILevel">On entry, contains the current UI level for the installation. After this
29 /// method returns, the installer resets the UI level to the returned value of this parameter.</param>
30 /// <returns>True if the embedded UI was successfully initialized; false if the installation
31 /// should continue without the embedded UI.</returns>
32 /// <exception cref="InstallCanceledException">The installation was canceled by the user.</exception>
33 /// <exception cref="InstallerException">The embedded UI failed to initialize and
34 /// causes the installation to fail.</exception>
35 public bool Initialize(Session session, string resourcePath, ref InstallUIOptions internalUILevel)
36 {
37 if (session != null)
38 {
39 if ((internalUILevel & InstallUIOptions.Full) != InstallUIOptions.Full)
40 {
41 // Don't show custom UI when the UI level is set to basic.
42 return false;
43
44 // An embedded UI could display an alternate dialog sequence for reduced or
45 // basic modes, but it's not implemented here. We'll just fall back to the
46 // built-in MSI basic UI.
47 }
48
49 if (String.Equals(session["REMOVE"], "All", StringComparison.OrdinalIgnoreCase))
50 {
51 // Don't show custom UI when uninstalling.
52 return false;
53
54 // An embedded UI could display an uninstall wizard, it's just not imlemented here.
55 }
56 }
57
58 // Start the setup wizard on a separate thread.
59 this.installStartEvent = new ManualResetEvent(false);
60 this.installExitEvent = new ManualResetEvent(false);
61 this.appThread = new Thread(this.Run);
62 this.appThread.SetApartmentState(ApartmentState.STA);
63 this.appThread.Start();
64
65 // Wait for the setup wizard to either kickoff the install or prematurely exit.
66 int waitResult = WaitHandle.WaitAny(new WaitHandle[] { this.installStartEvent, this.installExitEvent });
67 if (waitResult == 1)
68 {
69 // The setup wizard set the exit event instead of the start event. Cancel the installation.
70 throw new InstallCanceledException();
71 }
72 else
73 {
74 // Start the installation with a silenced internal UI.
75 // This "embedded external UI" will handle message types except for source resolution.
76 internalUILevel = InstallUIOptions.NoChange | InstallUIOptions.SourceResolutionOnly;
77 return true;
78 }
79 }
80
81 /// <summary>
82 /// Processes information and progress messages sent to the user interface.
83 /// </summary>
84 /// <param name="messageType">Message type.</param>
85 /// <param name="messageRecord">Record that contains message data.</param>
86 /// <param name="buttons">Message box buttons.</param>
87 /// <param name="icon">Message box icon.</param>
88 /// <param name="defaultButton">Message box default button.</param>
89 /// <returns>Result of processing the message.</returns>
90 public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord,
91 MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton)
92 {
93 // Synchronously send the message to the setup wizard window on its thread.
94 object result = this.setupWizard.Dispatcher.Invoke(DispatcherPriority.Send,
95 new Func<MessageResult>(delegate()
96 {
97 return this.setupWizard.ProcessMessage(messageType, messageRecord, buttons, icon, defaultButton);
98 }));
99 return (MessageResult) result;
100 }
101
102 /// <summary>
103 /// Shuts down the embedded UI at the end of the installation.
104 /// </summary>
105 /// <remarks>
106 /// If the installation was canceled during initialization, this method will not be called.
107 /// If the installation was canceled or failed at any later point, this method will be called at the end.
108 /// </remarks>
109 public void Shutdown()
110 {
111 // Wait for the user to exit the setup wizard.
112 this.setupWizard.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
113 new Action(delegate()
114 {
115 this.setupWizard.EnableExit();
116 }));
117 this.appThread.Join();
118 }
119
120 /// <summary>
121 /// Creates the setup wizard and runs the application thread.
122 /// </summary>
123 private void Run()
124 {
125 this.app = new Application();
126 this.setupWizard = new SetupWizard(this.installStartEvent);
127 this.setupWizard.InitializeComponent();
128 this.app.Run(this.setupWizard);
129 this.installExitEvent.Set();
130 }
131 }
132}
diff --git a/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml b/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml
deleted file mode 100644
index a43059e8..00000000
--- a/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml
+++ /dev/null
@@ -1,17 +0,0 @@
1
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Window x:Class="WixToolset.Dtf.Samples.EmbeddedUI.SetupWizard"
6 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
7 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
8 Title="Sample Embedded UI" Height="400" Width="540" Visibility="Visible">
9 <Grid>
10 <TextBox Margin="8,8,8,63" Name="messagesTextBox" IsReadOnly="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" FontFamily="Lucida Console" FontSize="10" />
11 <Button Height="23" HorizontalAlignment="Right" Name="installButton" VerticalAlignment="Bottom" Width="75" Click="installButton_Click" Margin="0,0,91,8">Install</Button>
12 <Button Height="23" HorizontalAlignment="Right" Name="exitButton" VerticalAlignment="Bottom" Width="75" Visibility="Hidden" Click="exitButton_Click" Margin="0,0,8,8">Exit</Button>
13 <Button Height="23" Margin="0,0,8,8" Name="cancelButton" VerticalAlignment="Bottom" Width="75" HorizontalAlignment="Right" Click="cancelButton_Click">Cancel</Button>
14 <ProgressBar Height="16" Margin="8,0,8,39" Name="progressBar" VerticalAlignment="Bottom" Visibility="Hidden" IsIndeterminate="False" />
15 <Label Height="28" HorizontalAlignment="Left" Margin="8,0,0,4.48" Name="progressLabel" VerticalAlignment="Bottom" Width="120" Visibility="Hidden">0%</Label>
16 </Grid>
17</Window>
diff --git a/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml.cs b/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml.cs
deleted file mode 100644
index b25b8a9e..00000000
--- a/src/samples/Dtf/EmbeddedUI/SetupWizard.xaml.cs
+++ /dev/null
@@ -1,111 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Samples.EmbeddedUI
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Text;
9 using System.Threading;
10 using System.Windows;
11 using System.Windows.Controls;
12 using System.Windows.Data;
13 using System.Windows.Documents;
14 using System.Windows.Input;
15 using System.Windows.Media;
16 using System.Windows.Media.Imaging;
17 using System.Windows.Navigation;
18 using System.Windows.Shapes;
19 using WixToolset.Dtf.WindowsInstaller;
20
21 /// <summary>
22 /// Interaction logic for SetupWizard.xaml
23 /// </summary>
24 public partial class SetupWizard : Window
25 {
26 private ManualResetEvent installStartEvent;
27 private InstallProgressCounter progressCounter;
28 private bool canceled;
29
30 public SetupWizard(ManualResetEvent installStartEvent)
31 {
32 this.installStartEvent = installStartEvent;
33 this.progressCounter = new InstallProgressCounter(0.5);
34 }
35
36 public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord,
37 MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton)
38 {
39 try
40 {
41 this.progressCounter.ProcessMessage(messageType, messageRecord);
42 this.progressBar.Value = this.progressBar.Minimum +
43 this.progressCounter.Progress * (this.progressBar.Maximum - this.progressBar.Minimum);
44 this.progressLabel.Content = "" + (int) Math.Round(100 * this.progressCounter.Progress) + "%";
45
46 switch (messageType)
47 {
48 case InstallMessage.Error:
49 case InstallMessage.Warning:
50 case InstallMessage.Info:
51 string message = String.Format("{0}: {1}", messageType, messageRecord);
52 this.LogMessage(message);
53 break;
54 }
55
56 if (this.canceled)
57 {
58 this.canceled = false;
59 return MessageResult.Cancel;
60 }
61 }
62 catch (Exception ex)
63 {
64 this.LogMessage(ex.ToString());
65 this.LogMessage(ex.StackTrace);
66 }
67
68 return MessageResult.OK;
69 }
70
71 private void LogMessage(string message)
72 {
73 this.messagesTextBox.Text += Environment.NewLine + message;
74 this.messagesTextBox.ScrollToEnd();
75 }
76
77 internal void EnableExit()
78 {
79 this.progressBar.Visibility = Visibility.Hidden;
80 this.progressLabel.Visibility = Visibility.Hidden;
81 this.cancelButton.Visibility = Visibility.Hidden;
82 this.exitButton.Visibility = Visibility.Visible;
83 }
84
85 private void installButton_Click(object sender, RoutedEventArgs e)
86 {
87 this.installButton.Visibility = Visibility.Hidden;
88 this.progressBar.Visibility = Visibility.Visible;
89 this.progressLabel.Visibility = Visibility.Visible;
90 this.installStartEvent.Set();
91 }
92
93 private void exitButton_Click(object sender, RoutedEventArgs e)
94 {
95 this.Close();
96 }
97
98 private void cancelButton_Click(object sender, RoutedEventArgs e)
99 {
100 if (this.installButton.Visibility == Visibility.Visible)
101 {
102 this.Close();
103 }
104 else
105 {
106 this.canceled = true;
107 this.cancelButton.IsEnabled = false;
108 }
109 }
110 }
111}
diff --git a/src/samples/Dtf/ManagedCA/AssemblyInfo.cs b/src/samples/Dtf/ManagedCA/AssemblyInfo.cs
deleted file mode 100644
index 75be36b2..00000000
--- a/src/samples/Dtf/ManagedCA/AssemblyInfo.cs
+++ /dev/null
@@ -1,5 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System.Reflection;
4
5[assembly: AssemblyDescription("Sample managed custom actions")]
diff --git a/src/samples/Dtf/ManagedCA/ManagedCA.csproj b/src/samples/Dtf/ManagedCA/ManagedCA.csproj
deleted file mode 100644
index 7fb32ad4..00000000
--- a/src/samples/Dtf/ManagedCA/ManagedCA.csproj
+++ /dev/null
@@ -1,33 +0,0 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
4 <ProjectGuid>{DB9E5F02-8241-440A-9B60-980EB5B42B13}</ProjectGuid>
5 <OutputType>Library</OutputType>
6 <RootNamespace>WixToolset.Dtf.Samples.ManagedCA</RootNamespace>
7 <AssemblyName>WixToolset.Dtf.Samples.ManagedCA</AssemblyName>
8 <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
9 <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
10 </PropertyGroup>
11 <ItemGroup>
12 <Compile Include="AssemblyInfo.cs" />
13 <Compile Include="SampleCAs.cs" />
14 </ItemGroup>
15 <ItemGroup>
16 <Reference Include="System" />
17 </ItemGroup>
18 <ItemGroup>
19 <ProjectReference Include="..\..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj">
20 <Project>{24121677-0ed0-41b5-833f-1b9a18e87bf4}</Project>
21 <Name>WixToolset.Dtf.WindowsInstaller</Name>
22 </ProjectReference>
23 </ItemGroup>
24
25 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
26
27<!--
28 <PropertyGroup>
29 <PostBuildEvent>"$(TargetDir)..\x86\MakeSfxCA.exe" "$(TargetPath)" "$(TargetDir)SfxCA.dll" "$(IntermediateOutputPath)$(TargetFileName)" WixToolset.Dtf.WindowsInstaller.dll="$(TargetDir)WixToolset.Dtf.WindowsInstaller.dll" testsub\SampleCAs.cs="$(ProjectDir)\SampleCAs.cs"</PostBuildEvent>
30 </PropertyGroup>
31-->
32
33</Project>
diff --git a/src/samples/Dtf/ManagedCA/SampleCAs.cs b/src/samples/Dtf/ManagedCA/SampleCAs.cs
deleted file mode 100644
index 645131c8..00000000
--- a/src/samples/Dtf/ManagedCA/SampleCAs.cs
+++ /dev/null
@@ -1,127 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Samples.ManagedCA
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using WixToolset.Dtf.WindowsInstaller;
9
10 public class SampleCAs
11 {
12 [CustomAction]
13 public static ActionResult SampleCA1(Session session)
14 {
15 using (Record msgRec = new Record(0))
16 {
17 msgRec[0] = "Hello from SampleCA1!" +
18 "\r\nCLR version is v" + Environment.Version;
19 session.Message(InstallMessage.Info, msgRec);
20 session.Message(InstallMessage.User, msgRec);
21 }
22
23 session.Log("Testing summary info...");
24 SummaryInfo summInfo = session.Database.SummaryInfo;
25 session.Log("MSI PackageCode = {0}", summInfo.RevisionNumber);
26 session.Log("MSI ModifyDate = {0}", summInfo.LastSaveTime);
27
28 string testProp = session["SampleCATest"];
29 session.Log("Simple property test: [SampleCATest]={0}.", testProp);
30
31 session.Log("Testing subdirectory extraction...");
32 string testFilePath = "testsub\\SampleCAs.cs";
33 if (!File.Exists(testFilePath))
34 {
35 session.Log("Subdirectory extraction failed. File not found: " + testFilePath);
36 return ActionResult.Failure;
37 }
38 else
39 {
40 session.Log("Found file extracted in subdirectory.");
41 }
42
43 session.Log("Testing record stream extraction...");
44 string tempFile = null;
45 try
46 {
47 tempFile = Path.GetTempFileName();
48 using (View binView = session.Database.OpenView(
49 "SELECT `Binary`.`Data` FROM `Binary`, `CustomAction` " +
50 "WHERE `CustomAction`.`Target` = 'SampleCA1' AND " +
51 "`CustomAction`.`Source` = `Binary`.`Name`"))
52 {
53 binView.Execute();
54 using (Record binRec = binView.Fetch())
55 {
56 binRec.GetStream(1, tempFile);
57 }
58 }
59
60 session.Log("CA binary file size: {0}", new FileInfo(tempFile).Length);
61 string binFileVersion = Installer.GetFileVersion(tempFile);
62 session.Log("CA binary file version: {0}", binFileVersion);
63 }
64 finally
65 {
66 if (tempFile != null && File.Exists(tempFile))
67 {
68 File.Delete(tempFile);
69 }
70 }
71
72 session.Log("Testing record stream reading...");
73 using (View binView2 = session.Database.OpenView("SELECT `Data` FROM `Binary` WHERE `Name` = 'TestData'"))
74 {
75 binView2.Execute();
76 using (Record binRec2 = binView2.Fetch())
77 {
78 Stream stream = binRec2.GetStream("Data");
79 string testData = new StreamReader(stream, System.Text.Encoding.UTF8).ReadToEnd();
80 session.Log("Test data: " + testData);
81 }
82 }
83
84 session.Log("Listing components");
85 using (View compView = session.Database.OpenView(
86 "SELECT `Component` FROM `Component`"))
87 {
88 compView.Execute();
89 foreach (Record compRec in compView)
90 {
91 using (compRec)
92 {
93 session.Log("\t{0}", compRec["Component"]);
94 }
95 }
96 }
97
98 session.Log("Testing the ability to access an external MSI database...");
99 string tempDbFile = Path.GetTempFileName();
100 using (Database tempDb = new Database(tempDbFile, DatabaseOpenMode.CreateDirect))
101 {
102 // Just create an empty database.
103 }
104 using (Database tempDb2 = new Database(tempDbFile))
105 {
106 // See if we can open and query the database.
107 IList<string> tables = tempDb2.ExecuteStringQuery("SELECT `Name` FROM `_Tables`");
108 session.Log("Found " + tables.Count + " tables in the newly created database.");
109 }
110 File.Delete(tempDbFile);
111
112 return ActionResult.Success;
113 }
114
115 [CustomAction("SampleCA2")]
116 public static ActionResult SampleCustomAction2(Session session)
117 {
118 using (Record msgRec = new Record(0))
119 {
120 msgRec[0] = "Hello from SampleCA2!";
121 session.Message(InstallMessage.Info, msgRec);
122 session.Message(InstallMessage.User, msgRec);
123 }
124 return ActionResult.UserExit;
125 }
126 }
127}
diff --git a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.cs b/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.cs
deleted file mode 100644
index 76ff79b3..00000000
--- a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.cs
+++ /dev/null
@@ -1,711 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Tools.MakeSfxCA
4{
5 using System;
6 using System.IO;
7 using System.Collections.Generic;
8 using System.Security;
9 using System.Text;
10 using System.Reflection;
11 using Compression;
12 using Compression.Cab;
13 using Resources;
14 using ResourceCollection = Resources.ResourceCollection;
15
16 /// <summary>
17 /// Command-line tool for building self-extracting custom action packages.
18 /// Appends cabbed CA binaries to SfxCA.dll and fixes up the result's
19 /// entry-points and file version to look like the CA module.
20 /// </summary>
21 public static class MakeSfxCA
22 {
23 private const string REQUIRED_WI_ASSEMBLY = "WixToolset.Dtf.WindowsInstaller.dll";
24
25 private static TextWriter log;
26
27 /// <summary>
28 /// Prints usage text for the tool.
29 /// </summary>
30 /// <param name="w">Console text writer.</param>
31 public static void Usage(TextWriter w)
32 {
33 w.WriteLine("Deployment Tools Foundation custom action packager version {0}",
34 Assembly.GetExecutingAssembly().GetName().Version);
35 w.WriteLine("Copyright (C) .NET Foundation and contributors. All rights reserved.");
36 w.WriteLine();
37 w.WriteLine("Usage: MakeSfxCA <outputca.dll> SfxCA.dll <inputca.dll> [support files ...]");
38 w.WriteLine();
39 w.WriteLine("Makes a self-extracting managed MSI CA or UI DLL package.");
40 w.WriteLine("Support files must include " + MakeSfxCA.REQUIRED_WI_ASSEMBLY);
41 w.WriteLine("Support files optionally include CustomAction.config/EmbeddedUI.config");
42 }
43
44 /// <summary>
45 /// Runs the MakeSfxCA command-line tool.
46 /// </summary>
47 /// <param name="args">Command-line arguments.</param>
48 /// <returns>0 on success, nonzero on failure.</returns>
49 public static int Main(string[] args)
50 {
51 if (args.Length < 3)
52 {
53 Usage(Console.Out);
54 return 1;
55 }
56
57 var output = args[0];
58 var sfxDll = args[1];
59 var inputs = new string[args.Length - 2];
60 Array.Copy(args, 2, inputs, 0, inputs.Length);
61
62 try
63 {
64 Build(output, sfxDll, inputs, Console.Out);
65 return 0;
66 }
67 catch (ArgumentException ex)
68 {
69 Console.Error.WriteLine("Error: Invalid argument: " + ex.Message);
70 return 1;
71 }
72 catch (FileNotFoundException ex)
73 {
74 Console.Error.WriteLine("Error: Cannot find file: " + ex.Message);
75 return 1;
76 }
77 catch (Exception ex)
78 {
79 Console.Error.WriteLine("Error: Unexpected error: " + ex);
80 return 1;
81 }
82 }
83
84 /// <summary>
85 /// Packages up all the inputs to the output location.
86 /// </summary>
87 /// <exception cref="Exception">Various exceptions are thrown
88 /// if things go wrong.</exception>
89 public static void Build(string output, string sfxDll, IList<string> inputs, TextWriter log)
90 {
91 MakeSfxCA.log = log;
92
93 if (string.IsNullOrEmpty(output))
94 {
95 throw new ArgumentNullException("output");
96 }
97
98 if (string.IsNullOrEmpty(sfxDll))
99 {
100 throw new ArgumentNullException("sfxDll");
101 }
102
103 if (inputs == null || inputs.Count == 0)
104 {
105 throw new ArgumentNullException("inputs");
106 }
107
108 if (!File.Exists(sfxDll))
109 {
110 throw new FileNotFoundException(sfxDll);
111 }
112
113 var customActionAssembly = inputs[0];
114 if (!File.Exists(customActionAssembly))
115 {
116 throw new FileNotFoundException(customActionAssembly);
117 }
118
119 inputs = MakeSfxCA.SplitList(inputs);
120
121 var inputsMap = MakeSfxCA.GetPackFileMap(inputs);
122
123 var foundWIAssembly = false;
124 foreach (var input in inputsMap.Keys)
125 {
126 if (string.Compare(input, MakeSfxCA.REQUIRED_WI_ASSEMBLY,
127 StringComparison.OrdinalIgnoreCase) == 0)
128 {
129 foundWIAssembly = true;
130 }
131 }
132
133 if (!foundWIAssembly)
134 {
135 throw new ArgumentException(MakeSfxCA.REQUIRED_WI_ASSEMBLY +
136 " must be included in the list of support files. " +
137 "If using the MSBuild targets, make sure the assembly reference " +
138 "has the Private (Copy Local) flag set.");
139 }
140
141 MakeSfxCA.ResolveDependentAssemblies(inputsMap, Path.GetDirectoryName(customActionAssembly));
142
143 var entryPoints = MakeSfxCA.FindEntryPoints(customActionAssembly);
144 var uiClass = MakeSfxCA.FindEmbeddedUIClass(customActionAssembly);
145
146 if (entryPoints.Count == 0 && uiClass == null)
147 {
148 throw new ArgumentException(
149 "No CA or UI entry points found in module: " + customActionAssembly);
150 }
151 else if (entryPoints.Count > 0 && uiClass != null)
152 {
153 throw new NotSupportedException(
154 "CA and UI entry points cannot be in the same assembly: " + customActionAssembly);
155 }
156
157 var dir = Path.GetDirectoryName(output);
158 if (dir.Length > 0 && !Directory.Exists(dir))
159 {
160 Directory.CreateDirectory(dir);
161 }
162
163 using (Stream outputStream = File.Create(output))
164 {
165 MakeSfxCA.WriteEntryModule(sfxDll, outputStream, entryPoints, uiClass);
166 }
167
168 MakeSfxCA.CopyVersionResource(customActionAssembly, output);
169
170 MakeSfxCA.PackInputFiles(output, inputsMap);
171
172 log.WriteLine("MakeSfxCA finished: " + new FileInfo(output).FullName);
173 }
174
175 /// <summary>
176 /// Splits any list items delimited by semicolons into separate items.
177 /// </summary>
178 /// <param name="list">Read-only input list.</param>
179 /// <returns>New list with resulting split items.</returns>
180 private static IList<string> SplitList(IList<string> list)
181 {
182 var newList = new List<string>(list.Count);
183
184 foreach (var item in list)
185 {
186 if (!string.IsNullOrEmpty(item))
187 {
188 foreach (var splitItem in item.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
189 {
190 newList.Add(splitItem);
191 }
192 }
193 }
194
195 return newList;
196 }
197
198 /// <summary>
199 /// Sets up a reflection-only assembly-resolve-handler to handle loading dependent assemblies during reflection.
200 /// </summary>
201 /// <param name="inputFiles">List of input files which include non-GAC dependent assemblies.</param>
202 /// <param name="inputDir">Directory to auto-locate additional dependent assemblies.</param>
203 /// <remarks>
204 /// Also searches the assembly's directory for unspecified dependent assemblies, and adds them
205 /// to the list of input files if found.
206 /// </remarks>
207 private static void ResolveDependentAssemblies(IDictionary<string, string> inputFiles, string inputDir)
208 {
209 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += delegate(object sender, ResolveEventArgs args)
210 {
211 AssemblyName resolveName = new AssemblyName(args.Name);
212 Assembly assembly = null;
213
214 // First, try to find the assembly in the list of input files.
215 foreach (var inputFile in inputFiles.Values)
216 {
217 var inputName = Path.GetFileNameWithoutExtension(inputFile);
218 var inputExtension = Path.GetExtension(inputFile);
219 if (string.Equals(inputName, resolveName.Name, StringComparison.OrdinalIgnoreCase) &&
220 (string.Equals(inputExtension, ".dll", StringComparison.OrdinalIgnoreCase) ||
221 string.Equals(inputExtension, ".exe", StringComparison.OrdinalIgnoreCase)))
222 {
223 assembly = MakeSfxCA.TryLoadDependentAssembly(inputFile);
224
225 if (assembly != null)
226 {
227 break;
228 }
229 }
230 }
231
232 // Second, try to find the assembly in the input directory.
233 if (assembly == null && inputDir != null)
234 {
235 string assemblyPath = null;
236 if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".dll"))
237 {
238 assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".dll";
239 }
240 else if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".exe"))
241 {
242 assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".exe";
243 }
244
245 if (assemblyPath != null)
246 {
247 assembly = MakeSfxCA.TryLoadDependentAssembly(assemblyPath);
248
249 if (assembly != null)
250 {
251 // Add this detected dependency to the list of files to be packed.
252 inputFiles.Add(Path.GetFileName(assemblyPath), assemblyPath);
253 }
254 }
255 }
256
257 // Third, try to load the assembly from the GAC.
258 if (assembly == null)
259 {
260 try
261 {
262 assembly = Assembly.ReflectionOnlyLoad(args.Name);
263 }
264 catch (FileNotFoundException)
265 {
266 }
267 }
268
269 if (assembly != null)
270 {
271 if (string.Equals(assembly.GetName().ToString(), resolveName.ToString()))
272 {
273 log.WriteLine(" Loaded dependent assembly: " + assembly.Location);
274 return assembly;
275 }
276
277 log.WriteLine(" Warning: Loaded mismatched dependent assembly: " + assembly.Location);
278 log.WriteLine(" Loaded assembly : " + assembly.GetName());
279 log.WriteLine(" Reference assembly: " + resolveName);
280 }
281 else
282 {
283 log.WriteLine(" Error: Dependent assembly not supplied: " + resolveName);
284 }
285
286 return null;
287 };
288 }
289
290 /// <summary>
291 /// Attempts a reflection-only load of a dependent assembly, logging the error if the load fails.
292 /// </summary>
293 /// <param name="assemblyPath">Path of the assembly file to laod.</param>
294 /// <returns>Loaded assembly, or null if the load failed.</returns>
295 private static Assembly TryLoadDependentAssembly(string assemblyPath)
296 {
297 Assembly assembly = null;
298 try
299 {
300 assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
301 }
302 catch (IOException ex)
303 {
304 log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message);
305 }
306 catch (BadImageFormatException ex)
307 {
308 log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message);
309 }
310 catch (SecurityException ex)
311 {
312 log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message);
313 }
314
315 return assembly;
316 }
317
318 /// <summary>
319 /// Searches the types in the input assembly for a type that implements IEmbeddedUI.
320 /// </summary>
321 /// <param name="module"></param>
322 /// <returns></returns>
323 private static string FindEmbeddedUIClass(string module)
324 {
325 log.WriteLine("Searching for an embedded UI class in {0}", Path.GetFileName(module));
326
327 string uiClass = null;
328
329 var assembly = Assembly.ReflectionOnlyLoadFrom(module);
330
331 foreach (var type in assembly.GetExportedTypes())
332 {
333 if (!type.IsAbstract)
334 {
335 foreach (var interfaceType in type.GetInterfaces())
336 {
337 if (interfaceType.FullName == "WixToolset.Dtf.WindowsInstaller.IEmbeddedUI")
338 {
339 if (uiClass == null)
340 {
341 uiClass = assembly.GetName().Name + "!" + type.FullName;
342 }
343 else
344 {
345 throw new ArgumentException("Multiple IEmbeddedUI implementations found.");
346 }
347 }
348 }
349 }
350 }
351
352 return uiClass;
353 }
354
355 /// <summary>
356 /// Reflects on an input CA module to locate custom action entry-points.
357 /// </summary>
358 /// <param name="module">Assembly module with CA entry-points.</param>
359 /// <returns>Mapping from entry-point names to assembly!class.method paths.</returns>
360 private static IDictionary<string, string> FindEntryPoints(string module)
361 {
362 log.WriteLine("Searching for custom action entry points " +
363 "in {0}", Path.GetFileName(module));
364
365 var entryPoints = new Dictionary<string, string>();
366
367 var assembly = Assembly.ReflectionOnlyLoadFrom(module);
368
369 foreach (var type in assembly.GetExportedTypes())
370 {
371 foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
372 {
373 var entryPointName = MakeSfxCA.GetEntryPoint(method);
374 if (entryPointName != null)
375 {
376 var entryPointPath = string.Format(
377 "{0}!{1}.{2}",
378 Path.GetFileNameWithoutExtension(module),
379 type.FullName,
380 method.Name);
381 entryPoints.Add(entryPointName, entryPointPath);
382
383 log.WriteLine(" {0}={1}", entryPointName, entryPointPath);
384 }
385 }
386 }
387
388 return entryPoints;
389 }
390
391 /// <summary>
392 /// Check for a CustomActionAttribute and return the entrypoint name for the method if it is a CA method.
393 /// </summary>
394 /// <param name="method">A public static method.</param>
395 /// <returns>Entrypoint name for the method as specified by the custom action attribute or just the method name,
396 /// or null if the method is not a custom action method.</returns>
397 private static string GetEntryPoint(MethodInfo method)
398 {
399 IList<CustomAttributeData> attributes;
400 try
401 {
402 attributes = CustomAttributeData.GetCustomAttributes(method);
403 }
404 catch (FileLoadException)
405 {
406 // Already logged load failures in the assembly-resolve-handler.
407 return null;
408 }
409
410 foreach (CustomAttributeData attribute in attributes)
411 {
412 if (attribute.ToString().StartsWith(
413 "[WixToolset.Dtf.WindowsInstaller.CustomActionAttribute(",
414 StringComparison.Ordinal))
415 {
416 string entryPointName = null;
417 foreach (var argument in attribute.ConstructorArguments)
418 {
419 // The entry point name is the first positional argument, if specified.
420 entryPointName = (string) argument.Value;
421 break;
422 }
423
424 if (string.IsNullOrEmpty(entryPointName))
425 {
426 entryPointName = method.Name;
427 }
428
429 return entryPointName;
430 }
431 }
432
433 return null;
434 }
435
436 /// <summary>
437 /// Counts the number of template entrypoints in SfxCA.dll.
438 /// </summary>
439 /// <remarks>
440 /// Depending on the requirements, SfxCA.dll might be built with
441 /// more entrypoints than the default.
442 /// </remarks>
443 private static int GetEntryPointSlotCount(byte[] fileBytes, string entryPointFormat)
444 {
445 for (var count = 0; ; count++)
446 {
447 var templateName = string.Format(entryPointFormat, count);
448 var templateAsciiBytes = Encoding.ASCII.GetBytes(templateName);
449
450 var nameOffset = FindBytes(fileBytes, templateAsciiBytes);
451 if (nameOffset < 0)
452 {
453 return count;
454 }
455 }
456 }
457
458 /// <summary>
459 /// Writes a modified version of SfxCA.dll to the output stream,
460 /// with the template entry-points mapped to the CA entry-points.
461 /// </summary>
462 /// <remarks>
463 /// To avoid having to recompile SfxCA.dll for every different set of CAs,
464 /// this method looks for a preset number of template entry-points in the
465 /// binary file and overwrites their entrypoint name and string data with
466 /// CA-specific values.
467 /// </remarks>
468 private static void WriteEntryModule(
469 string sfxDll, Stream outputStream, IDictionary<string, string> entryPoints, string uiClass)
470 {
471 log.WriteLine("Modifying SfxCA.dll stub");
472
473 byte[] fileBytes;
474 using (var readStream = File.OpenRead(sfxDll))
475 {
476 fileBytes = new byte[(int) readStream.Length];
477 readStream.Read(fileBytes, 0, fileBytes.Length);
478 }
479
480 const string ENTRYPOINT_FORMAT = "CustomActionEntryPoint{0:d03}";
481 const int MAX_ENTRYPOINT_NAME = 72;
482 const int MAX_ENTRYPOINT_PATH = 160;
483 //var emptyBytes = new byte[0];
484
485 var slotCount = MakeSfxCA.GetEntryPointSlotCount(fileBytes, ENTRYPOINT_FORMAT);
486
487 if (slotCount == 0)
488 {
489 throw new ArgumentException("Invalid SfxCA.dll file.");
490 }
491
492 if (entryPoints.Count > slotCount)
493 {
494 throw new ArgumentException(string.Format(
495 "The custom action assembly has {0} entrypoints, which is more than the maximum ({1}). " +
496 "Refactor the custom actions or add more entrypoint slots in SfxCA\\EntryPoints.h.",
497 entryPoints.Count, slotCount));
498 }
499
500 var slotSort = new string[slotCount];
501 for (var i = 0; i < slotCount - entryPoints.Count; i++)
502 {
503 slotSort[i] = string.Empty;
504 }
505
506 entryPoints.Keys.CopyTo(slotSort, slotCount - entryPoints.Count);
507 Array.Sort<string>(slotSort, slotCount - entryPoints.Count, entryPoints.Count, StringComparer.Ordinal);
508
509 for (var i = 0; ; i++)
510 {
511 var templateName = string.Format(ENTRYPOINT_FORMAT, i);
512 var templateAsciiBytes = Encoding.ASCII.GetBytes(templateName);
513 var templateUniBytes = Encoding.Unicode.GetBytes(templateName);
514
515 var nameOffset = MakeSfxCA.FindBytes(fileBytes, templateAsciiBytes);
516 if (nameOffset < 0)
517 {
518 break;
519 }
520
521 var pathOffset = MakeSfxCA.FindBytes(fileBytes, templateUniBytes);
522 if (pathOffset < 0)
523 {
524 break;
525 }
526
527 var entryPointName = slotSort[i];
528 var entryPointPath = entryPointName.Length > 0 ?
529 entryPoints[entryPointName] : string.Empty;
530
531 if (entryPointName.Length > MAX_ENTRYPOINT_NAME)
532 {
533 throw new ArgumentException(string.Format(
534 "Entry point name exceeds limit of {0} characters: {1}",
535 MAX_ENTRYPOINT_NAME,
536 entryPointName));
537 }
538
539 if (entryPointPath.Length > MAX_ENTRYPOINT_PATH)
540 {
541 throw new ArgumentException(string.Format(
542 "Entry point path exceeds limit of {0} characters: {1}",
543 MAX_ENTRYPOINT_PATH,
544 entryPointPath));
545 }
546
547 var replaceNameBytes = Encoding.ASCII.GetBytes(entryPointName);
548 var replacePathBytes = Encoding.Unicode.GetBytes(entryPointPath);
549
550 MakeSfxCA.ReplaceBytes(fileBytes, nameOffset, MAX_ENTRYPOINT_NAME, replaceNameBytes);
551 MakeSfxCA.ReplaceBytes(fileBytes, pathOffset, MAX_ENTRYPOINT_PATH * 2, replacePathBytes);
552 }
553
554 if (entryPoints.Count == 0 && uiClass != null)
555 {
556 // Remove the zzz prefix from exported EmbeddedUI entry-points.
557 foreach (var export in new string[] { "InitializeEmbeddedUI", "EmbeddedUIHandler", "ShutdownEmbeddedUI" })
558 {
559 var exportNameBytes = Encoding.ASCII.GetBytes("zzz" + export);
560
561 var exportOffset = MakeSfxCA.FindBytes(fileBytes, exportNameBytes);
562 if (exportOffset < 0)
563 {
564 throw new ArgumentException("Input SfxCA.dll does not contain exported entry-point: " + export);
565 }
566
567 var replaceNameBytes = Encoding.ASCII.GetBytes(export);
568 MakeSfxCA.ReplaceBytes(fileBytes, exportOffset, exportNameBytes.Length, replaceNameBytes);
569 }
570
571 if (uiClass.Length > MAX_ENTRYPOINT_PATH)
572 {
573 throw new ArgumentException(string.Format(
574 "UI class full name exceeds limit of {0} characters: {1}",
575 MAX_ENTRYPOINT_PATH,
576 uiClass));
577 }
578
579 var templateBytes = Encoding.Unicode.GetBytes("InitializeEmbeddedUI_FullClassName");
580 var replaceBytes = Encoding.Unicode.GetBytes(uiClass);
581
582 // Fill in the embedded UI implementor class so the proxy knows which one to load.
583 var replaceOffset = MakeSfxCA.FindBytes(fileBytes, templateBytes);
584 if (replaceOffset >= 0)
585 {
586 MakeSfxCA.ReplaceBytes(fileBytes, replaceOffset, MAX_ENTRYPOINT_PATH * 2, replaceBytes);
587 }
588 }
589
590 outputStream.Write(fileBytes, 0, fileBytes.Length);
591 }
592
593 /// <summary>
594 /// Searches for a sub-array of bytes within a larger array of bytes.
595 /// </summary>
596 private static int FindBytes(byte[] source, byte[] find)
597 {
598 for (var i = 0; i < source.Length; i++)
599 {
600 int j;
601 for (j = 0; j < find.Length; j++)
602 {
603 if (source[i + j] != find[j])
604 {
605 break;
606 }
607 }
608
609 if (j == find.Length)
610 {
611 return i;
612 }
613 }
614
615 return -1;
616 }
617
618 /// <summary>
619 /// Replaces a range of bytes with new bytes, padding any extra part
620 /// of the range with zeroes.
621 /// </summary>
622 private static void ReplaceBytes(
623 byte[] source, int offset, int length, byte[] replace)
624 {
625 for (var i = 0; i < length; i++)
626 {
627 if (i < replace.Length)
628 {
629 source[offset + i] = replace[i];
630 }
631 else
632 {
633 source[offset + i] = 0;
634 }
635 }
636 }
637
638 /// <summary>
639 /// Print the name of one file as it is being packed into the cab.
640 /// </summary>
641 private static void PackProgress(object source, ArchiveProgressEventArgs e)
642 {
643 if (e.ProgressType == ArchiveProgressType.StartFile && log != null)
644 {
645 log.WriteLine(" {0}", e.CurrentFileName);
646 }
647 }
648
649 /// <summary>
650 /// Gets a mapping from filenames as they will be in the cab to filenames
651 /// as they are currently on disk.
652 /// </summary>
653 /// <remarks>
654 /// By default, all files will be placed in the root of the cab. But inputs may
655 /// optionally include an alternate inside-cab file path before an equals sign.
656 /// </remarks>
657 private static IDictionary<string, string> GetPackFileMap(IList<string> inputs)
658 {
659 var fileMap = new Dictionary<string, string>();
660 foreach (var inputFile in inputs)
661 {
662 if (inputFile.IndexOf('=') > 0)
663 {
664 var parse = inputFile.Split('=');
665 if (!fileMap.ContainsKey(parse[0]))
666 {
667 fileMap.Add(parse[0], parse[1]);
668 }
669 }
670 else
671 {
672 var fileName = Path.GetFileName(inputFile);
673 if (!fileMap.ContainsKey(fileName))
674 {
675 fileMap.Add(fileName, inputFile);
676 }
677 }
678 }
679 return fileMap;
680 }
681
682 /// <summary>
683 /// Packs the input files into a cab that is appended to the
684 /// output SfxCA.dll.
685 /// </summary>
686 private static void PackInputFiles(string outputFile, IDictionary<string, string> fileMap)
687 {
688 log.WriteLine("Packaging files");
689
690 var cabInfo = new CabInfo(outputFile);
691 cabInfo.PackFileSet(null, fileMap, CompressionLevel.Max, PackProgress);
692 }
693
694 /// <summary>
695 /// Copies the version resource information from the CA module to
696 /// the CA package. This gives the package the file version and
697 /// description of the CA module, instead of the version and
698 /// description of SfxCA.dll.
699 /// </summary>
700 private static void CopyVersionResource(string sourceFile, string destFile)
701 {
702 log.WriteLine("Copying file version info from {0} to {1}",
703 sourceFile, destFile);
704
705 var rc = new ResourceCollection();
706 rc.Find(sourceFile, ResourceType.Version);
707 rc.Load(sourceFile);
708 rc.Save(destFile);
709 }
710 }
711}
diff --git a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.csproj b/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.csproj
deleted file mode 100644
index 25b2cdb8..00000000
--- a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project Sdk="Microsoft.NET.Sdk">
5 <PropertyGroup>
6 <TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
7 <OutputType>Exe</OutputType>
8 <RootNamespace>WixToolset.Dtf.Tools.MakeSfxCA</RootNamespace>
9 <AssemblyName>MakeSfxCA</AssemblyName>
10 <DebugType>embedded</DebugType>
11 <AppConfig>app.config</AppConfig>
12 <ApplicationManifest>MakeSfxCA.exe.manifest</ApplicationManifest>
13 <RollForward>Major</RollForward>
14 <RuntimeIdentifier>win-x86</RuntimeIdentifier>
15 </PropertyGroup>
16
17 <ItemGroup>
18 <ProjectReference Include="..\..\WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj" />
19 <ProjectReference Include="..\..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj" />
20 <ProjectReference Include="..\..\WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj" />
21 </ItemGroup>
22
23 <ItemGroup>
24 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
25 <PackageReference Include="GitInfo" Version="2.1.2" PrivateAssets="All" />
26 </ItemGroup>
27</Project>
diff --git a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.exe.manifest b/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.exe.manifest
deleted file mode 100644
index 49b074e0..00000000
--- a/src/samples/Dtf/Tools/MakeSfxCA/MakeSfxCA.exe.manifest
+++ /dev/null
@@ -1,20 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
6 <assemblyIdentity name="WixToolset.Dtf.Tools.MakeSfxCA" version="4.0.0.0" processorArchitecture="x86" type="win32"/>
7 <description>WiX Toolset Compiler</description>
8 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
9 <security>
10 <requestedPrivileges>
11 <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
12 </requestedPrivileges>
13 </security>
14 </trustInfo>
15 <application xmlns="urn:schemas-microsoft-com:asm.v3">
16 <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
17 <ws2:longPathAware>true</ws2:longPathAware>
18 </windowsSettings>
19 </application>
20</assembly>
diff --git a/src/samples/Dtf/Tools/MakeSfxCA/app.config b/src/samples/Dtf/Tools/MakeSfxCA/app.config
deleted file mode 100644
index 65d3d6c3..00000000
--- a/src/samples/Dtf/Tools/MakeSfxCA/app.config
+++ /dev/null
@@ -1,10 +0,0 @@
1<?xml version="1.0" encoding="utf-8" ?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<configuration>
6 <runtime>
7 <loadFromRemoteSources enabled="true"/>
8 <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
9 </runtime>
10</configuration>
diff --git a/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp b/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp
deleted file mode 100644
index 1988fb2a..00000000
--- a/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...);
6
7//---------------------------------------------------------------------
8// CLR HOSTING
9//---------------------------------------------------------------------
10
11/// <summary>
12/// Binds to the CLR after determining the appropriate version.
13/// </summary>
14/// <param name="hSession">Handle to the installer session,
15/// used just for logging.</param>
16/// <param name="version">Specific version of the CLR to load.
17/// If null, then the config file and/or primary assembly are
18/// used to determine the version.</param>
19/// <param name="szConfigFile">XML .config file which may contain
20/// a startup section to direct which version of the CLR to use.
21/// May be NULL.</param>
22/// <param name="szPrimaryAssembly">Assembly to be used to determine
23/// the version of the CLR in the absence of other configuration.
24/// May be NULL.</param>
25/// <param name="ppHost">Returned runtime host interface.</param>
26/// <returns>True if the CLR was loaded successfully, false if
27/// there was some error.</returns>
28/// <remarks>
29/// If szPrimaryAssembly is NULL and szConfigFile is also NULL or
30/// does not contain any version configuration, the CLR will not be loaded.
31/// </remarks>
32bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile,
33 const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost)
34{
35 typedef HRESULT (__stdcall *PGetRequestedRuntimeInfo)(LPCWSTR pExe, LPCWSTR pwszVersion,
36 LPCWSTR pConfigurationFile, DWORD startupFlags, DWORD runtimeInfoFlags,
37 LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength,
38 LPWSTR pVersion, DWORD cchBuffer, DWORD* dwlength);
39 typedef HRESULT (__stdcall *PCorBindToRuntimeEx)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor,
40 DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
41
42 HMODULE hmodMscoree = LoadLibrary(L"mscoree.dll");
43 if (hmodMscoree == NULL)
44 {
45 Log(hSession, L"Failed to load mscoree.dll (Error code %d). This custom action "
46 L"requires the .NET Framework to be installed.", GetLastError());
47 return false;
48 }
49 PGetRequestedRuntimeInfo pGetRequestedRuntimeInfo = (PGetRequestedRuntimeInfo)
50 GetProcAddress(hmodMscoree, "GetRequestedRuntimeInfo");
51 PCorBindToRuntimeEx pCorBindToRuntimeEx = (PCorBindToRuntimeEx)
52 GetProcAddress(hmodMscoree, "CorBindToRuntimeEx");
53 if (pGetRequestedRuntimeInfo == NULL || pCorBindToRuntimeEx == NULL)
54 {
55 Log(hSession, L"Failed to locate functions in mscoree.dll (Error code %d). This custom action "
56 L"requires the .NET Framework to be installed.", GetLastError());
57 FreeLibrary(hmodMscoree);
58 return false;
59 }
60
61 wchar_t szClrVersion[20];
62 HRESULT hr;
63
64 if (szVersion != NULL && szVersion[0] != L'\0')
65 {
66 wcsncpy_s(szClrVersion, 20, szVersion, 20);
67 }
68 else
69 {
70 wchar_t szVersionDir[MAX_PATH];
71 hr = pGetRequestedRuntimeInfo(szPrimaryAssembly, NULL,
72 szConfigFile, 0, 0, szVersionDir, MAX_PATH, NULL, szClrVersion, 20, NULL);
73 if (FAILED(hr))
74 {
75 Log(hSession, L"Failed to get requested CLR info. Error code 0x%x", hr);
76 Log(hSession, L"Ensure that the proper version of the .NET Framework is installed, or "
77 L"that there is a matching supportedRuntime element in CustomAction.config. "
78 L"If you are binding to .NET 4 or greater add "
79 L"useLegacyV2RuntimeActivationPolicy=true to the <startup> element.");
80 FreeLibrary(hmodMscoree);
81 return false;
82 }
83 }
84
85 Log(hSession, L"Binding to CLR version %s", szClrVersion);
86
87 ICorRuntimeHost* pHost;
88 hr = pCorBindToRuntimeEx(szClrVersion, NULL,
89 STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
90 CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**) &pHost);
91 if (FAILED(hr))
92 {
93 Log(hSession, L"Failed to bind to the CLR. Error code 0x%X", hr);
94 FreeLibrary(hmodMscoree);
95 return false;
96 }
97 hr = pHost->Start();
98 if (FAILED(hr))
99 {
100 Log(hSession, L"Failed to start the CLR. Error code 0x%X", hr);
101 pHost->Release();
102 FreeLibrary(hmodMscoree);
103 return false;
104 }
105 *ppHost = pHost;
106 FreeLibrary(hmodMscoree);
107 return true;
108}
109
110/// <summary>
111/// Creates a new CLR application domain.
112/// </summary>
113/// <param name="hSession">Handle to the installer session,
114/// used just for logging</param>
115/// <param name="pHost">Interface to the runtime host where the
116/// app domain will be created.</param>
117/// <param name="szName">Name of the app domain to create.</param>
118/// <param name="szAppBase">Application base directory path, where
119/// the app domain will look first to load its assemblies.</param>
120/// <param name="szConfigFile">Optional XML .config file containing any
121/// configuration for thae app domain.</param>
122/// <param name="ppAppDomain">Returned app domain interface.</param>
123/// <returns>True if the app domain was created successfully, false if
124/// there was some error.</returns>
125bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost,
126 const wchar_t* szName, const wchar_t* szAppBase,
127 const wchar_t* szConfigFile, _AppDomain** ppAppDomain)
128{
129 IUnknown* punkAppDomainSetup = NULL;
130 IAppDomainSetup* pAppDomainSetup = NULL;
131 HRESULT hr = pHost->CreateDomainSetup(&punkAppDomainSetup);
132 if (SUCCEEDED(hr))
133 {
134 hr = punkAppDomainSetup->QueryInterface(__uuidof(IAppDomainSetup), (void**) &pAppDomainSetup);
135 punkAppDomainSetup->Release();
136 }
137 if (FAILED(hr))
138 {
139 Log(hSession, L"Failed to create app domain setup. Error code 0x%X", hr);
140 return false;
141 }
142
143 const wchar_t* szUrlPrefix = L"file:///";
144 size_t cchApplicationBase = wcslen(szUrlPrefix) + wcslen(szAppBase);
145 wchar_t* szApplicationBase = (wchar_t*) _alloca((cchApplicationBase + 1) * sizeof(wchar_t));
146 if (szApplicationBase == NULL) hr = E_OUTOFMEMORY;
147 else
148 {
149 StringCchCopy(szApplicationBase, cchApplicationBase + 1, szUrlPrefix);
150 StringCchCat(szApplicationBase, cchApplicationBase + 1, szAppBase);
151 BSTR bstrApplicationBase = SysAllocString(szApplicationBase);
152 if (bstrApplicationBase == NULL) hr = E_OUTOFMEMORY;
153 else
154 {
155 hr = pAppDomainSetup->put_ApplicationBase(bstrApplicationBase);
156 SysFreeString(bstrApplicationBase);
157 }
158 }
159
160 if (SUCCEEDED(hr) && szConfigFile != NULL)
161 {
162 BSTR bstrConfigFile = SysAllocString(szConfigFile);
163 if (bstrConfigFile == NULL) hr = E_OUTOFMEMORY;
164 else
165 {
166 hr = pAppDomainSetup->put_ConfigurationFile(bstrConfigFile);
167 SysFreeString(bstrConfigFile);
168 }
169 }
170
171 if (FAILED(hr))
172 {
173 Log(hSession, L"Failed to configure app domain setup. Error code 0x%X", hr);
174 pAppDomainSetup->Release();
175 return false;
176 }
177
178 IUnknown* punkAppDomain;
179 hr = pHost->CreateDomainEx(szName, pAppDomainSetup, NULL, &punkAppDomain);
180 pAppDomainSetup->Release();
181 if (SUCCEEDED(hr))
182 {
183 hr = punkAppDomain->QueryInterface(__uuidof(_AppDomain), (void**) ppAppDomain);
184 punkAppDomain->Release();
185 }
186
187 if (FAILED(hr))
188 {
189 Log(hSession, L"Failed to create app domain. Error code 0x%X", hr);
190 return false;
191 }
192
193 return true;
194}
195
196/// <summary>
197/// Locates a specific method in a specific class and assembly.
198/// </summary>
199/// <param name="hSession">Handle to the installer session,
200/// used just for logging</param>
201/// <param name="pAppDomain">Application domain in which to
202/// load assemblies.</param>
203/// <param name="szAssembly">Display name of the assembly
204/// containing the method.</param>
205/// <param name="szClass">Fully-qualified name of the class
206/// containing the method.</param>
207/// <param name="szMethod">Name of the method.</param>
208/// <param name="ppMethod">Returned method interface.</param>
209/// <returns>True if the method was located, otherwise false.</returns>
210/// <remarks>Only public static methods are searched. Method
211/// parameter types are not considered; if there are multiple
212/// matching methods with different parameters, an error results.</remarks>
213bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain,
214 const wchar_t* szAssembly, const wchar_t* szClass,
215 const wchar_t* szMethod, _MethodInfo** ppMethod)
216{
217 HRESULT hr;
218 _Assembly* pAssembly = NULL;
219 BSTR bstrAssemblyName = SysAllocString(szAssembly);
220 if (bstrAssemblyName == NULL) hr = E_OUTOFMEMORY;
221 else
222 {
223 hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly);
224 SysFreeString(bstrAssemblyName);
225 }
226 if (FAILED(hr))
227 {
228 Log(hSession, L"Failed to load assembly %s. Error code 0x%X", szAssembly, hr);
229 return false;
230 }
231
232 _Type* pType = NULL;
233 BSTR bstrClass = SysAllocString(szClass);
234 if (bstrClass == NULL) hr = E_OUTOFMEMORY;
235 else
236 {
237 hr = pAssembly->GetType_2(bstrClass, &pType);
238 SysFreeString(bstrClass);
239 }
240 pAssembly->Release();
241 if (FAILED(hr) || pType == NULL)
242 {
243 Log(hSession, L"Failed to load class %s. Error code 0x%X", szClass, hr);
244 return false;
245 }
246
247 BSTR bstrMethod = SysAllocString(szMethod);
248 if (bstrMethod == NULL) hr = E_OUTOFMEMORY;
249 else
250 {
251 hr = pType->GetMethod_2(bstrMethod,
252 (BindingFlags) (BindingFlags_Public | BindingFlags_Static), ppMethod);
253 SysFreeString(bstrMethod);
254 }
255 pType->Release();
256 if (FAILED(hr) || *ppMethod == NULL)
257 {
258 Log(hSession, L"Failed to get method %s. Error code 0x%X", szMethod, hr);
259 return false;
260 }
261 return true;
262}
diff --git a/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp b/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp
deleted file mode 100644
index a49cdeec..00000000
--- a/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include "SfxUtil.h"
5
6// Globals for keeping track of things across UI messages.
7static const wchar_t* g_szWorkingDir;
8static ICorRuntimeHost* g_pClrHost;
9static _AppDomain* g_pAppDomain;
10static _MethodInfo* g_pProcessMessageMethod;
11static _MethodInfo* g_pShutdownMethod;
12
13// Reserve extra space for strings to be replaced at build time.
14#define NULLSPACE \
15L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
16L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
17L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
18L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
19
20// Prototypes for local functions.
21// See the function definitions for comments.
22
23bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession,
24 const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult);
25
26/// <summary>
27/// First entry-point for the UI DLL when loaded and called by MSI.
28/// Extracts the payload, hosts the CLR, and invokes the managed
29/// initialize method.
30/// </summary>
31/// <param name="hSession">Handle to the installer session,
32/// used for logging errors and to be passed on to the managed initialize method.</param>
33/// <param name="szResourcePath">Path the directory where resources from the MsiEmbeddedUI table
34/// have been extracted, and where additional payload from this package will be extracted.</param>
35/// <param name="pdwInternalUILevel">MSI install UI level passed to and returned from
36/// the managed initialize method.</param>
37extern "C"
38UINT __stdcall InitializeEmbeddedUI(MSIHANDLE hSession, LPCWSTR szResourcePath, LPDWORD pdwInternalUILevel)
39{
40 // If the managed initialize method cannot be called, continue the installation in BASIC UI mode.
41 UINT uiResult = INSTALLUILEVEL_BASIC;
42
43 const wchar_t* szClassName = L"InitializeEmbeddedUI_FullClassName" NULLSPACE;
44
45 g_szWorkingDir = szResourcePath;
46
47 wchar_t szModule[MAX_PATH];
48 DWORD cchCopied = GetModuleFileName(g_hModule, szModule, MAX_PATH - 1);
49 if (cchCopied == 0)
50 {
51 Log(hSession, L"Failed to get module path. Error code %d.", GetLastError());
52 return uiResult;
53 }
54 else if (cchCopied == MAX_PATH - 1)
55 {
56 Log(hSession, L"Failed to get module path -- path is too long.");
57 return uiResult;
58 }
59
60 Log(hSession, L"Extracting embedded UI to temporary directory: %s", g_szWorkingDir);
61 int err = ExtractCabinet(szModule, g_szWorkingDir);
62 if (err != 0)
63 {
64 Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err);
65 Log(hSession, L"Ensure that no MsiEmbeddedUI.FileName values are the same as "
66 L"any file contained in the embedded UI package.");
67 return uiResult;
68 }
69
70 wchar_t szConfigFilePath[MAX_PATH + 20];
71 StringCchCopy(szConfigFilePath, MAX_PATH + 20, g_szWorkingDir);
72 StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\EmbeddedUI.config");
73
74 const wchar_t* szConfigFile = szConfigFilePath;
75 if (!PathFileExists(szConfigFilePath))
76 {
77 szConfigFile = NULL;
78 }
79
80 wchar_t szWIAssembly[MAX_PATH + 50];
81 StringCchCopy(szWIAssembly, MAX_PATH + 50, g_szWorkingDir);
82 StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\WixToolset.Dtf.WindowsInstaller.dll");
83
84 if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &g_pClrHost))
85 {
86 if (CreateAppDomain(hSession, g_pClrHost, L"EmbeddedUI", g_szWorkingDir,
87 szConfigFile, &g_pAppDomain))
88 {
89 const wchar_t* szMsiAssemblyName = L"WixToolset.Dtf.WindowsInstaller";
90 const wchar_t* szProxyClass = L"WixToolset.Dtf.WindowsInstaller.EmbeddedUIProxy";
91 const wchar_t* szInitMethod = L"Initialize";
92 const wchar_t* szProcessMessageMethod = L"ProcessMessage";
93 const wchar_t* szShutdownMethod = L"Shutdown";
94
95 if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName,
96 szProxyClass, szProcessMessageMethod, &g_pProcessMessageMethod) &&
97 GetMethod(hSession, g_pAppDomain, szMsiAssemblyName,
98 szProxyClass, szShutdownMethod, &g_pShutdownMethod))
99 {
100 _MethodInfo* pInitMethod;
101 if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName,
102 szProxyClass, szInitMethod, &pInitMethod))
103 {
104 bool invokeSuccess = InvokeInitializeMethod(pInitMethod, hSession, szClassName, pdwInternalUILevel, &uiResult);
105 pInitMethod->Release();
106 if (invokeSuccess)
107 {
108 if (uiResult == 0)
109 {
110 return ERROR_SUCCESS;
111 }
112 else if (uiResult == ERROR_INSTALL_USEREXIT)
113 {
114 // InitializeEmbeddedUI is not allowed to return ERROR_INSTALL_USEREXIT.
115 // So return success here and then IDCANCEL on the next progress message.
116 uiResult = 0;
117 *pdwInternalUILevel = INSTALLUILEVEL_NONE;
118 Log(hSession, L"Initialization canceled by user.");
119 }
120 }
121 }
122 }
123
124 g_pProcessMessageMethod->Release();
125 g_pProcessMessageMethod = NULL;
126 g_pShutdownMethod->Release();
127 g_pShutdownMethod = NULL;
128
129 g_pClrHost->UnloadDomain(g_pAppDomain);
130 g_pAppDomain->Release();
131 g_pAppDomain = NULL;
132 }
133 g_pClrHost->Stop();
134 g_pClrHost->Release();
135 g_pClrHost = NULL;
136 }
137
138 return uiResult;
139}
140
141/// <summary>
142/// Entry-point for UI progress messages received from the MSI engine during an active installation.
143/// Forwards the progress messages to the managed handler method and returns its result.
144/// </summary>
145extern "C"
146INT __stdcall EmbeddedUIHandler(UINT uiMessageType, MSIHANDLE hRecord)
147{
148 if (g_pProcessMessageMethod == NULL)
149 {
150 // Initialization was canceled.
151 return IDCANCEL;
152 }
153
154 VARIANT vResult;
155 VariantInit(&vResult);
156
157 VARIANT vNull;
158 vNull.vt = VT_EMPTY;
159
160 SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 2);
161 VARIANT vMessageType;
162 vMessageType.vt = VT_I4;
163 vMessageType.lVal = (LONG) uiMessageType;
164 LONG index = 0;
165 HRESULT hr = SafeArrayPutElement(saArgs, &index, &vMessageType);
166 if (FAILED(hr)) goto LExit;
167 VARIANT vRecord;
168 vRecord.vt = VT_I4;
169 vRecord.lVal = (LONG) hRecord;
170 index = 1;
171 hr = SafeArrayPutElement(saArgs, &index, &vRecord);
172 if (FAILED(hr)) goto LExit;
173
174 hr = g_pProcessMessageMethod->Invoke_3(vNull, saArgs, &vResult);
175
176LExit:
177 SafeArrayDestroy(saArgs);
178 if (SUCCEEDED(hr))
179 {
180 return vResult.intVal;
181 }
182 else
183 {
184 return -1;
185 }
186}
187
188/// <summary>
189/// Entry-point for the UI shutdown message received from the MSI engine after installation has completed.
190/// Forwards the shutdown message to the managed shutdown method, then shuts down the CLR.
191/// </summary>
192extern "C"
193DWORD __stdcall ShutdownEmbeddedUI()
194{
195 if (g_pShutdownMethod != NULL)
196 {
197 VARIANT vNull;
198 vNull.vt = VT_EMPTY;
199 SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
200 g_pShutdownMethod->Invoke_3(vNull, saArgs, NULL);
201 SafeArrayDestroy(saArgs);
202
203 g_pClrHost->UnloadDomain(g_pAppDomain);
204 g_pAppDomain->Release();
205 g_pClrHost->Stop();
206 g_pClrHost->Release();
207 }
208
209 return 0;
210}
211
212/// <summary>
213/// Loads and invokes the managed portion of the proxy.
214/// </summary>
215/// <param name="pInitMethod">Managed initialize method to be invoked.</param>
216/// <param name="hSession">Handle to the installer session,
217/// used for logging errors and to be passed on to the managed initialize method.</param>
218/// <param name="szClassName">Name of the UI class to be loaded.
219/// This must be of the form: AssemblyName!Namespace.Class</param>
220/// <param name="pdwInternalUILevel">MSI install UI level passed to and returned from
221/// the managed initialize method.</param>
222/// <param name="puiResult">Return value of the invoked initialize method.</param>
223/// <returns>True if the managed proxy was invoked successfully, or an
224/// error code if there was some error. Note the initialize method itself may
225/// return an error via puiResult while this method still returns true
226/// since the invocation was successful.</returns>
227bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession, const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult)
228{
229 VARIANT vResult;
230 VariantInit(&vResult);
231
232 VARIANT vNull;
233 vNull.vt = VT_EMPTY;
234
235 SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3);
236 VARIANT vSessionHandle;
237 vSessionHandle.vt = VT_I4;
238 vSessionHandle.lVal = (LONG) hSession;
239 LONG index = 0;
240 HRESULT hr = SafeArrayPutElement(saArgs, &index, &vSessionHandle);
241 if (FAILED(hr)) goto LExit;
242 VARIANT vEntryPoint;
243 vEntryPoint.vt = VT_BSTR;
244 vEntryPoint.bstrVal = SysAllocString(szClassName);
245 if (vEntryPoint.bstrVal == NULL)
246 {
247 hr = E_OUTOFMEMORY;
248 goto LExit;
249 }
250 index = 1;
251 hr = SafeArrayPutElement(saArgs, &index, &vEntryPoint);
252 if (FAILED(hr)) goto LExit;
253 VARIANT vUILevel;
254 vUILevel.vt = VT_I4;
255 vUILevel.ulVal = *pdwInternalUILevel;
256 index = 2;
257 hr = SafeArrayPutElement(saArgs, &index, &vUILevel);
258 if (FAILED(hr)) goto LExit;
259
260 hr = pInitMethod->Invoke_3(vNull, saArgs, &vResult);
261
262LExit:
263 SafeArrayDestroy(saArgs);
264 if (SUCCEEDED(hr))
265 {
266 *puiResult = (UINT) vResult.lVal;
267 if ((*puiResult & 0xFFFF) == 0)
268 {
269 // Due to interop limitations, the successful resulting UILevel is returned
270 // as the high-word of the return value instead of via a ref parameter.
271 *pdwInternalUILevel = *puiResult >> 16;
272 *puiResult = 0;
273 }
274 return true;
275 }
276 else
277 {
278 Log(hSession, L"Failed to invoke EmbeddedUI Initialize method. Error code 0x%X", hr);
279 return false;
280 }
281}
diff --git a/src/samples/Dtf/Tools/SfxCA/EntryPoints.def b/src/samples/Dtf/Tools/SfxCA/EntryPoints.def
deleted file mode 100644
index dd28b920..00000000
--- a/src/samples/Dtf/Tools/SfxCA/EntryPoints.def
+++ /dev/null
@@ -1,140 +0,0 @@
1; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3
4LIBRARY "SfxCA"
5
6EXPORTS
7
8CustomActionEntryPoint000________________________________________________=CustomActionEntryPoint000
9CustomActionEntryPoint001________________________________________________=CustomActionEntryPoint001
10CustomActionEntryPoint002________________________________________________=CustomActionEntryPoint002
11CustomActionEntryPoint003________________________________________________=CustomActionEntryPoint003
12CustomActionEntryPoint004________________________________________________=CustomActionEntryPoint004
13CustomActionEntryPoint005________________________________________________=CustomActionEntryPoint005
14CustomActionEntryPoint006________________________________________________=CustomActionEntryPoint006
15CustomActionEntryPoint007________________________________________________=CustomActionEntryPoint007
16CustomActionEntryPoint008________________________________________________=CustomActionEntryPoint008
17CustomActionEntryPoint009________________________________________________=CustomActionEntryPoint009
18CustomActionEntryPoint010________________________________________________=CustomActionEntryPoint010
19CustomActionEntryPoint011________________________________________________=CustomActionEntryPoint011
20CustomActionEntryPoint012________________________________________________=CustomActionEntryPoint012
21CustomActionEntryPoint013________________________________________________=CustomActionEntryPoint013
22CustomActionEntryPoint014________________________________________________=CustomActionEntryPoint014
23CustomActionEntryPoint015________________________________________________=CustomActionEntryPoint015
24CustomActionEntryPoint016________________________________________________=CustomActionEntryPoint016
25CustomActionEntryPoint017________________________________________________=CustomActionEntryPoint017
26CustomActionEntryPoint018________________________________________________=CustomActionEntryPoint018
27CustomActionEntryPoint019________________________________________________=CustomActionEntryPoint019
28CustomActionEntryPoint020________________________________________________=CustomActionEntryPoint020
29CustomActionEntryPoint021________________________________________________=CustomActionEntryPoint021
30CustomActionEntryPoint022________________________________________________=CustomActionEntryPoint022
31CustomActionEntryPoint023________________________________________________=CustomActionEntryPoint023
32CustomActionEntryPoint024________________________________________________=CustomActionEntryPoint024
33CustomActionEntryPoint025________________________________________________=CustomActionEntryPoint025
34CustomActionEntryPoint026________________________________________________=CustomActionEntryPoint026
35CustomActionEntryPoint027________________________________________________=CustomActionEntryPoint027
36CustomActionEntryPoint028________________________________________________=CustomActionEntryPoint028
37CustomActionEntryPoint029________________________________________________=CustomActionEntryPoint029
38CustomActionEntryPoint030________________________________________________=CustomActionEntryPoint030
39CustomActionEntryPoint031________________________________________________=CustomActionEntryPoint031
40CustomActionEntryPoint032________________________________________________=CustomActionEntryPoint032
41CustomActionEntryPoint033________________________________________________=CustomActionEntryPoint033
42CustomActionEntryPoint034________________________________________________=CustomActionEntryPoint034
43CustomActionEntryPoint035________________________________________________=CustomActionEntryPoint035
44CustomActionEntryPoint036________________________________________________=CustomActionEntryPoint036
45CustomActionEntryPoint037________________________________________________=CustomActionEntryPoint037
46CustomActionEntryPoint038________________________________________________=CustomActionEntryPoint038
47CustomActionEntryPoint039________________________________________________=CustomActionEntryPoint039
48CustomActionEntryPoint040________________________________________________=CustomActionEntryPoint040
49CustomActionEntryPoint041________________________________________________=CustomActionEntryPoint041
50CustomActionEntryPoint042________________________________________________=CustomActionEntryPoint042
51CustomActionEntryPoint043________________________________________________=CustomActionEntryPoint043
52CustomActionEntryPoint044________________________________________________=CustomActionEntryPoint044
53CustomActionEntryPoint045________________________________________________=CustomActionEntryPoint045
54CustomActionEntryPoint046________________________________________________=CustomActionEntryPoint046
55CustomActionEntryPoint047________________________________________________=CustomActionEntryPoint047
56CustomActionEntryPoint048________________________________________________=CustomActionEntryPoint048
57CustomActionEntryPoint049________________________________________________=CustomActionEntryPoint049
58CustomActionEntryPoint050________________________________________________=CustomActionEntryPoint050
59CustomActionEntryPoint051________________________________________________=CustomActionEntryPoint051
60CustomActionEntryPoint052________________________________________________=CustomActionEntryPoint052
61CustomActionEntryPoint053________________________________________________=CustomActionEntryPoint053
62CustomActionEntryPoint054________________________________________________=CustomActionEntryPoint054
63CustomActionEntryPoint055________________________________________________=CustomActionEntryPoint055
64CustomActionEntryPoint056________________________________________________=CustomActionEntryPoint056
65CustomActionEntryPoint057________________________________________________=CustomActionEntryPoint057
66CustomActionEntryPoint058________________________________________________=CustomActionEntryPoint058
67CustomActionEntryPoint059________________________________________________=CustomActionEntryPoint059
68CustomActionEntryPoint060________________________________________________=CustomActionEntryPoint060
69CustomActionEntryPoint061________________________________________________=CustomActionEntryPoint061
70CustomActionEntryPoint062________________________________________________=CustomActionEntryPoint062
71CustomActionEntryPoint063________________________________________________=CustomActionEntryPoint063
72CustomActionEntryPoint064________________________________________________=CustomActionEntryPoint064
73CustomActionEntryPoint065________________________________________________=CustomActionEntryPoint065
74CustomActionEntryPoint066________________________________________________=CustomActionEntryPoint066
75CustomActionEntryPoint067________________________________________________=CustomActionEntryPoint067
76CustomActionEntryPoint068________________________________________________=CustomActionEntryPoint068
77CustomActionEntryPoint069________________________________________________=CustomActionEntryPoint069
78CustomActionEntryPoint070________________________________________________=CustomActionEntryPoint070
79CustomActionEntryPoint071________________________________________________=CustomActionEntryPoint071
80CustomActionEntryPoint072________________________________________________=CustomActionEntryPoint072
81CustomActionEntryPoint073________________________________________________=CustomActionEntryPoint073
82CustomActionEntryPoint074________________________________________________=CustomActionEntryPoint074
83CustomActionEntryPoint075________________________________________________=CustomActionEntryPoint075
84CustomActionEntryPoint076________________________________________________=CustomActionEntryPoint076
85CustomActionEntryPoint077________________________________________________=CustomActionEntryPoint077
86CustomActionEntryPoint078________________________________________________=CustomActionEntryPoint078
87CustomActionEntryPoint079________________________________________________=CustomActionEntryPoint079
88CustomActionEntryPoint080________________________________________________=CustomActionEntryPoint080
89CustomActionEntryPoint081________________________________________________=CustomActionEntryPoint081
90CustomActionEntryPoint082________________________________________________=CustomActionEntryPoint082
91CustomActionEntryPoint083________________________________________________=CustomActionEntryPoint083
92CustomActionEntryPoint084________________________________________________=CustomActionEntryPoint084
93CustomActionEntryPoint085________________________________________________=CustomActionEntryPoint085
94CustomActionEntryPoint086________________________________________________=CustomActionEntryPoint086
95CustomActionEntryPoint087________________________________________________=CustomActionEntryPoint087
96CustomActionEntryPoint088________________________________________________=CustomActionEntryPoint088
97CustomActionEntryPoint089________________________________________________=CustomActionEntryPoint089
98CustomActionEntryPoint090________________________________________________=CustomActionEntryPoint090
99CustomActionEntryPoint091________________________________________________=CustomActionEntryPoint091
100CustomActionEntryPoint092________________________________________________=CustomActionEntryPoint092
101CustomActionEntryPoint093________________________________________________=CustomActionEntryPoint093
102CustomActionEntryPoint094________________________________________________=CustomActionEntryPoint094
103CustomActionEntryPoint095________________________________________________=CustomActionEntryPoint095
104CustomActionEntryPoint096________________________________________________=CustomActionEntryPoint096
105CustomActionEntryPoint097________________________________________________=CustomActionEntryPoint097
106CustomActionEntryPoint098________________________________________________=CustomActionEntryPoint098
107CustomActionEntryPoint099________________________________________________=CustomActionEntryPoint099
108CustomActionEntryPoint100________________________________________________=CustomActionEntryPoint100
109CustomActionEntryPoint101________________________________________________=CustomActionEntryPoint101
110CustomActionEntryPoint102________________________________________________=CustomActionEntryPoint102
111CustomActionEntryPoint103________________________________________________=CustomActionEntryPoint103
112CustomActionEntryPoint104________________________________________________=CustomActionEntryPoint104
113CustomActionEntryPoint105________________________________________________=CustomActionEntryPoint105
114CustomActionEntryPoint106________________________________________________=CustomActionEntryPoint106
115CustomActionEntryPoint107________________________________________________=CustomActionEntryPoint107
116CustomActionEntryPoint108________________________________________________=CustomActionEntryPoint108
117CustomActionEntryPoint109________________________________________________=CustomActionEntryPoint109
118CustomActionEntryPoint110________________________________________________=CustomActionEntryPoint110
119CustomActionEntryPoint111________________________________________________=CustomActionEntryPoint111
120CustomActionEntryPoint112________________________________________________=CustomActionEntryPoint112
121CustomActionEntryPoint113________________________________________________=CustomActionEntryPoint113
122CustomActionEntryPoint114________________________________________________=CustomActionEntryPoint114
123CustomActionEntryPoint115________________________________________________=CustomActionEntryPoint115
124CustomActionEntryPoint116________________________________________________=CustomActionEntryPoint116
125CustomActionEntryPoint117________________________________________________=CustomActionEntryPoint117
126CustomActionEntryPoint118________________________________________________=CustomActionEntryPoint118
127CustomActionEntryPoint119________________________________________________=CustomActionEntryPoint119
128CustomActionEntryPoint120________________________________________________=CustomActionEntryPoint120
129CustomActionEntryPoint121________________________________________________=CustomActionEntryPoint121
130CustomActionEntryPoint122________________________________________________=CustomActionEntryPoint122
131CustomActionEntryPoint123________________________________________________=CustomActionEntryPoint123
132CustomActionEntryPoint124________________________________________________=CustomActionEntryPoint124
133CustomActionEntryPoint125________________________________________________=CustomActionEntryPoint125
134CustomActionEntryPoint126________________________________________________=CustomActionEntryPoint126
135CustomActionEntryPoint127________________________________________________=CustomActionEntryPoint127
136
137zzzzInvokeManagedCustomActionOutOfProcW=InvokeManagedCustomActionOutOfProc
138zzzInitializeEmbeddedUI=InitializeEmbeddedUI
139zzzEmbeddedUIHandler=EmbeddedUIHandler
140zzzShutdownEmbeddedUI=ShutdownEmbeddedUI
diff --git a/src/samples/Dtf/Tools/SfxCA/EntryPoints.h b/src/samples/Dtf/Tools/SfxCA/EntryPoints.h
deleted file mode 100644
index bd2fa970..00000000
--- a/src/samples/Dtf/Tools/SfxCA/EntryPoints.h
+++ /dev/null
@@ -1,162 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3int InvokeCustomAction(MSIHANDLE hSession,
4 const wchar_t* szWorkingDir, const wchar_t* szEntryPoint);
5
6/// <summary>
7/// Macro for defining and exporting a custom action entrypoint.
8/// </summary>
9/// <param name="name">Name of the entrypoint as exported from
10/// the DLL.</param>
11/// <param name="method">Path to the managed custom action method,
12/// in the form: "AssemblyName!Namespace.Class.Method"</param>
13/// <remarks>
14/// To prevent the exported name from being decorated, add
15/// /EXPORT:name to the linker options for every entrypoint.
16/// </remarks>
17#define CUSTOMACTION_ENTRYPOINT(name,method) extern "C" int __stdcall \
18 name(MSIHANDLE hSession) { return InvokeCustomAction(hSession, NULL, method); }
19
20// TEMPLATE ENTRYPOINTS
21// To be edited by the MakeSfxCA tool.
22
23#define NULLSPACE \
24L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
25L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
26L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
27L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
28
29#define TEMPLATE_CA_ENTRYPOINT(id,sid) CUSTOMACTION_ENTRYPOINT( \
30 CustomActionEntryPoint##id##, \
31 L"CustomActionEntryPoint" sid NULLSPACE)
32
33TEMPLATE_CA_ENTRYPOINT(000,L"000");
34TEMPLATE_CA_ENTRYPOINT(001,L"001");
35TEMPLATE_CA_ENTRYPOINT(002,L"002");
36TEMPLATE_CA_ENTRYPOINT(003,L"003");
37TEMPLATE_CA_ENTRYPOINT(004,L"004");
38TEMPLATE_CA_ENTRYPOINT(005,L"005");
39TEMPLATE_CA_ENTRYPOINT(006,L"006");
40TEMPLATE_CA_ENTRYPOINT(007,L"007");
41TEMPLATE_CA_ENTRYPOINT(008,L"008");
42TEMPLATE_CA_ENTRYPOINT(009,L"009");
43TEMPLATE_CA_ENTRYPOINT(010,L"010");
44TEMPLATE_CA_ENTRYPOINT(011,L"011");
45TEMPLATE_CA_ENTRYPOINT(012,L"012");
46TEMPLATE_CA_ENTRYPOINT(013,L"013");
47TEMPLATE_CA_ENTRYPOINT(014,L"014");
48TEMPLATE_CA_ENTRYPOINT(015,L"015");
49TEMPLATE_CA_ENTRYPOINT(016,L"016");
50TEMPLATE_CA_ENTRYPOINT(017,L"017");
51TEMPLATE_CA_ENTRYPOINT(018,L"018");
52TEMPLATE_CA_ENTRYPOINT(019,L"019");
53TEMPLATE_CA_ENTRYPOINT(020,L"020");
54TEMPLATE_CA_ENTRYPOINT(021,L"021");
55TEMPLATE_CA_ENTRYPOINT(022,L"022");
56TEMPLATE_CA_ENTRYPOINT(023,L"023");
57TEMPLATE_CA_ENTRYPOINT(024,L"024");
58TEMPLATE_CA_ENTRYPOINT(025,L"025");
59TEMPLATE_CA_ENTRYPOINT(026,L"026");
60TEMPLATE_CA_ENTRYPOINT(027,L"027");
61TEMPLATE_CA_ENTRYPOINT(028,L"028");
62TEMPLATE_CA_ENTRYPOINT(029,L"029");
63TEMPLATE_CA_ENTRYPOINT(030,L"030");
64TEMPLATE_CA_ENTRYPOINT(031,L"031");
65TEMPLATE_CA_ENTRYPOINT(032,L"032");
66TEMPLATE_CA_ENTRYPOINT(033,L"033");
67TEMPLATE_CA_ENTRYPOINT(034,L"034");
68TEMPLATE_CA_ENTRYPOINT(035,L"035");
69TEMPLATE_CA_ENTRYPOINT(036,L"036");
70TEMPLATE_CA_ENTRYPOINT(037,L"037");
71TEMPLATE_CA_ENTRYPOINT(038,L"038");
72TEMPLATE_CA_ENTRYPOINT(039,L"039");
73TEMPLATE_CA_ENTRYPOINT(040,L"040");
74TEMPLATE_CA_ENTRYPOINT(041,L"041");
75TEMPLATE_CA_ENTRYPOINT(042,L"042");
76TEMPLATE_CA_ENTRYPOINT(043,L"043");
77TEMPLATE_CA_ENTRYPOINT(044,L"044");
78TEMPLATE_CA_ENTRYPOINT(045,L"045");
79TEMPLATE_CA_ENTRYPOINT(046,L"046");
80TEMPLATE_CA_ENTRYPOINT(047,L"047");
81TEMPLATE_CA_ENTRYPOINT(048,L"048");
82TEMPLATE_CA_ENTRYPOINT(049,L"049");
83TEMPLATE_CA_ENTRYPOINT(050,L"050");
84TEMPLATE_CA_ENTRYPOINT(051,L"051");
85TEMPLATE_CA_ENTRYPOINT(052,L"052");
86TEMPLATE_CA_ENTRYPOINT(053,L"053");
87TEMPLATE_CA_ENTRYPOINT(054,L"054");
88TEMPLATE_CA_ENTRYPOINT(055,L"055");
89TEMPLATE_CA_ENTRYPOINT(056,L"056");
90TEMPLATE_CA_ENTRYPOINT(057,L"057");
91TEMPLATE_CA_ENTRYPOINT(058,L"058");
92TEMPLATE_CA_ENTRYPOINT(059,L"059");
93TEMPLATE_CA_ENTRYPOINT(060,L"060");
94TEMPLATE_CA_ENTRYPOINT(061,L"061");
95TEMPLATE_CA_ENTRYPOINT(062,L"062");
96TEMPLATE_CA_ENTRYPOINT(063,L"063");
97TEMPLATE_CA_ENTRYPOINT(064,L"064");
98TEMPLATE_CA_ENTRYPOINT(065,L"065");
99TEMPLATE_CA_ENTRYPOINT(066,L"066");
100TEMPLATE_CA_ENTRYPOINT(067,L"067");
101TEMPLATE_CA_ENTRYPOINT(068,L"068");
102TEMPLATE_CA_ENTRYPOINT(069,L"069");
103TEMPLATE_CA_ENTRYPOINT(070,L"070");
104TEMPLATE_CA_ENTRYPOINT(071,L"071");
105TEMPLATE_CA_ENTRYPOINT(072,L"072");
106TEMPLATE_CA_ENTRYPOINT(073,L"073");
107TEMPLATE_CA_ENTRYPOINT(074,L"074");
108TEMPLATE_CA_ENTRYPOINT(075,L"075");
109TEMPLATE_CA_ENTRYPOINT(076,L"076");
110TEMPLATE_CA_ENTRYPOINT(077,L"077");
111TEMPLATE_CA_ENTRYPOINT(078,L"078");
112TEMPLATE_CA_ENTRYPOINT(079,L"079");
113TEMPLATE_CA_ENTRYPOINT(080,L"080");
114TEMPLATE_CA_ENTRYPOINT(081,L"081");
115TEMPLATE_CA_ENTRYPOINT(082,L"082");
116TEMPLATE_CA_ENTRYPOINT(083,L"083");
117TEMPLATE_CA_ENTRYPOINT(084,L"084");
118TEMPLATE_CA_ENTRYPOINT(085,L"085");
119TEMPLATE_CA_ENTRYPOINT(086,L"086");
120TEMPLATE_CA_ENTRYPOINT(087,L"087");
121TEMPLATE_CA_ENTRYPOINT(088,L"088");
122TEMPLATE_CA_ENTRYPOINT(089,L"089");
123TEMPLATE_CA_ENTRYPOINT(090,L"090");
124TEMPLATE_CA_ENTRYPOINT(091,L"091");
125TEMPLATE_CA_ENTRYPOINT(092,L"092");
126TEMPLATE_CA_ENTRYPOINT(093,L"093");
127TEMPLATE_CA_ENTRYPOINT(094,L"094");
128TEMPLATE_CA_ENTRYPOINT(095,L"095");
129TEMPLATE_CA_ENTRYPOINT(096,L"096");
130TEMPLATE_CA_ENTRYPOINT(097,L"097");
131TEMPLATE_CA_ENTRYPOINT(098,L"098");
132TEMPLATE_CA_ENTRYPOINT(099,L"099");
133TEMPLATE_CA_ENTRYPOINT(100,L"100");
134TEMPLATE_CA_ENTRYPOINT(101,L"101");
135TEMPLATE_CA_ENTRYPOINT(102,L"102");
136TEMPLATE_CA_ENTRYPOINT(103,L"103");
137TEMPLATE_CA_ENTRYPOINT(104,L"104");
138TEMPLATE_CA_ENTRYPOINT(105,L"105");
139TEMPLATE_CA_ENTRYPOINT(106,L"106");
140TEMPLATE_CA_ENTRYPOINT(107,L"107");
141TEMPLATE_CA_ENTRYPOINT(108,L"108");
142TEMPLATE_CA_ENTRYPOINT(109,L"109");
143TEMPLATE_CA_ENTRYPOINT(110,L"110");
144TEMPLATE_CA_ENTRYPOINT(111,L"111");
145TEMPLATE_CA_ENTRYPOINT(112,L"112");
146TEMPLATE_CA_ENTRYPOINT(113,L"113");
147TEMPLATE_CA_ENTRYPOINT(114,L"114");
148TEMPLATE_CA_ENTRYPOINT(115,L"115");
149TEMPLATE_CA_ENTRYPOINT(116,L"116");
150TEMPLATE_CA_ENTRYPOINT(117,L"117");
151TEMPLATE_CA_ENTRYPOINT(118,L"118");
152TEMPLATE_CA_ENTRYPOINT(119,L"119");
153TEMPLATE_CA_ENTRYPOINT(120,L"120");
154TEMPLATE_CA_ENTRYPOINT(121,L"121");
155TEMPLATE_CA_ENTRYPOINT(122,L"122");
156TEMPLATE_CA_ENTRYPOINT(123,L"123");
157TEMPLATE_CA_ENTRYPOINT(124,L"124");
158TEMPLATE_CA_ENTRYPOINT(125,L"125");
159TEMPLATE_CA_ENTRYPOINT(126,L"126");
160TEMPLATE_CA_ENTRYPOINT(127,L"127");
161
162// Note: Keep in sync with EntryPoints.def
diff --git a/src/samples/Dtf/Tools/SfxCA/Extract.cpp b/src/samples/Dtf/Tools/SfxCA/Extract.cpp
deleted file mode 100644
index 171cf52f..00000000
--- a/src/samples/Dtf/Tools/SfxCA/Extract.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5//---------------------------------------------------------------------
6// CABINET EXTRACTION
7//---------------------------------------------------------------------
8
9// Globals make this code unsuited for multhreaded use,
10// but FDI doesn't provide any other way to pass context.
11
12// Handle to the FDI (cab extraction) engine. Need access to this in a callback.
13static HFDI g_hfdi;
14
15// FDI is not unicode-aware, so avoid passing these paths through the callbacks.
16static const wchar_t* g_szExtractDir;
17static const wchar_t* g_szCabFile;
18
19// Offset into the source file where the cabinet really starts.
20// Used to trick FDI into extracting from a concatenated cabinet.
21static int g_lCabOffset;
22
23// Use the secure CRT version of _wsopen if available.
24#ifdef __GOT_SECURE_LIB__
25#define _wsopen__s(hf,file,oflag,shflag,pmode) _wsopen_s(&hf,file,oflag,shflag,pmode)
26#else
27#define _wsopen__s(hf,file,oflag,shflag,pmode) hf = _wsopen(file,oflag,shflag,pmode)
28#endif
29
30/// <summary>
31/// FDI callback to open a cabinet file.
32/// </summary>
33/// <param name="pszFile">Name of the file to be opened. This parameter
34/// is ignored since with our limited use this method is only ever called
35/// to open the main cabinet file.</param>
36/// <param name="oflag">Type of operations allowed.</param>
37/// <param name="pmode">Permission setting.</param>
38/// <returns>Integer file handle, or -1 if the file could not be opened.</returns>
39/// <remarks>
40/// To support reading from a cabinet that is concatenated onto
41/// another file, this function first searches for the offset of the cabinet,
42/// then saves that offset for use in recalculating later seeks.
43/// </remarks>
44static FNOPEN(CabOpen)
45{
46 UNREFERENCED_PARAMETER(pszFile);
47 int hf;
48 _wsopen__s(hf, g_szCabFile, oflag, _SH_DENYWR, pmode);
49 if (hf != -1)
50 {
51 FDICABINETINFO cabInfo;
52 int length = _lseek(hf, 0, SEEK_END);
53 for(int offset = 0; offset < length; offset += 256)
54 {
55 if (_lseek(hf, offset, SEEK_SET) != offset) break;
56 if (FDIIsCabinet(g_hfdi, hf, &cabInfo))
57 {
58 g_lCabOffset = offset;
59 _lseek(hf, offset, SEEK_SET);
60 return hf;
61 }
62 }
63 _close(hf);
64 }
65 return -1;
66}
67
68/// <summary>
69/// FDI callback to seek within a file.
70/// </summary>
71/// <param name="hf">File handle.</param>
72/// <param name="dist">Seek distance</param>
73/// <param name="seektype">Whether to seek relative to the
74/// beginning, current position, or end of the file.</param>
75/// <returns>Resultant position within the cabinet.</returns>
76/// <remarks>
77/// To support reading from a cabinet that is concatenated onto
78/// another file, this function recalculates seeks based on the
79/// offset that was determined when the cabinet was opened.
80/// </remarks>
81static FNSEEK(CabSeek)
82{
83 if (seektype == SEEK_SET) dist += g_lCabOffset;
84 int pos = _lseek((int) hf, dist, seektype);
85 pos -= g_lCabOffset;
86 return pos;
87}
88
89/// <summary>
90/// Ensures a directory and its parent directory path exists.
91/// </summary>
92/// <param name="szDirPath">Directory path, not including file name.</param>
93/// <returns>0 if the directory exists or was successfully created, else nonzero.</returns>
94/// <remarks>
95/// This function modifies characters in szDirPath, but always restores them
96/// regardless of error condition.
97/// </remarks>
98static int EnsureDirectoryExists(__inout_z wchar_t* szDirPath)
99{
100 int ret = 0;
101 if (!::CreateDirectoryW(szDirPath, NULL))
102 {
103 UINT err = ::GetLastError();
104 if (err != ERROR_ALREADY_EXISTS)
105 {
106 // Directory creation failed for some reason other than already existing.
107 // Try to create the parent directory first.
108 wchar_t* szLastSlash = NULL;
109 for (wchar_t* sz = szDirPath; *sz; sz++)
110 {
111 if (*sz == L'\\')
112 {
113 szLastSlash = sz;
114 }
115 }
116 if (szLastSlash)
117 {
118 // Temporarily take one directory off the path and recurse.
119 *szLastSlash = L'\0';
120 ret = EnsureDirectoryExists(szDirPath);
121 *szLastSlash = L'\\';
122
123 // Try to create the directory if all parents are created.
124 if (ret == 0 && !::CreateDirectoryW(szDirPath, NULL))
125 {
126 err = ::GetLastError();
127 if (err != ERROR_ALREADY_EXISTS)
128 {
129 ret = -1;
130 }
131 }
132 }
133 else
134 {
135 ret = -1;
136 }
137 }
138 }
139 return ret;
140}
141
142/// <summary>
143/// Ensures a file's directory and its parent directory path exists.
144/// </summary>
145/// <param name="szDirPath">Path including file name.</param>
146/// <returns>0 if the file's directory exists or was successfully created, else nonzero.</returns>
147/// <remarks>
148/// This function modifies characters in szFilePath, but always restores them
149/// regardless of error condition.
150/// </remarks>
151static int EnsureFileDirectoryExists(__inout_z wchar_t* szFilePath)
152{
153 int ret = 0;
154 wchar_t* szLastSlash = NULL;
155 for (wchar_t* sz = szFilePath; *sz; sz++)
156 {
157 if (*sz == L'\\')
158 {
159 szLastSlash = sz;
160 }
161 }
162 if (szLastSlash)
163 {
164 *szLastSlash = L'\0';
165 ret = EnsureDirectoryExists(szFilePath);
166 *szLastSlash = L'\\';
167 }
168 return ret;
169}
170
171/// <summary>
172/// FDI callback for handling files in the cabinet.
173/// </summary>
174/// <param name="fdint">Type of notification.</param>
175/// <param name="pfdin">Structure containing data about the notification.</param>
176/// <remarks>
177/// Refer to fdi.h for more comments on this notification callback.
178/// </remarks>
179static FNFDINOTIFY(CabNotification)
180{
181 // fdintCOPY_FILE:
182 // Called for each file that *starts* in the current cabinet, giving
183 // the client the opportunity to request that the file be copied or
184 // skipped.
185 // Entry:
186 // pfdin->psz1 = file name in cabinet
187 // pfdin->cb = uncompressed size of file
188 // pfdin->date = file date
189 // pfdin->time = file time
190 // pfdin->attribs = file attributes
191 // pfdin->iFolder = file's folder index
192 // Exit-Success:
193 // Return non-zero file handle for destination file; FDI writes
194 // data to this file use the PFNWRITE function supplied to FDICreate,
195 // and then calls fdintCLOSE_FILE_INFO to close the file and set
196 // the date, time, and attributes.
197 // Exit-Failure:
198 // Returns 0 => Skip file, do not copy
199 // Returns -1 => Abort FDICopy() call
200 if (fdint == fdintCOPY_FILE)
201 {
202 size_t cchFile = MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, NULL, 0);
203 size_t cchFilePath = wcslen(g_szExtractDir) + 1 + cchFile;
204 wchar_t* szFilePath = (wchar_t*) _alloca((cchFilePath + 1) * sizeof(wchar_t));
205 if (szFilePath == NULL) return -1;
206 StringCchCopyW(szFilePath, cchFilePath + 1, g_szExtractDir);
207 StringCchCatW(szFilePath, cchFilePath + 1, L"\\");
208 MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1,
209 szFilePath + cchFilePath - cchFile, (int) cchFile + 1);
210 int hf = -1;
211 if (EnsureFileDirectoryExists(szFilePath) == 0)
212 {
213 _wsopen__s(hf, szFilePath,
214 _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL,
215 _SH_DENYWR, _S_IREAD | _S_IWRITE);
216 }
217 return hf;
218 }
219
220 // fdintCLOSE_FILE_INFO:
221 // Called after all of the data has been written to a target file.
222 // This function must close the file and set the file date, time,
223 // and attributes.
224 // Entry:
225 // pfdin->psz1 = file name in cabinet
226 // pfdin->hf = file handle
227 // pfdin->date = file date
228 // pfdin->time = file time
229 // pfdin->attribs = file attributes
230 // pfdin->iFolder = file's folder index
231 // pfdin->cb = Run After Extract (0 - don't run, 1 Run)
232 // Exit-Success:
233 // Returns TRUE
234 // Exit-Failure:
235 // Returns FALSE, or -1 to abort
236 else if (fdint == fdintCLOSE_FILE_INFO)
237 {
238 _close((int) pfdin->hf);
239 return TRUE;
240 }
241 return 0;
242}
243
244/// <summary>
245/// Extracts all contents of a cabinet file to a directory.
246/// </summary>
247/// <param name="szCabFile">Path to the cabinet file to be extracted.
248/// The cabinet may actually start at some offset within the file,
249/// as long as that offset is a multiple of 256.</param>
250/// <param name="szExtractDir">Directory where files are to be extracted.
251/// This directory must already exist, but should be empty.</param>
252/// <returns>0 if the cabinet was extracted successfully,
253/// or an error code if any error occurred.</returns>
254/// <remarks>
255/// The extraction will not overwrite any files in the destination
256/// directory; extraction will be interrupted and fail if any files
257/// with the same name already exist.
258/// </remarks>
259int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir)
260{
261 ERF erf;
262 // Most of the FDI callbacks can be handled by existing CRT I/O functions.
263 // For our functionality we only need to handle the open and seek callbacks.
264 HFDI hfdi = FDICreate((PFNALLOC) malloc, (PFNFREE) free, CabOpen,
265 (PFNREAD) _read, (PFNWRITE) _write, (PFNCLOSE) _close,
266 CabSeek, cpu80386, &erf);
267 if (hfdi != NULL)
268 {
269 g_hfdi = hfdi;
270 g_szCabFile = szCabFile;
271 g_szExtractDir = szExtractDir;
272 char szEmpty[1] = {0};
273 if (FDICopy(hfdi, szEmpty, szEmpty, 0, CabNotification, NULL, NULL))
274 {
275 FDIDestroy(hfdi);
276 return 0;
277 }
278 FDIDestroy(hfdi);
279 }
280
281 return erf.erfOper;
282}
diff --git a/src/samples/Dtf/Tools/SfxCA/RemoteMsi.cpp b/src/samples/Dtf/Tools/SfxCA/RemoteMsi.cpp
deleted file mode 100644
index ba59fdf7..00000000
--- a/src/samples/Dtf/Tools/SfxCA/RemoteMsi.cpp
+++ /dev/null
@@ -1,629 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include "RemoteMsiSession.h"
5
6
7//
8// Ensures that the request buffer is large enough to hold a request,
9// reallocating the buffer if necessary.
10// It will also reduce the buffer size if the previous allocation was very large.
11//
12static __success(return == 0) UINT EnsureBufSize(__deref_out_ecount(*pcchBuf) wchar_t** pszBuf, __deref_inout DWORD* pcchBuf, DWORD cchRequired)
13{
14 // It will also reduce the buffer size if the previous allocation was very large.
15 if (*pcchBuf < cchRequired || (LARGE_BUFFER_THRESHOLD/2 < *pcchBuf && cchRequired < *pcchBuf))
16 {
17 if (*pszBuf != NULL)
18 {
19 SecureZeroMemory(*pszBuf, *pcchBuf);
20 delete[] *pszBuf;
21 }
22
23 *pcchBuf = max(MIN_BUFFER_STRING_SIZE, cchRequired);
24 *pszBuf = new wchar_t[*pcchBuf];
25
26 if (*pszBuf == NULL)
27 {
28 return ERROR_OUTOFMEMORY;
29 }
30 }
31
32 return ERROR_SUCCESS;
33}
34
35typedef int (WINAPI *PMsiFunc_I_I)(int in1, __out int* out1);
36typedef int (WINAPI *PMsiFunc_II_I)(int in1, int in2, __out int* out1);
37typedef int (WINAPI *PMsiFunc_IS_I)(int in1, __in_z wchar_t* in2, __out int* out1);
38typedef int (WINAPI *PMsiFunc_ISI_I)(int in1, __in_z wchar_t* in2, int in3, __out int* out1);
39typedef int (WINAPI *PMsiFunc_ISII_I)(int in1, __in_z wchar_t* in2, int in3, int in4, __out int* out1);
40typedef int (WINAPI *PMsiFunc_IS_II)(int in1, __in_z wchar_t* in2, __out int* out1, __out int* out2);
41typedef MSIDBERROR (WINAPI *PMsiEFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
42typedef int (WINAPI *PMsiFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
43typedef int (WINAPI *PMsiFunc_II_S)(int in1, int in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
44typedef int (WINAPI *PMsiFunc_IS_S)(int in1, __in_z wchar_t* in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
45typedef int (WINAPI *PMsiFunc_ISII_SII)(int in1, __in_z wchar_t* in2, int in3, int in4, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1, __out int* out2, __out int* out3);
46
47UINT MsiFunc_I_I(PMsiFunc_I_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
48{
49 int in1 = pReq->fields[0].iValue;
50 int out1;
51 UINT ret = (UINT) func(in1, &out1);
52 if (ret == 0)
53 {
54 pResp->fields[1].vt = VT_I4;
55 pResp->fields[1].iValue = out1;
56 }
57 return ret;
58}
59
60UINT MsiFunc_II_I(PMsiFunc_II_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
61{
62 int in1 = pReq->fields[0].iValue;
63 int in2 = pReq->fields[1].iValue;
64 int out1;
65 UINT ret = (UINT) func(in1, in2, &out1);
66 if (ret == 0)
67 {
68 pResp->fields[1].vt = VT_I4;
69 pResp->fields[1].iValue = out1;
70 }
71 return ret;
72}
73
74UINT MsiFunc_IS_I(PMsiFunc_IS_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
75{
76 int in1 = pReq->fields[0].iValue;
77 wchar_t* in2 = pReq->fields[1].szValue;
78 int out1;
79 UINT ret = (UINT) func(in1, in2, &out1);
80 if (ret == 0)
81 {
82 pResp->fields[1].vt = VT_I4;
83 pResp->fields[1].iValue = out1;
84 }
85 return ret;
86}
87
88UINT MsiFunc_ISI_I(PMsiFunc_ISI_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
89{
90 int in1 = pReq->fields[0].iValue;
91 wchar_t* in2 = pReq->fields[1].szValue;
92 int in3 = pReq->fields[2].iValue;
93 int out1;
94 UINT ret = (UINT) func(in1, in2, in3, &out1);
95 if (ret == 0)
96 {
97 pResp->fields[1].vt = VT_I4;
98 pResp->fields[1].iValue = out1;
99 }
100 return ret;
101}
102
103UINT MsiFunc_ISII_I(PMsiFunc_ISII_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
104{
105 int in1 = pReq->fields[0].iValue;
106 wchar_t* in2 = pReq->fields[1].szValue;
107 int in3 = pReq->fields[2].iValue;
108 int in4 = pReq->fields[3].iValue;
109 int out1;
110 UINT ret = (UINT) func(in1, in2, in3, in4, &out1);
111 if (ret == 0)
112 {
113 pResp->fields[1].vt = VT_I4;
114 pResp->fields[1].iValue = out1;
115 }
116 return ret;
117}
118
119UINT MsiFunc_IS_II(PMsiFunc_IS_II func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp)
120{
121 int in1 = pReq->fields[0].iValue;
122 wchar_t* in2 = pReq->fields[1].szValue;
123 int out1, out2;
124 UINT ret = (UINT) func(in1, in2, &out1, &out2);
125 if (ret == 0)
126 {
127 pResp->fields[1].vt = VT_I4;
128 pResp->fields[1].iValue = out1;
129 pResp->fields[2].vt = VT_I4;
130 pResp->fields[2].iValue = out2;
131 }
132 return ret;
133}
134
135UINT MsiFunc_I_S(PMsiFunc_I_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf)
136{
137 int in1 = pReq->fields[0].iValue;
138 szBuf[0] = L'\0';
139 DWORD cchValue = cchBuf;
140 UINT ret = (UINT) func(in1, szBuf, &cchValue);
141 if (ret == ERROR_MORE_DATA)
142 {
143 ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue);
144 if (ret == 0)
145 {
146 ret = (UINT) func(in1, szBuf, &cchValue);
147 }
148 }
149 if (ret == 0)
150 {
151 pResp->fields[1].vt = VT_LPWSTR;
152 pResp->fields[1].szValue = szBuf;
153 }
154 return ret;
155}
156
157MSIDBERROR MsiEFunc_I_S(PMsiEFunc_I_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf)
158{
159 int in1 = pReq->fields[0].iValue;
160 szBuf[0] = L'\0';
161 DWORD cchValue = cchBuf;
162 MSIDBERROR ret = func(in1, szBuf, &cchValue);
163 if (ret == MSIDBERROR_MOREDATA)
164 {
165 if (0 == EnsureBufSize(&szBuf, &cchBuf, ++cchValue))
166 {
167 ret = func(in1, szBuf, &cchValue);
168 }
169 }
170 if (ret != MSIDBERROR_MOREDATA)
171 {
172 pResp->fields[1].vt = VT_LPWSTR;
173 pResp->fields[1].szValue = szBuf;
174 }
175 return ret;
176}
177
178UINT MsiFunc_II_S(PMsiFunc_II_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf)
179{
180 int in1 = pReq->fields[0].iValue;
181 int in2 = pReq->fields[1].iValue;
182 szBuf[0] = L'\0';
183 DWORD cchValue = cchBuf;
184 UINT ret = (UINT) func(in1, in2, szBuf, &cchValue);
185 if (ret == ERROR_MORE_DATA)
186 {
187 ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue);
188 if (ret == 0)
189 {
190 ret = (UINT) func(in1, in2, szBuf, &cchValue);
191 }
192 }
193 if (ret == 0)
194 {
195 pResp->fields[1].vt = VT_LPWSTR;
196 pResp->fields[1].szValue = szBuf;
197 }
198 return ret;
199}
200
201UINT MsiFunc_IS_S(PMsiFunc_IS_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf)
202{
203 int in1 = pReq->fields[0].iValue;
204 wchar_t* in2 = pReq->fields[1].szValue;
205 szBuf[0] = L'\0';
206 DWORD cchValue = cchBuf;
207 UINT ret = (UINT) func(in1, in2, szBuf, &cchValue);
208 if (ret == ERROR_MORE_DATA)
209 {
210 ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue);
211 if (ret == 0)
212 {
213 ret = (UINT) func(in1, in2, szBuf, &cchValue);
214 }
215 }
216 if (ret == 0)
217 {
218 pResp->fields[1].vt = VT_LPWSTR;
219 pResp->fields[1].szValue = szBuf;
220 }
221 return ret;
222}
223
224UINT MsiFunc_ISII_SII(PMsiFunc_ISII_SII func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf)
225{
226 int in1 = pReq->fields[0].iValue;
227 wchar_t* in2 = pReq->fields[1].szValue;
228 int in3 = pReq->fields[2].iValue;
229 int in4 = pReq->fields[3].iValue;
230 szBuf[0] = L'\0';
231 DWORD cchValue = cchBuf;
232 int out2, out3;
233 UINT ret = (UINT) func(in1, in2, in3, in4, szBuf, &cchValue, &out2, &out3);
234 if (ret == ERROR_MORE_DATA)
235 {
236 ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue);
237 if (ret == 0)
238 {
239 ret = (UINT) func(in1, in2, in3, in4, szBuf, &cchValue, &out2, &out3);
240 }
241 }
242 if (ret == 0)
243 {
244 pResp->fields[1].vt = VT_LPWSTR;
245 pResp->fields[1].szValue = szBuf;
246 pResp->fields[2].vt = VT_I4;
247 pResp->fields[2].iValue = out2;
248 pResp->fields[3].vt = VT_I4;
249 pResp->fields[3].iValue = out3;
250 }
251 return ret;
252}
253
254void RemoteMsiSession::ProcessRequest(RequestId id, const RequestData* pReq, RequestData* pResp)
255{
256 SecureZeroMemory(pResp, sizeof(RequestData));
257
258 UINT ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, 1024);
259
260 if (0 == ret)
261 {
262 switch (id)
263 {
264 case RemoteMsiSession::EndSession:
265 {
266 this->ExitCode = pReq->fields[0].iValue;
267 }
268 break;
269 case RemoteMsiSession::MsiCloseHandle:
270 {
271 MSIHANDLE h = (MSIHANDLE) pReq->fields[0].iValue;
272 ret = ::MsiCloseHandle(h);
273 }
274 break;
275 case RemoteMsiSession::MsiProcessMessage:
276 {
277 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
278 INSTALLMESSAGE eMessageType = (INSTALLMESSAGE) pReq->fields[1].iValue;
279 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[2].iValue;
280 ret = ::MsiProcessMessage(hInstall, eMessageType, hRecord);
281 }
282 break;
283 case RemoteMsiSession::MsiGetProperty:
284 {
285 ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetProperty, pReq, pResp, m_pBufSend, m_cbBufSend);
286 }
287 break;
288 case RemoteMsiSession::MsiSetProperty:
289 {
290 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
291 const wchar_t* szName = pReq->fields[1].szValue;
292 const wchar_t* szValue = pReq->fields[2].szValue;
293 ret = ::MsiSetProperty(hInstall, szName, szValue);
294 }
295 break;
296 case RemoteMsiSession::MsiCreateRecord:
297 {
298 UINT cParams = pReq->fields[0].uiValue;
299 ret = ::MsiCreateRecord(cParams);
300 }
301 break;
302 case RemoteMsiSession::MsiRecordGetFieldCount:
303 {
304 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
305 ret = ::MsiRecordGetFieldCount(hRecord);
306 }
307 break;
308 case RemoteMsiSession::MsiRecordGetInteger:
309 {
310 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
311 UINT iField = pReq->fields[1].uiValue;
312 ret = ::MsiRecordGetInteger(hRecord, iField);
313 }
314 break;
315 case RemoteMsiSession::MsiRecordSetInteger:
316 {
317 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
318 UINT iField = pReq->fields[1].uiValue;
319 int iValue = pReq->fields[2].iValue;
320 ret = ::MsiRecordSetInteger(hRecord, iField, iValue);
321 }
322 break;
323 case RemoteMsiSession::MsiRecordGetString:
324 {
325 ret = MsiFunc_II_S((PMsiFunc_II_S) ::MsiRecordGetString, pReq, pResp, m_pBufSend, m_cbBufSend);
326 }
327 break;
328 case RemoteMsiSession::MsiRecordSetString:
329 {
330 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
331 UINT iField = pReq->fields[1].uiValue;
332 const wchar_t* szValue = pReq->fields[2].szValue;
333 ret = ::MsiRecordSetString(hRecord, iField, szValue);
334 }
335 break;
336 case RemoteMsiSession::MsiRecordClearData:
337 {
338 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
339 ret = ::MsiRecordClearData(hRecord);
340 }
341 break;
342 case RemoteMsiSession::MsiRecordIsNull:
343 {
344 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
345 UINT iField = pReq->fields[1].uiValue;
346 ret = ::MsiRecordIsNull(hRecord, iField);
347 }
348 break;
349 case RemoteMsiSession::MsiFormatRecord:
350 {
351 ret = MsiFunc_II_S((PMsiFunc_II_S) ::MsiFormatRecord, pReq, pResp, m_pBufSend, m_cbBufSend);
352 }
353 break;
354 case RemoteMsiSession::MsiGetActiveDatabase:
355 {
356 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
357 ret = (UINT) ::MsiGetActiveDatabase(hInstall);
358 }
359 break;
360 case RemoteMsiSession::MsiDatabaseOpenView:
361 {
362 ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiDatabaseOpenView, pReq, pResp);
363 }
364 break;
365 case RemoteMsiSession::MsiViewExecute:
366 {
367 MSIHANDLE hView = (MSIHANDLE) pReq->fields[0].iValue;
368 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[1].iValue;
369 ret = ::MsiViewExecute(hView, hRecord);
370 }
371 break;
372 case RemoteMsiSession::MsiViewFetch:
373 {
374 ret = MsiFunc_I_I((PMsiFunc_I_I) ::MsiViewFetch, pReq, pResp);
375 }
376 break;
377 case RemoteMsiSession::MsiViewModify:
378 {
379 MSIHANDLE hView = (MSIHANDLE) pReq->fields[0].iValue;
380 MSIMODIFY eModifyMode = (MSIMODIFY) pReq->fields[1].iValue;
381 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[2].iValue;
382 ret = ::MsiViewModify(hView, eModifyMode, hRecord);
383 }
384 break;
385 case RemoteMsiSession::MsiViewGetError:
386 {
387 ret = MsiEFunc_I_S((PMsiEFunc_I_S) ::MsiViewGetError, pReq, pResp, m_pBufSend, m_cbBufSend);
388 }
389 break;
390 case RemoteMsiSession::MsiViewGetColumnInfo:
391 {
392 ret = MsiFunc_II_I((PMsiFunc_II_I) ::MsiViewGetColumnInfo, pReq, pResp);
393 }
394 break;
395 case RemoteMsiSession::MsiDatabaseGetPrimaryKeys:
396 {
397 ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiDatabaseGetPrimaryKeys, pReq, pResp);
398 }
399 break;
400 case RemoteMsiSession::MsiDatabaseIsTablePersistent:
401 {
402 MSIHANDLE hDb = (MSIHANDLE) pReq->fields[0].iValue;
403 const wchar_t* szTable = pReq->fields[1].szValue;
404 ret = ::MsiDatabaseIsTablePersistent(hDb, szTable);
405 }
406 break;
407 case RemoteMsiSession::MsiDoAction:
408 {
409 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
410 const wchar_t* szAction = pReq->fields[1].szValue;
411 ret = ::MsiDoAction(hInstall, szAction);
412 }
413 break;
414 case RemoteMsiSession::MsiEnumComponentCosts:
415 {
416 ret = MsiFunc_ISII_SII((PMsiFunc_ISII_SII) ::MsiEnumComponentCosts, pReq, pResp, m_pBufSend, m_cbBufSend);
417 }
418 break;
419 case RemoteMsiSession::MsiEvaluateCondition:
420 {
421 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
422 const wchar_t* szCondition = pReq->fields[1].szValue;
423 ret = ::MsiEvaluateCondition(hInstall, szCondition);
424 }
425 break;
426 case RemoteMsiSession::MsiGetComponentState:
427 {
428 ret = MsiFunc_IS_II((PMsiFunc_IS_II) ::MsiGetComponentState, pReq, pResp);
429 }
430 break;
431 case RemoteMsiSession::MsiGetFeatureCost:
432 {
433 ret = MsiFunc_ISII_I((PMsiFunc_ISII_I) ::MsiGetFeatureCost, pReq, pResp);
434 }
435 break;
436 case RemoteMsiSession::MsiGetFeatureState:
437 {
438 ret = MsiFunc_IS_II((PMsiFunc_IS_II) ::MsiGetFeatureState, pReq, pResp);
439 }
440 break;
441 case RemoteMsiSession::MsiGetFeatureValidStates:
442 {
443 ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiGetFeatureValidStates, pReq, pResp);
444 }
445 break;
446 case RemoteMsiSession::MsiGetLanguage:
447 {
448 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
449 ret = ::MsiGetLanguage(hInstall);
450 }
451 break;
452 case RemoteMsiSession::MsiGetLastErrorRecord:
453 {
454 ret = ::MsiGetLastErrorRecord();
455 }
456 break;
457 case RemoteMsiSession::MsiGetMode:
458 {
459 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
460 MSIRUNMODE iRunMode = (MSIRUNMODE) pReq->fields[1].iValue;
461 ret = ::MsiGetMode(hInstall, iRunMode);
462 }
463 break;
464 case RemoteMsiSession::MsiGetSourcePath:
465 {
466 ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetSourcePath, pReq, pResp, m_pBufSend, m_cbBufSend);
467 }
468 break;
469 case RemoteMsiSession::MsiGetSummaryInformation:
470 {
471 ret = MsiFunc_ISI_I((PMsiFunc_ISI_I) ::MsiGetSummaryInformation, pReq, pResp);
472 }
473 break;
474 case RemoteMsiSession::MsiGetTargetPath:
475 {
476 ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetTargetPath, pReq, pResp, m_pBufSend, m_cbBufSend);
477 }
478 break;
479 case RemoteMsiSession::MsiRecordDataSize:
480 {
481 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
482 UINT iField = pReq->fields[1].uiValue;
483 ret = ::MsiRecordDataSize(hRecord, iField);
484 }
485 break;
486 case RemoteMsiSession::MsiRecordReadStream:
487 {
488 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
489 UINT iField = pReq->fields[1].uiValue;
490 DWORD cbRead = (DWORD) pReq->fields[2].uiValue;
491 ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, (cbRead + 1) / 2);
492 if (ret == 0)
493 {
494 ret = ::MsiRecordReadStream(hRecord, iField, (char*) m_pBufSend, &cbRead);
495 if (ret == 0)
496 {
497 pResp->fields[1].vt = VT_STREAM;
498 pResp->fields[1].szValue = m_pBufSend;
499 pResp->fields[2].vt = VT_I4;
500 pResp->fields[2].uiValue = (UINT) cbRead;
501 }
502 }
503 }
504 break;
505 case RemoteMsiSession::MsiRecordSetStream:
506 {
507 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
508 UINT iField = pReq->fields[1].uiValue;
509 const wchar_t* szFilePath = pReq->fields[2].szValue;
510 ret = ::MsiRecordSetStream(hRecord, iField, szFilePath);
511 }
512 break;
513 case RemoteMsiSession::MsiSequence:
514 {
515 MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue;
516 const wchar_t* szTable = pReq->fields[1].szValue;
517 UINT iSequenceMode = pReq->fields[2].uiValue;
518 ret = ::MsiSequence(hRecord, szTable, iSequenceMode);
519 }
520 break;
521 case RemoteMsiSession::MsiSetComponentState:
522 {
523 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
524 const wchar_t* szComponent = pReq->fields[1].szValue;
525 INSTALLSTATE iState = (INSTALLSTATE) pReq->fields[2].iValue;
526 ret = ::MsiSetComponentState(hInstall, szComponent, iState);
527 }
528 break;
529 case RemoteMsiSession::MsiSetFeatureAttributes:
530 {
531 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
532 const wchar_t* szFeature = pReq->fields[1].szValue;
533 DWORD dwAttrs = (DWORD) pReq->fields[2].uiValue;
534 ret = ::MsiSetFeatureAttributes(hInstall, szFeature, dwAttrs);
535 }
536 break;
537 case RemoteMsiSession::MsiSetFeatureState:
538 {
539 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
540 const wchar_t* szFeature = pReq->fields[1].szValue;
541 INSTALLSTATE iState = (INSTALLSTATE) pReq->fields[2].iValue;
542 ret = ::MsiSetFeatureState(hInstall, szFeature, iState);
543 }
544 break;
545 case RemoteMsiSession::MsiSetInstallLevel:
546 {
547 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
548 int iInstallLevel = pReq->fields[1].iValue;
549 ret = ::MsiSetInstallLevel(hInstall, iInstallLevel);
550 }
551 break;
552 case RemoteMsiSession::MsiSetMode:
553 {
554 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
555 MSIRUNMODE iRunMode = (MSIRUNMODE) pReq->fields[1].uiValue;
556 BOOL fState = (BOOL) pReq->fields[2].iValue;
557 ret = ::MsiSetMode(hInstall, iRunMode, fState);
558 }
559 break;
560 case RemoteMsiSession::MsiSetTargetPath:
561 {
562 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
563 const wchar_t* szFolder = pReq->fields[1].szValue;
564 const wchar_t* szFolderPath = pReq->fields[2].szValue;
565 ret = ::MsiSetTargetPath(hInstall, szFolder, szFolderPath);
566 }
567 break;
568 case RemoteMsiSession::MsiSummaryInfoGetProperty:
569 {
570 MSIHANDLE hSummaryInfo = (MSIHANDLE) pReq->fields[0].iValue;
571 UINT uiProperty = pReq->fields[1].uiValue;
572 UINT uiDataType;
573 int iValue;
574 FILETIME ftValue;
575 m_pBufSend[0] = L'\0';
576 DWORD cchValue = m_cbBufSend;
577 ret = ::MsiSummaryInfoGetProperty(hSummaryInfo, uiProperty, &uiDataType, &iValue, &ftValue, m_pBufSend, &cchValue);
578 if (ret == ERROR_MORE_DATA)
579 {
580 ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, ++cchValue);
581 if (ret == 0)
582 {
583 ret = ::MsiSummaryInfoGetProperty(hSummaryInfo, uiProperty, &uiDataType, &iValue, &ftValue, m_pBufSend, &cchValue);
584 }
585 }
586 if (ret == 0)
587 {
588 pResp->fields[1].vt = VT_UI4;
589 pResp->fields[1].uiValue = uiDataType;
590
591 switch (uiDataType)
592 {
593 case VT_I2:
594 case VT_I4:
595 pResp->fields[2].vt = VT_I4;
596 pResp->fields[2].iValue = iValue;
597 break;
598 case VT_FILETIME:
599 pResp->fields[2].vt = VT_UI4;
600 pResp->fields[2].iValue = ftValue.dwHighDateTime;
601 pResp->fields[3].vt = VT_UI4;
602 pResp->fields[3].iValue = ftValue.dwLowDateTime;
603 break;
604 case VT_LPSTR:
605 pResp->fields[2].vt = VT_LPWSTR;
606 pResp->fields[2].szValue = m_pBufSend;
607 break;
608 }
609 }
610 }
611 break;
612 case RemoteMsiSession::MsiVerifyDiskSpace:
613 {
614 MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue;
615 ret = ::MsiVerifyDiskSpace(hInstall);
616 }
617 break;
618
619 default:
620 {
621 ret = ERROR_INVALID_FUNCTION;
622 }
623 break;
624 }
625 }
626
627 pResp->fields[0].vt = VT_UI4;
628 pResp->fields[0].uiValue = ret;
629}
diff --git a/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h b/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h
deleted file mode 100644
index 90c7c01f..00000000
--- a/src/samples/Dtf/Tools/SfxCA/RemoteMsiSession.h
+++ /dev/null
@@ -1,898 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#define LARGE_BUFFER_THRESHOLD 65536 // bytes
4#define MIN_BUFFER_STRING_SIZE 1024 // wchar_ts
5
6///////////////////////////////////////////////////////////////////////////////////////
7// RemoteMsiSession //
8//////////////////////
9//
10// Allows accessing MSI APIs from another process using named pipes.
11//
12class RemoteMsiSession
13{
14public:
15
16 // This enumeration MUST stay in sync with the
17 // managed equivalent in RemotableNativeMethods.cs!
18 enum RequestId
19 {
20 EndSession = 0,
21 MsiCloseHandle,
22 MsiCreateRecord,
23 MsiDatabaseGetPrimaryKeys,
24 MsiDatabaseIsTablePersistent,
25 MsiDatabaseOpenView,
26 MsiDoAction,
27 MsiEnumComponentCosts,
28 MsiEvaluateCondition,
29 MsiFormatRecord,
30 MsiGetActiveDatabase,
31 MsiGetComponentState,
32 MsiGetFeatureCost,
33 MsiGetFeatureState,
34 MsiGetFeatureValidStates,
35 MsiGetLanguage,
36 MsiGetLastErrorRecord,
37 MsiGetMode,
38 MsiGetProperty,
39 MsiGetSourcePath,
40 MsiGetSummaryInformation,
41 MsiGetTargetPath,
42 MsiProcessMessage,
43 MsiRecordClearData,
44 MsiRecordDataSize,
45 MsiRecordGetFieldCount,
46 MsiRecordGetInteger,
47 MsiRecordGetString,
48 MsiRecordIsNull,
49 MsiRecordReadStream,
50 MsiRecordSetInteger,
51 MsiRecordSetStream,
52 MsiRecordSetString,
53 MsiSequence,
54 MsiSetComponentState,
55 MsiSetFeatureAttributes,
56 MsiSetFeatureState,
57 MsiSetInstallLevel,
58 MsiSetMode,
59 MsiSetProperty,
60 MsiSetTargetPath,
61 MsiSummaryInfoGetProperty,
62 MsiVerifyDiskSpace,
63 MsiViewExecute,
64 MsiViewFetch,
65 MsiViewGetError,
66 MsiViewGetColumnInfo,
67 MsiViewModify,
68 };
69
70 static const int MAX_REQUEST_FIELDS = 4;
71
72 // Used to pass data back and forth for remote API calls,
73 // including in & out params & return values.
74 // Only strings and ints are supported.
75 struct RequestData
76 {
77 struct
78 {
79 VARENUM vt;
80 union {
81 int iValue;
82 UINT uiValue;
83 DWORD cchValue;
84 LPWSTR szValue;
85 BYTE* sValue;
86 DWORD cbValue;
87 };
88 } fields[MAX_REQUEST_FIELDS];
89 };
90
91public:
92
93 // This value is set from the single data parameter in the EndSession request.
94 // It saves the exit code of the out-of-proc custom action.
95 int ExitCode;
96
97 /////////////////////////////////////////////////////////////////////////////////////
98 // RemoteMsiSession constructor
99 //
100 // Creates a new remote session instance, for use either by the server
101 // or client process.
102 //
103 // szName - Identifies the session instance being remoted. The server and
104 // the client must use the same name. The name should be unique
105 // enough to avoid conflicting with other instances on the system.
106 //
107 // fServer - True if the calling process is the server process, false if the
108 // calling process is the client process.
109 //
110 RemoteMsiSession(const wchar_t* szName, bool fServer=true)
111 : m_fServer(fServer),
112 m_szName(szName != NULL && szName[0] != L'\0' ? szName : L"RemoteMsiSession"),
113 m_szPipeName(NULL),
114 m_hPipe(NULL),
115 m_fConnecting(false),
116 m_fConnected(false),
117 m_hReceiveThread(NULL),
118 m_hReceiveStopEvent(NULL),
119 m_pBufReceive(NULL),
120 m_cbBufReceive(0),
121 m_pBufSend(NULL),
122 m_cbBufSend(0),
123 ExitCode(ERROR_INSTALL_FAILURE)
124 {
125 SecureZeroMemory(&m_overlapped, sizeof(OVERLAPPED));
126 m_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
127 }
128
129 /////////////////////////////////////////////////////////////////////////////////////
130 // RemoteMsiSession destructor
131 //
132 // Closes any open handles and frees any allocated memory.
133 //
134 ~RemoteMsiSession()
135 {
136 WaitExitCode();
137 if (m_hPipe != NULL)
138 {
139 CloseHandle(m_hPipe);
140 m_hPipe = NULL;
141 }
142 if (m_overlapped.hEvent != NULL)
143 {
144 CloseHandle(m_overlapped.hEvent);
145 m_overlapped.hEvent = NULL;
146 }
147 if (m_szPipeName != NULL)
148 {
149 delete[] m_szPipeName;
150 m_szPipeName = NULL;
151 }
152 if (m_pBufReceive != NULL)
153 {
154 SecureZeroMemory(m_pBufReceive, m_cbBufReceive);
155 delete[] m_pBufReceive;
156 m_pBufReceive = NULL;
157 }
158 if (m_pBufSend != NULL)
159 {
160 SecureZeroMemory(m_pBufSend, m_cbBufSend);
161 delete[] m_pBufSend;
162 m_pBufSend = NULL;
163 }
164 m_fConnecting = false;
165 m_fConnected = false;
166 }
167
168 /////////////////////////////////////////////////////////////////////////////////////
169 // RemoteMsiSession::WaitExitCode()
170 //
171 // Waits for the server processing thread to complete.
172 //
173 void WaitExitCode()
174 {
175 if (m_hReceiveThread != NULL)
176 {
177 SetEvent(m_hReceiveStopEvent);
178 WaitForSingleObject(m_hReceiveThread, INFINITE);
179 CloseHandle(m_hReceiveThread);
180 m_hReceiveThread = NULL;
181 }
182 }
183
184 /////////////////////////////////////////////////////////////////////////////////////
185 // RemoteMsiSession::Connect()
186 //
187 // Connects the inter-process communication channel.
188 // (Currently implemented as a named pipe.)
189 //
190 // This method must be called first by the server process, then by the client
191 // process. The method does not block; the server will asynchronously wait
192 // for the client process to make the connection.
193 //
194 // Returns: 0 on success, Win32 error code on failure.
195 //
196 virtual DWORD Connect()
197 {
198 const wchar_t* szPipePrefix = L"\\\\.\\pipe\\";
199 size_t cchPipeNameBuf = wcslen(szPipePrefix) + wcslen(m_szName) + 1;
200 m_szPipeName = new wchar_t[cchPipeNameBuf];
201
202 if (m_szPipeName == NULL)
203 {
204 return ERROR_OUTOFMEMORY;
205 }
206 else
207 {
208 wcscpy_s(m_szPipeName, cchPipeNameBuf, szPipePrefix);
209 wcscat_s(m_szPipeName, cchPipeNameBuf, m_szName);
210
211 if (m_fServer)
212 {
213 return this->ConnectPipeServer();
214 }
215 else
216 {
217 return this->ConnectPipeClient();
218 }
219 }
220 }
221
222 /////////////////////////////////////////////////////////////////////////////////////
223 // RemoteMsiSession::IsConnected()
224 //
225 // Checks if the server process and client process are currently connected.
226 //
227 virtual bool IsConnected() const
228 {
229 return m_fConnected;
230 }
231
232 /////////////////////////////////////////////////////////////////////////////////////
233 // RemoteMsiSession::ProcessRequests()
234 //
235 // For use by the service process. Watches for requests in the input buffer and calls
236 // the callback for each one.
237 //
238 // This method does not block; it spawns a separate thread to do the work.
239 //
240 // Returns: 0 on success, Win32 error code on failure.
241 //
242 virtual DWORD ProcessRequests()
243 {
244 return this->StartProcessingReqests();
245 }
246
247 /////////////////////////////////////////////////////////////////////////////////////
248 // RemoteMsiSession::SendRequest()
249 //
250 // For use by the client process. Sends a request to the server and
251 // synchronously waits on a response, up to the timeout value.
252 //
253 // id - ID code of the MSI API call being requested.
254 //
255 // pRequest - Pointer to a data structure containing request parameters.
256 //
257 // ppResponse - [OUT] Pointer to a location that receives the response parameters.
258 //
259 // Returns: 0 on success, Win32 error code on failure.
260 // Returns WAIT_TIMEOUT if no response was received in time.
261 //
262 virtual DWORD SendRequest(RequestId id, const RequestData* pRequest, RequestData** ppResponse)
263 {
264 if (m_fServer)
265 {
266 return ERROR_INVALID_OPERATION;
267 }
268
269 if (!m_fConnected)
270 {
271 *ppResponse = NULL;
272 return 0;
273 }
274
275 DWORD dwRet = this->SendRequest(id, pRequest);
276 if (dwRet != 0)
277 {
278 return dwRet;
279 }
280
281 if (id != EndSession)
282 {
283 static RequestData response;
284 if (ppResponse != NULL)
285 {
286 *ppResponse = &response;
287 }
288
289 return this->ReceiveResponse(id, &response);
290 }
291 else
292 {
293 CloseHandle(m_hPipe);
294 m_hPipe = NULL;
295 m_fConnected = false;
296 return 0;
297 }
298 }
299
300private:
301
302 //
303 // Do not allow assignment.
304 //
305 RemoteMsiSession& operator=(const RemoteMsiSession&);
306
307 //
308 // Called only by the server process.
309 // Create a new thread to handle receiving requests.
310 //
311 DWORD StartProcessingReqests()
312 {
313 if (!m_fServer || m_hReceiveStopEvent != NULL)
314 {
315 return ERROR_INVALID_OPERATION;
316 }
317
318 DWORD dwRet = 0;
319
320 m_hReceiveStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
321
322 if (m_hReceiveStopEvent == NULL)
323 {
324 dwRet = GetLastError();
325 }
326 else
327 {
328 if (m_hReceiveThread != NULL)
329 {
330 CloseHandle(m_hReceiveThread);
331 }
332
333 m_hReceiveThread = CreateThread(NULL, 0,
334 RemoteMsiSession::ProcessRequestsThreadStatic, this, 0, NULL);
335
336 if (m_hReceiveThread == NULL)
337 {
338 dwRet = GetLastError();
339 CloseHandle(m_hReceiveStopEvent);
340 m_hReceiveStopEvent = NULL;
341 }
342 }
343
344 return dwRet;
345 }
346
347 //
348 // Called only by the watcher process.
349 // First verify the connection is complete. Then continually read and parse messages,
350 // invoke the callback, and send the replies.
351 //
352 static DWORD WINAPI ProcessRequestsThreadStatic(void* pv)
353 {
354 return reinterpret_cast<RemoteMsiSession*>(pv)->ProcessRequestsThread();
355 }
356
357 DWORD ProcessRequestsThread()
358 {
359 DWORD dwRet;
360
361 dwRet = CompleteConnection();
362 if (dwRet != 0)
363 {
364 if (dwRet == ERROR_OPERATION_ABORTED) dwRet = 0;
365 }
366
367 while (m_fConnected)
368 {
369 RequestId id;
370 RequestData req;
371 dwRet = ReceiveRequest(&id, &req);
372 if (dwRet != 0)
373 {
374 if (dwRet == ERROR_OPERATION_ABORTED ||
375 dwRet == ERROR_BROKEN_PIPE || dwRet == ERROR_NO_DATA)
376 {
377 dwRet = 0;
378 }
379 }
380 else
381 {
382 RequestData resp;
383 ProcessRequest(id, &req, &resp);
384
385 if (id == EndSession)
386 {
387 break;
388 }
389
390 dwRet = SendResponse(id, &resp);
391 if (dwRet != 0 && dwRet != ERROR_BROKEN_PIPE && dwRet != ERROR_NO_DATA)
392 {
393 dwRet = 0;
394 }
395 }
396 }
397
398 CloseHandle(m_hReceiveStopEvent);
399 m_hReceiveStopEvent = NULL;
400 return dwRet;
401 }
402
403 //
404 // Called only by the server process's receive thread.
405 // Read one request into a RequestData object.
406 //
407 DWORD ReceiveRequest(RequestId* pId, RequestData* pReq)
408 {
409 DWORD dwRet = this->ReadPipe((BYTE*) pId, sizeof(RequestId));
410
411 if (dwRet == 0)
412 {
413 dwRet = this->ReadRequestData(pReq);
414 }
415
416 return dwRet;
417 }
418
419 //
420 // Called by the server process's receive thread or the client's request call
421 // to read the response. Read data from the pipe, allowing interruption by the
422 // stop event if on the server.
423 //
424 DWORD ReadPipe(__out_bcount(cbRead) BYTE* pBuf, DWORD cbRead)
425 {
426 DWORD dwRet = 0;
427 DWORD dwTotalBytesRead = 0;
428
429 while (dwRet == 0 && dwTotalBytesRead < cbRead)
430 {
431 DWORD dwBytesReadThisTime;
432 ResetEvent(m_overlapped.hEvent);
433 if (!ReadFile(m_hPipe, pBuf + dwTotalBytesRead, cbRead - dwTotalBytesRead, &dwBytesReadThisTime, &m_overlapped))
434 {
435 dwRet = GetLastError();
436 if (dwRet == ERROR_IO_PENDING)
437 {
438 if (m_fServer)
439 {
440 HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent };
441 dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE);
442 }
443 else
444 {
445 dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE);
446 }
447
448 if (dwRet == WAIT_OBJECT_0)
449 {
450 if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesReadThisTime, FALSE))
451 {
452 dwRet = GetLastError();
453 }
454 }
455 else if (dwRet == WAIT_FAILED)
456 {
457 dwRet = GetLastError();
458 }
459 else
460 {
461 dwRet = ERROR_OPERATION_ABORTED;
462 }
463 }
464 }
465
466 dwTotalBytesRead += dwBytesReadThisTime;
467 }
468
469 if (dwRet != 0)
470 {
471 if (m_fServer)
472 {
473 CancelIo(m_hPipe);
474 DisconnectNamedPipe(m_hPipe);
475 }
476 else
477 {
478 CloseHandle(m_hPipe);
479 m_hPipe = NULL;
480 }
481 m_fConnected = false;
482 }
483
484 return dwRet;
485 }
486
487 //
488 // Called only by the server process.
489 // Given a request, invoke the MSI API and return the response.
490 // This is implemented in RemoteMsi.cpp.
491 //
492 void ProcessRequest(RequestId id, const RequestData* pReq, RequestData* pResp);
493
494 //
495 // Called only by the client process.
496 // Send request data over the pipe.
497 //
498 DWORD SendRequest(RequestId id, const RequestData* pRequest)
499 {
500 DWORD dwRet = WriteRequestData(id, pRequest);
501
502 if (dwRet != 0)
503 {
504 m_fConnected = false;
505 CloseHandle(m_hPipe);
506 m_hPipe = NULL;
507 }
508
509 return dwRet;
510 }
511
512 //
513 // Called only by the server process.
514 // Just send a response over the pipe.
515 //
516 DWORD SendResponse(RequestId id, const RequestData* pResp)
517 {
518 DWORD dwRet = WriteRequestData(id, pResp);
519
520 if (dwRet != 0)
521 {
522 DisconnectNamedPipe(m_hPipe);
523 m_fConnected = false;
524 }
525
526 return dwRet;
527 }
528
529 //
530 // Called either by the client or server process.
531 // Writes data to the pipe for a request or response.
532 //
533 DWORD WriteRequestData(RequestId id, const RequestData* pReq)
534 {
535 DWORD dwRet = 0;
536
537 RequestData req = *pReq; // Make a copy because the const data can't be changed.
538
539 dwRet = this->WritePipe((const BYTE *)&id, sizeof(RequestId));
540 if (dwRet != 0)
541 {
542 return dwRet;
543 }
544
545 BYTE* sValues[MAX_REQUEST_FIELDS] = {0};
546 for (int i = 0; i < MAX_REQUEST_FIELDS; i++)
547 {
548 if (req.fields[i].vt == VT_LPWSTR)
549 {
550 sValues[i] = (BYTE*) req.fields[i].szValue;
551 req.fields[i].cchValue = (DWORD) wcslen(req.fields[i].szValue);
552 }
553 else if (req.fields[i].vt == VT_STREAM)
554 {
555 sValues[i] = req.fields[i].sValue;
556 req.fields[i].cbValue = (DWORD) req.fields[i + 1].uiValue;
557 }
558 }
559
560 dwRet = this->WritePipe((const BYTE *)&req, sizeof(RequestData));
561 if (dwRet != 0)
562 {
563 return dwRet;
564 }
565
566 for (int i = 0; i < MAX_REQUEST_FIELDS; i++)
567 {
568 if (sValues[i] != NULL)
569 {
570 DWORD cbValue;
571 if (req.fields[i].vt == VT_LPWSTR)
572 {
573 cbValue = (req.fields[i].cchValue + 1) * sizeof(WCHAR);
574 }
575 else
576 {
577 cbValue = req.fields[i].cbValue;
578 }
579
580 dwRet = this->WritePipe(const_cast<BYTE*> (sValues[i]), cbValue);
581 if (dwRet != 0)
582 {
583 break;
584 }
585 }
586 }
587
588 return dwRet;
589 }
590
591 //
592 // Called when writing a request or response. Writes data to
593 // the pipe, allowing interruption by the stop event if on the server.
594 //
595 DWORD WritePipe(const BYTE* pBuf, DWORD cbWrite)
596 {
597 DWORD dwRet = 0;
598 DWORD dwTotalBytesWritten = 0;
599
600 while (dwRet == 0 && dwTotalBytesWritten < cbWrite)
601 {
602 DWORD dwBytesWrittenThisTime;
603 ResetEvent(m_overlapped.hEvent);
604 if (!WriteFile(m_hPipe, pBuf + dwTotalBytesWritten, cbWrite - dwTotalBytesWritten, &dwBytesWrittenThisTime, &m_overlapped))
605 {
606 dwRet = GetLastError();
607 if (dwRet == ERROR_IO_PENDING)
608 {
609 if (m_fServer)
610 {
611 HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent };
612 dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE);
613 }
614 else
615 {
616 dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE);
617 }
618
619 if (dwRet == WAIT_OBJECT_0)
620 {
621 if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesWrittenThisTime, FALSE))
622 {
623 dwRet = GetLastError();
624 }
625 }
626 else if (dwRet == WAIT_FAILED)
627 {
628 dwRet = GetLastError();
629 }
630 else
631 {
632 dwRet = ERROR_OPERATION_ABORTED;
633 }
634 }
635 }
636
637 dwTotalBytesWritten += dwBytesWrittenThisTime;
638 }
639
640 return dwRet;
641 }
642
643 //
644 // Called either by the client or server process.
645 // Reads data from the pipe for a request or response.
646 //
647 DWORD ReadRequestData(RequestData* pReq)
648 {
649 DWORD dwRet = ReadPipe((BYTE*) pReq, sizeof(RequestData));
650
651 if (dwRet == 0)
652 {
653 DWORD cbData = 0;
654 for (int i = 0; i < MAX_REQUEST_FIELDS; i++)
655 {
656 if (pReq->fields[i].vt == VT_LPWSTR)
657 {
658 cbData += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR);
659 }
660 else if (pReq->fields[i].vt == VT_STREAM)
661 {
662 cbData += pReq->fields[i].cbValue;
663 }
664 }
665
666 if (cbData > 0)
667 {
668 if (!CheckRequestDataBuf(cbData))
669 {
670 return ERROR_OUTOFMEMORY;
671 }
672
673 dwRet = this->ReadPipe((BYTE*) m_pBufReceive, cbData);
674 if (dwRet == 0)
675 {
676 DWORD dwOffset = 0;
677 for (int i = 0; i < MAX_REQUEST_FIELDS; i++)
678 {
679 if (pReq->fields[i].vt == VT_LPWSTR)
680 {
681 LPWSTR szTemp = (LPWSTR) (m_pBufReceive + dwOffset);
682 dwOffset += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR);
683 pReq->fields[i].szValue = szTemp;
684 }
685 else if (pReq->fields[i].vt == VT_STREAM)
686 {
687 BYTE* sTemp = m_pBufReceive + dwOffset;
688 dwOffset += pReq->fields[i].cbValue;
689 pReq->fields[i].sValue = sTemp;
690 }
691 }
692 }
693 }
694 }
695
696 return dwRet;
697 }
698
699 //
700 // Called only by the client process.
701 // Wait for a response on the pipe. If no response is received before the timeout,
702 // then give up and close the connection.
703 //
704 DWORD ReceiveResponse(RequestId id, RequestData* pResp)
705 {
706 RequestId responseId;
707 DWORD dwRet = ReadPipe((BYTE*) &responseId, sizeof(RequestId));
708 if (dwRet == 0 && responseId != id)
709 {
710 dwRet = ERROR_OPERATION_ABORTED;
711 }
712
713 if (dwRet == 0)
714 {
715 dwRet = this->ReadRequestData(pResp);
716 }
717
718 return dwRet;
719 }
720
721 //
722 // Called only by the server process's receive thread.
723 // Try to complete and verify an asynchronous connection operation.
724 //
725 DWORD CompleteConnection()
726 {
727 DWORD dwRet = 0;
728 if (m_fConnecting)
729 {
730 HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent };
731 DWORD dwWaitRes = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE);
732
733 if (dwWaitRes == WAIT_OBJECT_0)
734 {
735 m_fConnecting = false;
736
737 DWORD dwUnused;
738 if (GetOverlappedResult(m_hPipe, &m_overlapped, &dwUnused, FALSE))
739 {
740 m_fConnected = true;
741 }
742 else
743 {
744 dwRet = GetLastError();
745 }
746 }
747 else if (dwWaitRes == WAIT_FAILED)
748 {
749 CancelIo(m_hPipe);
750 dwRet = GetLastError();
751 }
752 else
753 {
754 CancelIo(m_hPipe);
755 dwRet = ERROR_OPERATION_ABORTED;
756 }
757 }
758 return dwRet;
759 }
760
761 //
762 // Called only by the server process.
763 // Creates a named pipe instance and begins asynchronously waiting
764 // for a connection from the client process.
765 //
766 DWORD ConnectPipeServer()
767 {
768 DWORD dwRet = 0;
769 const int BUFSIZE = 1024; // Suggested pipe I/O buffer sizes
770 m_hPipe = CreateNamedPipe(
771 m_szPipeName,
772 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
773 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
774 1, BUFSIZE, BUFSIZE, 0, NULL);
775 if (m_hPipe == INVALID_HANDLE_VALUE)
776 {
777 m_hPipe = NULL;
778 dwRet = GetLastError();
779 }
780 else if (ConnectNamedPipe(m_hPipe, &m_overlapped))
781 {
782 m_fConnected = true;
783 }
784 else
785 {
786 dwRet = GetLastError();
787
788 if (dwRet == ERROR_PIPE_BUSY)
789 {
790 // All pipe instances are busy, so wait for a maximum of 20 seconds
791 dwRet = 0;
792 if (WaitNamedPipe(m_szPipeName, 20000))
793 {
794 m_fConnected = true;
795 }
796 else
797 {
798 dwRet = GetLastError();
799 }
800 }
801
802 if (dwRet == ERROR_IO_PENDING)
803 {
804 dwRet = 0;
805 m_fConnecting = true;
806 }
807 }
808 return dwRet;
809 }
810
811 //
812 // Called only by the client process.
813 // Attemps to open a connection to an existing named pipe instance
814 // which should have already been created by the server process.
815 //
816 DWORD ConnectPipeClient()
817 {
818 DWORD dwRet = 0;
819 m_hPipe = CreateFile(
820 m_szPipeName, GENERIC_READ | GENERIC_WRITE,
821 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
822 if (m_hPipe != INVALID_HANDLE_VALUE)
823 {
824 m_fConnected = true;
825 }
826 else
827 {
828 m_hPipe = NULL;
829 dwRet = GetLastError();
830 }
831 return dwRet;
832 }
833
834 //
835 // Ensures that the request buffer is large enough to hold a request,
836 // reallocating the buffer if necessary.
837 // It will also reduce the buffer size if the previous allocation was very large.
838 //
839 BOOL CheckRequestDataBuf(DWORD cbBuf)
840 {
841 if (m_cbBufReceive < cbBuf || (LARGE_BUFFER_THRESHOLD < m_cbBufReceive && cbBuf < m_cbBufReceive))
842 {
843 if (m_pBufReceive != NULL)
844 {
845 SecureZeroMemory(m_pBufReceive, m_cbBufReceive);
846 delete[] m_pBufReceive;
847 }
848 m_cbBufReceive = max(MIN_BUFFER_STRING_SIZE*2, cbBuf);
849 m_pBufReceive = new BYTE[m_cbBufReceive];
850 if (m_pBufReceive == NULL)
851 {
852 m_cbBufReceive = 0;
853 }
854 }
855 return m_pBufReceive != NULL;
856 }
857
858private:
859
860 // Name of this instance.
861 const wchar_t* m_szName;
862
863 // "\\.\pipe\name"
864 wchar_t* m_szPipeName;
865
866 // Handle to the pipe instance.
867 HANDLE m_hPipe;
868
869 // Handle to the thread that receives requests.
870 HANDLE m_hReceiveThread;
871
872 // Handle to the event used to signal the receive thread to exit.
873 HANDLE m_hReceiveStopEvent;
874
875 // All pipe I/O is done in overlapped mode to avoid unintentional blocking.
876 OVERLAPPED m_overlapped;
877
878 // Dynamically-resized buffer for receiving requests.
879 BYTE* m_pBufReceive;
880
881 // Current size of the receive request buffer.
882 DWORD m_cbBufReceive;
883
884 // Dynamically-resized buffer for sending requests.
885 wchar_t* m_pBufSend;
886
887 // Current size of the send request buffer.
888 DWORD m_cbBufSend;
889
890 // True if this is the server process, false if this is the client process.
891 const bool m_fServer;
892
893 // True if an asynchronous connection operation is currently in progress.
894 bool m_fConnecting;
895
896 // True if the pipe is currently connected.
897 bool m_fConnected;
898};
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxCA.cpp b/src/samples/Dtf/Tools/SfxCA/SfxCA.cpp
deleted file mode 100644
index 06319f1e..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxCA.cpp
+++ /dev/null
@@ -1,363 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include "EntryPoints.h"
5#include "SfxUtil.h"
6
7#define MANAGED_CAs_OUT_OF_PROC 1
8
9HMODULE g_hModule;
10bool g_fRunningOutOfProc = false;
11
12RemoteMsiSession* g_pRemote = NULL;
13
14// Prototypes for local functions.
15// See the function definitions for comments.
16
17bool InvokeManagedCustomAction(MSIHANDLE hSession,
18 _AppDomain* pAppDomain, const wchar_t* szEntryPoint, int* piResult);
19
20/// <summary>
21/// Entry-point for the CA DLL when re-launched as a separate process;
22/// connects the comm channel for remote MSI APIs, then invokes the
23/// managed custom action entry-point.
24/// </summary>
25/// <remarks>
26/// Do not change the parameters or calling-convention: RUNDLL32
27/// requires this exact signature.
28/// </remarks>
29extern "C"
30void __stdcall InvokeManagedCustomActionOutOfProc(
31 __in HWND hwnd, __in HINSTANCE hinst, __in_z wchar_t* szCmdLine, int nCmdShow)
32{
33 UNREFERENCED_PARAMETER(hwnd);
34 UNREFERENCED_PARAMETER(hinst);
35 UNREFERENCED_PARAMETER(nCmdShow);
36
37 g_fRunningOutOfProc = true;
38
39 const wchar_t* szSessionName = szCmdLine;
40 MSIHANDLE hSession;
41 const wchar_t* szEntryPoint;
42
43 int i;
44 for (i = 0; szCmdLine[i] && szCmdLine[i] != L' '; i++);
45 if (szCmdLine[i] != L'\0') szCmdLine[i++] = L'\0';
46 hSession = _wtoi(szCmdLine + i);
47
48 for (; szCmdLine[i] && szCmdLine[i] != L' '; i++);
49 if (szCmdLine[i] != L'\0') szCmdLine[i++] = L'\0';
50 szEntryPoint = szCmdLine + i;
51
52 g_pRemote = new RemoteMsiSession(szSessionName, false);
53 g_pRemote->Connect();
54
55 int ret = InvokeCustomAction(hSession, NULL, szEntryPoint);
56
57 RemoteMsiSession::RequestData requestData;
58 SecureZeroMemory(&requestData, sizeof(RemoteMsiSession::RequestData));
59 requestData.fields[0].vt = VT_I4;
60 requestData.fields[0].iValue = ret;
61 g_pRemote->SendRequest(RemoteMsiSession::EndSession, &requestData, NULL);
62 delete g_pRemote;
63}
64
65/// <summary>
66/// Re-launch this CA DLL as a separate process, and setup a comm channel
67/// for remote MSI API calls back to this process.
68/// </summary>
69int InvokeOutOfProcManagedCustomAction(MSIHANDLE hSession, const wchar_t* szEntryPoint)
70{
71 wchar_t szSessionName[100] = {0};
72 swprintf_s(szSessionName, 100, L"SfxCA_%d", ::GetTickCount());
73
74 RemoteMsiSession remote(szSessionName, true);
75
76 DWORD ret = remote.Connect();
77 if (ret != 0)
78 {
79 Log(hSession, L"Failed to create communication pipe for new CA process. Error code: %d", ret);
80 return ERROR_INSTALL_FAILURE;
81 }
82
83 ret = remote.ProcessRequests();
84 if (ret != 0)
85 {
86 Log(hSession, L"Failed to open communication pipe for new CA process. Error code: %d", ret);
87 return ERROR_INSTALL_FAILURE;
88 }
89
90 wchar_t szModule[MAX_PATH] = {0};
91 GetModuleFileName(g_hModule, szModule, MAX_PATH);
92
93 const wchar_t* rundll32 = L"rundll32.exe";
94 wchar_t szRunDll32Path[MAX_PATH] = {0};
95 GetSystemDirectory(szRunDll32Path, MAX_PATH);
96 wcscat_s(szRunDll32Path, MAX_PATH, L"\\");
97 wcscat_s(szRunDll32Path, MAX_PATH, rundll32);
98
99 const wchar_t* entry = L"zzzzInvokeManagedCustomActionOutOfProc";
100 wchar_t szCommandLine[1024] = {0};
101 swprintf_s(szCommandLine, 1024, L"%s \"%s\",%s %s %d %s",
102 rundll32, szModule, entry, szSessionName, hSession, szEntryPoint);
103
104 STARTUPINFO si;
105 SecureZeroMemory(&si, sizeof(STARTUPINFO));
106 si.cb = sizeof(STARTUPINFO);
107
108 PROCESS_INFORMATION pi;
109 SecureZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
110
111 if (!CreateProcess(szRunDll32Path, szCommandLine, NULL, NULL, FALSE,
112 0, NULL, NULL, &si, &pi))
113 {
114 DWORD err = GetLastError();
115 Log(hSession, L"Failed to create new CA process via RUNDLL32. Error code: %d", err);
116 return ERROR_INSTALL_FAILURE;
117 }
118
119 DWORD dwWait = WaitForSingleObject(pi.hProcess, INFINITE);
120 if (dwWait != WAIT_OBJECT_0)
121 {
122 DWORD err = GetLastError();
123 Log(hSession, L"Failed to wait for CA process. Error code: %d", err);
124 return ERROR_INSTALL_FAILURE;
125 }
126
127 DWORD dwExitCode;
128 BOOL bRet = GetExitCodeProcess(pi.hProcess, &dwExitCode);
129 if (!bRet)
130 {
131 DWORD err = GetLastError();
132 Log(hSession, L"Failed to get exit code of CA process. Error code: %d", err);
133 return ERROR_INSTALL_FAILURE;
134 }
135 else if (dwExitCode != 0)
136 {
137 Log(hSession, L"RUNDLL32 returned error code: %d", dwExitCode);
138 return ERROR_INSTALL_FAILURE;
139 }
140
141 CloseHandle(pi.hThread);
142 CloseHandle(pi.hProcess);
143
144 remote.WaitExitCode();
145 return remote.ExitCode;
146}
147
148/// <summary>
149/// Entrypoint for the managed CA proxy (RemotableNativeMethods) to
150/// call MSI APIs remotely.
151/// </summary>
152void __stdcall MsiRemoteInvoke(RemoteMsiSession::RequestId id, RemoteMsiSession::RequestData* pRequest, RemoteMsiSession::RequestData** ppResponse)
153{
154 if (g_fRunningOutOfProc)
155 {
156 g_pRemote->SendRequest(id, pRequest, ppResponse);
157 }
158 else
159 {
160 *ppResponse = NULL;
161 }
162}
163
164/// <summary>
165/// Invokes a managed custom action from native code by
166/// extracting the package to a temporary working directory
167/// then hosting the CLR and locating and calling the entrypoint.
168/// </summary>
169/// <param name="hSession">Handle to the installation session.
170/// Passed to custom action entrypoints by the installer engine.</param>
171/// <param name="szWorkingDir">Directory containing the CA binaries
172/// and the CustomAction.config file defining the entrypoints.
173/// This may be NULL, in which case the current module must have
174/// a concatenated cabinet containing those files, which will be
175/// extracted to a temporary directory.</param>
176/// <param name="szEntryPoint">Name of the CA entrypoint to be invoked.
177/// This must be either an explicit &quot;AssemblyName!Namespace.Class.Method&quot;
178/// string, or a simple name that maps to a full entrypoint definition
179/// in CustomAction.config.</param>
180/// <returns>The value returned by the managed custom action method,
181/// or ERROR_INSTALL_FAILURE if the CA could not be invoked.</returns>
182int InvokeCustomAction(MSIHANDLE hSession,
183 const wchar_t* szWorkingDir, const wchar_t* szEntryPoint)
184{
185#ifdef MANAGED_CAs_OUT_OF_PROC
186 if (!g_fRunningOutOfProc && szWorkingDir == NULL)
187 {
188 return InvokeOutOfProcManagedCustomAction(hSession, szEntryPoint);
189 }
190#endif
191
192 wchar_t szTempDir[MAX_PATH];
193 bool fDeleteTemp = false;
194 if (szWorkingDir == NULL)
195 {
196 if (!ExtractToTempDirectory(hSession, g_hModule, szTempDir, MAX_PATH))
197 {
198 return ERROR_INSTALL_FAILURE;
199 }
200 szWorkingDir = szTempDir;
201 fDeleteTemp = true;
202 }
203
204 wchar_t szConfigFilePath[MAX_PATH + 20];
205 StringCchCopy(szConfigFilePath, MAX_PATH + 20, szWorkingDir);
206 StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\CustomAction.config");
207
208 const wchar_t* szConfigFile = szConfigFilePath;
209 if (!::PathFileExists(szConfigFilePath))
210 {
211 szConfigFile = NULL;
212 }
213
214 wchar_t szWIAssembly[MAX_PATH + 50];
215 StringCchCopy(szWIAssembly, MAX_PATH + 50, szWorkingDir);
216 StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\WixToolset.Dtf.WindowsInstaller.dll");
217
218 int iResult = ERROR_INSTALL_FAILURE;
219 ICorRuntimeHost* pHost;
220 if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &pHost))
221 {
222 _AppDomain* pAppDomain;
223 if (CreateAppDomain(hSession, pHost, L"CustomAction", szWorkingDir,
224 szConfigFile, &pAppDomain))
225 {
226 if (!InvokeManagedCustomAction(hSession, pAppDomain, szEntryPoint, &iResult))
227 {
228 iResult = ERROR_INSTALL_FAILURE;
229 }
230 HRESULT hr = pHost->UnloadDomain(pAppDomain);
231 if (FAILED(hr))
232 {
233 Log(hSession, L"Failed to unload app domain. Error code 0x%X", hr);
234 }
235 pAppDomain->Release();
236 }
237
238 pHost->Stop();
239 pHost->Release();
240 }
241
242 if (fDeleteTemp)
243 {
244 DeleteDirectory(szTempDir);
245 }
246 return iResult;
247}
248
249/// <summary>
250/// Called by the system when the DLL is loaded.
251/// Saves the module handle for later use.
252/// </summary>
253BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, void* pReserved)
254{
255 UNREFERENCED_PARAMETER(pReserved);
256
257 switch (dwReason)
258 {
259 case DLL_PROCESS_ATTACH:
260 g_hModule = hModule;
261 break;
262 case DLL_THREAD_ATTACH:
263 case DLL_THREAD_DETACH:
264 case DLL_PROCESS_DETACH:
265 break;
266 }
267 return TRUE;
268}
269
270/// <summary>
271/// Loads and invokes the managed portion of the proxy.
272/// </summary>
273/// <param name="hSession">Handle to the installer session,
274/// used for logging errors and to be passed on to the custom action.</param>
275/// <param name="pAppDomain">AppDomain which has its application
276/// base set to the CA working directory.</param>
277/// <param name="szEntryPoint">Name of the CA entrypoint to be invoked.
278/// This must be either an explicit &quot;AssemblyName!Namespace.Class.Method&quot;
279/// string, or a simple name that maps to a full entrypoint definition
280/// in CustomAction.config.</param>
281/// <param name="piResult">Return value of the invoked custom
282/// action method.</param>
283/// <returns>True if the managed proxy was invoked successfully,
284/// false if there was some error. Note the custom action itself may
285/// return an error via piResult while this method still returns true
286/// since the invocation was successful.</returns>
287bool InvokeManagedCustomAction(MSIHANDLE hSession, _AppDomain* pAppDomain,
288 const wchar_t* szEntryPoint, int* piResult)
289{
290 VARIANT vResult;
291 ::VariantInit(&vResult);
292
293 const bool f64bit = (sizeof(void*) == sizeof(LONGLONG));
294 const wchar_t* szMsiAssemblyName = L"WixToolset.Dtf.WindowsInstaller";
295 const wchar_t* szMsiCAProxyClass = L"WixToolset.Dtf.WindowsInstaller.CustomActionProxy";
296 const wchar_t* szMsiCAInvokeMethod = (f64bit ? L"InvokeCustomAction64" : L"InvokeCustomAction32");
297
298 _MethodInfo* pCAInvokeMethod;
299 if (!GetMethod(hSession, pAppDomain, szMsiAssemblyName,
300 szMsiCAProxyClass, szMsiCAInvokeMethod, &pCAInvokeMethod))
301 {
302 return false;
303 }
304
305 HRESULT hr;
306 VARIANT vNull;
307 vNull.vt = VT_EMPTY;
308 SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3);
309 VARIANT vSessionHandle;
310 vSessionHandle.vt = VT_I4;
311 vSessionHandle.intVal = hSession;
312 LONG index = 0;
313 hr = SafeArrayPutElement(saArgs, &index, &vSessionHandle);
314 if (FAILED(hr)) goto LExit;
315 VARIANT vEntryPoint;
316 vEntryPoint.vt = VT_BSTR;
317 vEntryPoint.bstrVal = SysAllocString(szEntryPoint);
318 if (vEntryPoint.bstrVal == NULL)
319 {
320 hr = E_OUTOFMEMORY;
321 goto LExit;
322 }
323 index = 1;
324 hr = SafeArrayPutElement(saArgs, &index, &vEntryPoint);
325 if (FAILED(hr)) goto LExit;
326 VARIANT vRemotingFunctionPtr;
327#pragma warning(push)
328#pragma warning(disable:4127) // conditional expression is constant
329 if (f64bit)
330#pragma warning(pop)
331 {
332 vRemotingFunctionPtr.vt = VT_I8;
333 vRemotingFunctionPtr.llVal = (LONGLONG) (g_fRunningOutOfProc ? MsiRemoteInvoke : NULL);
334 }
335 else
336 {
337 vRemotingFunctionPtr.vt = VT_I4;
338#pragma warning(push)
339#pragma warning(disable:4302) // truncation
340#pragma warning(disable:4311) // pointer truncation
341 vRemotingFunctionPtr.lVal = (LONG) (g_fRunningOutOfProc ? MsiRemoteInvoke : NULL);
342#pragma warning(pop)
343 }
344 index = 2;
345 hr = SafeArrayPutElement(saArgs, &index, &vRemotingFunctionPtr);
346 if (FAILED(hr)) goto LExit;
347
348 hr = pCAInvokeMethod->Invoke_3(vNull, saArgs, &vResult);
349
350LExit:
351 SafeArrayDestroy(saArgs);
352 pCAInvokeMethod->Release();
353
354 if (FAILED(hr))
355 {
356 Log(hSession, L"Failed to invoke custom action method. Error code 0x%X", hr);
357 return false;
358 }
359
360 *piResult = vResult.intVal;
361 return true;
362}
363
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxCA.rc b/src/samples/Dtf/Tools/SfxCA/SfxCA.rc
deleted file mode 100644
index 4d78194b..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxCA.rc
+++ /dev/null
@@ -1,10 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#define VER_DLL
4#define VER_LANG_NEUTRAL
5#define VER_ORIGINAL_FILENAME "SfxCA.dll"
6#define VER_INTERNAL_NAME "SfxCA"
7#define VER_FILE_DESCRIPTION "DTF Self-Extracting Custom Action"
8
9// Additional resources here
10
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj b/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj
deleted file mode 100644
index 4684d6e0..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj
+++ /dev/null
@@ -1,67 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <ItemGroup Label="ProjectConfigurations">
5 <ProjectConfiguration Include="Debug|Win32">
6 <Configuration>Debug</Configuration>
7 <Platform>Win32</Platform>
8 </ProjectConfiguration>
9 <ProjectConfiguration Include="Release|Win32">
10 <Configuration>Release</Configuration>
11 <Platform>Win32</Platform>
12 </ProjectConfiguration>
13 <ProjectConfiguration Include="Debug|x64">
14 <Configuration>Debug</Configuration>
15 <Platform>x64</Platform>
16 </ProjectConfiguration>
17 <ProjectConfiguration Include="Release|x64">
18 <Configuration>Release</Configuration>
19 <Platform>x64</Platform>
20 </ProjectConfiguration>
21 </ItemGroup>
22
23 <PropertyGroup Label="Globals">
24 <ProjectGuid>{55D5BA28-D427-4F53-80C2-FE9EF23C1553}</ProjectGuid>
25 <ConfigurationType>DynamicLibrary</ConfigurationType>
26 <TargetName>SfxCA</TargetName>
27 <CharacterSet>Unicode</CharacterSet>
28 <ProjectModuleDefinitionFile>EntryPoints.def</ProjectModuleDefinitionFile>
29 </PropertyGroup>
30 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
31 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
32 <PropertyGroup>
33 <ProjectAdditionalLinkLibraries>msi.lib;cabinet.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
34 </PropertyGroup>
35 <ItemGroup>
36 <ClCompile Include="ClrHost.cpp" />
37 <ClCompile Include="Extract.cpp" />
38 <ClCompile Include="precomp.cpp">
39 <PrecompiledHeader>Create</PrecompiledHeader>
40 </ClCompile>
41 <ClCompile Include="RemoteMsi.cpp" />
42 <ClCompile Include="SfxCA.cpp" />
43 <ClCompile Include="SfxUtil.cpp" />
44 <ClCompile Include="EmbeddedUI.cpp" />
45 </ItemGroup>
46 <ItemGroup>
47 <ClInclude Include="precomp.h" />
48 <ClInclude Include="EntryPoints.h" />
49 <ClInclude Include="RemoteMsiSession.h" />
50 <ClInclude Include="SfxUtil.h" />
51 </ItemGroup>
52 <ItemGroup>
53 <None Include="EntryPoints.def" />
54 <None Include="packages.config" />
55 </ItemGroup>
56 <ItemGroup>
57 <ResourceCompile Include="SfxCA.rc" />
58 </ItemGroup>
59 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
60 <Import Project="..\..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" />
61 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
62 <PropertyGroup>
63 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
64 </PropertyGroup>
65 <Error Condition="!Exists('..\..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets'))" />
66 </Target>
67</Project> \ No newline at end of file
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj.filters b/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj.filters
deleted file mode 100644
index a5ebf693..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxCA.vcxproj.filters
+++ /dev/null
@@ -1,62 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <ClCompile Include="ClrHost.cpp">
5 <Filter>Source Files</Filter>
6 </ClCompile>
7 <ClCompile Include="EmbeddedUI.cpp">
8 <Filter>Source Files</Filter>
9 </ClCompile>
10 <ClCompile Include="Extract.cpp">
11 <Filter>Source Files</Filter>
12 </ClCompile>
13 <ClCompile Include="RemoteMsi.cpp">
14 <Filter>Source Files</Filter>
15 </ClCompile>
16 <ClCompile Include="SfxCA.cpp">
17 <Filter>Source Files</Filter>
18 </ClCompile>
19 <ClCompile Include="SfxUtil.cpp">
20 <Filter>Source Files</Filter>
21 </ClCompile>
22 <ClCompile Include="precomp.cpp">
23 <Filter>Source Files</Filter>
24 </ClCompile>
25 </ItemGroup>
26 <ItemGroup>
27 <ClInclude Include="EntryPoints.h">
28 <Filter>Header Files</Filter>
29 </ClInclude>
30 <ClInclude Include="precomp.h">
31 <Filter>Header Files</Filter>
32 </ClInclude>
33 <ClInclude Include="RemoteMsiSession.h">
34 <Filter>Header Files</Filter>
35 </ClInclude>
36 <ClInclude Include="SfxUtil.h">
37 <Filter>Header Files</Filter>
38 </ClInclude>
39 </ItemGroup>
40 <ItemGroup>
41 <Filter Include="Resource Files">
42 <UniqueIdentifier>{81c92f68-18c2-4cd4-a588-5c3616860dd9}</UniqueIdentifier>
43 </Filter>
44 <Filter Include="Header Files">
45 <UniqueIdentifier>{6cdc30ee-e14d-4679-b92e-3e080535e53b}</UniqueIdentifier>
46 </Filter>
47 <Filter Include="Source Files">
48 <UniqueIdentifier>{1666a44e-4f2e-4f13-980e-d0c3dfa7cb6d}</UniqueIdentifier>
49 </Filter>
50 </ItemGroup>
51 <ItemGroup>
52 <ResourceCompile Include="SfxCA.rc">
53 <Filter>Resource Files</Filter>
54 </ResourceCompile>
55 </ItemGroup>
56 <ItemGroup>
57 <None Include="EntryPoints.def">
58 <Filter>Resource Files</Filter>
59 </None>
60 <None Include="packages.config" />
61 </ItemGroup>
62</Project> \ No newline at end of file
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxUtil.cpp b/src/samples/Dtf/Tools/SfxCA/SfxUtil.cpp
deleted file mode 100644
index 1bf2c5b2..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxUtil.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include "SfxUtil.h"
5
6/// <summary>
7/// Writes a formatted message to the MSI log.
8/// Does out-of-proc MSI calls if necessary.
9/// </summary>
10void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...)
11{
12 const int LOG_BUFSIZE = 4096;
13 wchar_t szBuf[LOG_BUFSIZE];
14 va_list args;
15 va_start(args, szMessage);
16 StringCchVPrintf(szBuf, LOG_BUFSIZE, szMessage, args);
17
18 if (!g_fRunningOutOfProc || NULL == g_pRemote)
19 {
20 MSIHANDLE hRec = MsiCreateRecord(1);
21 MsiRecordSetString(hRec, 0, L"SFXCA: [1]");
22 MsiRecordSetString(hRec, 1, szBuf);
23 MsiProcessMessage(hSession, INSTALLMESSAGE_INFO, hRec);
24 MsiCloseHandle(hRec);
25 }
26 else
27 {
28 // Logging is the only remote-MSI operation done from unmanaged code.
29 // It's not very convenient here because part of the infrastructure
30 // for remote MSI APIs is on the managed side.
31
32 RemoteMsiSession::RequestData req;
33 RemoteMsiSession::RequestData* pResp = NULL;
34 SecureZeroMemory(&req, sizeof(RemoteMsiSession::RequestData));
35
36 req.fields[0].vt = VT_UI4;
37 req.fields[0].uiValue = 1;
38 g_pRemote->SendRequest(RemoteMsiSession::MsiCreateRecord, &req, &pResp);
39 MSIHANDLE hRec = (MSIHANDLE) pResp->fields[0].iValue;
40
41 req.fields[0].vt = VT_I4;
42 req.fields[0].iValue = (int) hRec;
43 req.fields[1].vt = VT_UI4;
44 req.fields[1].uiValue = 0;
45 req.fields[2].vt = VT_LPWSTR;
46 req.fields[2].szValue = L"SFXCA: [1]";
47 g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp);
48
49 req.fields[0].vt = VT_I4;
50 req.fields[0].iValue = (int) hRec;
51 req.fields[1].vt = VT_UI4;
52 req.fields[1].uiValue = 1;
53 req.fields[2].vt = VT_LPWSTR;
54 req.fields[2].szValue = szBuf;
55 g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp);
56
57 req.fields[0].vt = VT_I4;
58 req.fields[0].iValue = (int) hSession;
59 req.fields[1].vt = VT_I4;
60 req.fields[1].iValue = (int) INSTALLMESSAGE_INFO;
61 req.fields[2].vt = VT_I4;
62 req.fields[2].iValue = (int) hRec;
63 g_pRemote->SendRequest(RemoteMsiSession::MsiProcessMessage, &req, &pResp);
64
65 req.fields[0].vt = VT_I4;
66 req.fields[0].iValue = (int) hRec;
67 req.fields[1].vt = VT_EMPTY;
68 req.fields[2].vt = VT_EMPTY;
69 g_pRemote->SendRequest(RemoteMsiSession::MsiCloseHandle, &req, &pResp);
70 }
71}
72
73/// <summary>
74/// Deletes a directory, including all files and subdirectories.
75/// </summary>
76/// <param name="szDir">Path to the directory to delete,
77/// not including a trailing backslash.</param>
78/// <returns>True if the directory was successfully deleted, or false
79/// if the deletion failed (most likely because some files were locked).
80/// </returns>
81bool DeleteDirectory(const wchar_t* szDir)
82{
83 size_t cchDir = wcslen(szDir);
84 size_t cchPathBuf = cchDir + 3 + MAX_PATH;
85 wchar_t* szPath = (wchar_t*) _alloca(cchPathBuf * sizeof(wchar_t));
86 if (szPath == NULL) return false;
87 StringCchCopy(szPath, cchPathBuf, szDir);
88 StringCchCat(szPath, cchPathBuf, L"\\*");
89 WIN32_FIND_DATA fd;
90 HANDLE hSearch = FindFirstFile(szPath, &fd);
91 while (hSearch != INVALID_HANDLE_VALUE)
92 {
93 StringCchCopy(szPath + cchDir + 1, cchPathBuf - (cchDir + 1), fd.cFileName);
94 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
95 {
96 if (wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0)
97 {
98 DeleteDirectory(szPath);
99 }
100 }
101 else
102 {
103 DeleteFile(szPath);
104 }
105 if (!FindNextFile(hSearch, &fd))
106 {
107 FindClose(hSearch);
108 hSearch = INVALID_HANDLE_VALUE;
109 }
110 }
111 return RemoveDirectory(szDir) != 0;
112}
113
114bool DirectoryExists(const wchar_t* szDir)
115{
116 if (szDir != NULL)
117 {
118 DWORD dwAttrs = GetFileAttributes(szDir);
119 if (dwAttrs != -1 && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0)
120 {
121 return true;
122 }
123 }
124 return false;
125}
126
127/// <summary>
128/// Extracts a cabinet that is concatenated to a module
129/// to a new temporary directory.
130/// </summary>
131/// <param name="hSession">Handle to the installer session,
132/// used just for logging.</param>
133/// <param name="hModule">Module that has the concatenated cabinet.</param>
134/// <param name="szTempDir">Buffer for returning the path of the
135/// created temp directory.</param>
136/// <param name="cchTempDirBuf">Size in characters of the buffer.
137/// <returns>True if the files were extracted, or false if the
138/// buffer was too small or the directory could not be created
139/// or the extraction failed for some other reason.</returns>
140__success(return != false)
141bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule,
142 __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf)
143{
144 wchar_t szModule[MAX_PATH];
145 DWORD cchCopied = GetModuleFileName(hModule, szModule, MAX_PATH - 1);
146 if (cchCopied == 0)
147 {
148 Log(hSession, L"Failed to get module path. Error code %d.", GetLastError());
149 return false;
150 }
151 else if (cchCopied == MAX_PATH - 1)
152 {
153 Log(hSession, L"Failed to get module path -- path is too long.");
154 return false;
155 }
156
157 if (szTempDir == NULL || cchTempDirBuf < wcslen(szModule) + 1)
158 {
159 Log(hSession, L"Temp directory buffer is NULL or too small.");
160 return false;
161 }
162 StringCchCopy(szTempDir, cchTempDirBuf, szModule);
163 StringCchCat(szTempDir, cchTempDirBuf, L"-");
164
165 DWORD cchTempDir = (DWORD) wcslen(szTempDir);
166 for (int i = 0; DirectoryExists(szTempDir); i++)
167 {
168 swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i);
169 }
170
171 if (!CreateDirectory(szTempDir, NULL))
172 {
173 cchCopied = GetTempPath(cchTempDirBuf, szTempDir);
174 if (cchCopied == 0 || cchCopied >= cchTempDirBuf)
175 {
176 Log(hSession, L"Failed to get temp directory. Error code %d", GetLastError());
177 return false;
178 }
179
180 wchar_t* szModuleName = wcsrchr(szModule, L'\\');
181 if (szModuleName == NULL) szModuleName = szModule;
182 else szModuleName = szModuleName + 1;
183 StringCchCat(szTempDir, cchTempDirBuf, szModuleName);
184 StringCchCat(szTempDir, cchTempDirBuf, L"-");
185
186 cchTempDir = (DWORD) wcslen(szTempDir);
187 for (int i = 0; DirectoryExists(szTempDir); i++)
188 {
189 swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i);
190 }
191
192 if (!CreateDirectory(szTempDir, NULL))
193 {
194 Log(hSession, L"Failed to create temp directory. Error code %d", GetLastError());
195 return false;
196 }
197 }
198
199 Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir);
200 int err = ExtractCabinet(szModule, szTempDir);
201 if (err != 0)
202 {
203 Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err);
204 DeleteDirectory(szTempDir);
205 return false;
206 }
207 return true;
208}
209
diff --git a/src/samples/Dtf/Tools/SfxCA/SfxUtil.h b/src/samples/Dtf/Tools/SfxCA/SfxUtil.h
deleted file mode 100644
index af12d8dd..00000000
--- a/src/samples/Dtf/Tools/SfxCA/SfxUtil.h
+++ /dev/null
@@ -1,31 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "RemoteMsiSession.h"
4
5void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...);
6
7int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir);
8
9bool DeleteDirectory(const wchar_t* szDir);
10
11__success(return != false)
12bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule,
13 __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf);
14
15bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile,
16 const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost);
17
18bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost,
19 const wchar_t* szName, const wchar_t* szAppBase,
20 const wchar_t* szConfigFile, _AppDomain** ppAppDomain);
21
22bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain,
23 const wchar_t* szAssembly, const wchar_t* szClass,
24 const wchar_t* szMethod, _MethodInfo** ppCAMethod);
25
26extern HMODULE g_hModule;
27extern bool g_fRunningOutOfProc;
28
29extern RemoteMsiSession* g_pRemote;
30
31
diff --git a/src/samples/Dtf/Tools/SfxCA/packages.config b/src/samples/Dtf/Tools/SfxCA/packages.config
deleted file mode 100644
index 1ffaa8df..00000000
--- a/src/samples/Dtf/Tools/SfxCA/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Nerdbank.GitVersioning" version="3.3.37" targetFramework="native" developmentDependency="true" />
4</packages> \ No newline at end of file
diff --git a/src/samples/Dtf/Tools/SfxCA/precomp.cpp b/src/samples/Dtf/Tools/SfxCA/precomp.cpp
deleted file mode 100644
index ce82c1d7..00000000
--- a/src/samples/Dtf/Tools/SfxCA/precomp.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h" \ No newline at end of file
diff --git a/src/samples/Dtf/Tools/SfxCA/precomp.h b/src/samples/Dtf/Tools/SfxCA/precomp.h
deleted file mode 100644
index 48d4f011..00000000
--- a/src/samples/Dtf/Tools/SfxCA/precomp.h
+++ /dev/null
@@ -1,18 +0,0 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <msiquery.h>
7#include <strsafe.h>
8#include <mscoree.h>
9#include <io.h>
10#include <fcntl.h>
11#include <share.h>
12#include <shlwapi.h>
13#include <sys/stat.h>
14#include <malloc.h>
15#include <fdi.h>
16#include <msiquery.h>
17#import <mscorlib.tlb> raw_interfaces_only rename("ReportEvent", "CorReportEvent")
18using namespace mscorlib;
diff --git a/src/samples/Dtf/Tools/Tools.proj b/src/samples/Dtf/Tools/Tools.proj
deleted file mode 100644
index 751247dc..00000000
--- a/src/samples/Dtf/Tools/Tools.proj
+++ /dev/null
@@ -1,15 +0,0 @@
1<?xml version='1.0' encoding='utf-8'?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
6 <ItemGroup>
7 <ProjectReference Include="MakeSfxCA\MakeSfxCA.csproj" />
8 <ProjectReference Include="SfxCA\SfxCA.vcxproj" />
9 <ProjectReference Include="SfxCA\SfxCA.vcxproj">
10 <Properties>Platform=x64</Properties>
11 </ProjectReference>
12 </ItemGroup>
13
14 <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\Traversal.targets" />
15</Project>