aboutsummaryrefslogtreecommitdiff
path: root/src/samples
diff options
context:
space:
mode:
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>