diff options
Diffstat (limited to 'src/samples')
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 | |||
3 | using 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | using 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
5 | void 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> | ||
32 | bool 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> | ||
125 | bool 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> | ||
213 | bool 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. | ||
7 | static const wchar_t* g_szWorkingDir; | ||
8 | static ICorRuntimeHost* g_pClrHost; | ||
9 | static _AppDomain* g_pAppDomain; | ||
10 | static _MethodInfo* g_pProcessMessageMethod; | ||
11 | static _MethodInfo* g_pShutdownMethod; | ||
12 | |||
13 | // Reserve extra space for strings to be replaced at build time. | ||
14 | #define NULLSPACE \ | ||
15 | L"\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" \ | ||
16 | L"\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" \ | ||
17 | L"\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" \ | ||
18 | L"\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 | |||
23 | bool 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> | ||
37 | extern "C" | ||
38 | UINT __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> | ||
145 | extern "C" | ||
146 | INT __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 | |||
176 | LExit: | ||
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> | ||
192 | extern "C" | ||
193 | DWORD __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> | ||
227 | bool 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 | |||
262 | LExit: | ||
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 | |||
4 | LIBRARY "SfxCA" | ||
5 | |||
6 | EXPORTS | ||
7 | |||
8 | CustomActionEntryPoint000________________________________________________=CustomActionEntryPoint000 | ||
9 | CustomActionEntryPoint001________________________________________________=CustomActionEntryPoint001 | ||
10 | CustomActionEntryPoint002________________________________________________=CustomActionEntryPoint002 | ||
11 | CustomActionEntryPoint003________________________________________________=CustomActionEntryPoint003 | ||
12 | CustomActionEntryPoint004________________________________________________=CustomActionEntryPoint004 | ||
13 | CustomActionEntryPoint005________________________________________________=CustomActionEntryPoint005 | ||
14 | CustomActionEntryPoint006________________________________________________=CustomActionEntryPoint006 | ||
15 | CustomActionEntryPoint007________________________________________________=CustomActionEntryPoint007 | ||
16 | CustomActionEntryPoint008________________________________________________=CustomActionEntryPoint008 | ||
17 | CustomActionEntryPoint009________________________________________________=CustomActionEntryPoint009 | ||
18 | CustomActionEntryPoint010________________________________________________=CustomActionEntryPoint010 | ||
19 | CustomActionEntryPoint011________________________________________________=CustomActionEntryPoint011 | ||
20 | CustomActionEntryPoint012________________________________________________=CustomActionEntryPoint012 | ||
21 | CustomActionEntryPoint013________________________________________________=CustomActionEntryPoint013 | ||
22 | CustomActionEntryPoint014________________________________________________=CustomActionEntryPoint014 | ||
23 | CustomActionEntryPoint015________________________________________________=CustomActionEntryPoint015 | ||
24 | CustomActionEntryPoint016________________________________________________=CustomActionEntryPoint016 | ||
25 | CustomActionEntryPoint017________________________________________________=CustomActionEntryPoint017 | ||
26 | CustomActionEntryPoint018________________________________________________=CustomActionEntryPoint018 | ||
27 | CustomActionEntryPoint019________________________________________________=CustomActionEntryPoint019 | ||
28 | CustomActionEntryPoint020________________________________________________=CustomActionEntryPoint020 | ||
29 | CustomActionEntryPoint021________________________________________________=CustomActionEntryPoint021 | ||
30 | CustomActionEntryPoint022________________________________________________=CustomActionEntryPoint022 | ||
31 | CustomActionEntryPoint023________________________________________________=CustomActionEntryPoint023 | ||
32 | CustomActionEntryPoint024________________________________________________=CustomActionEntryPoint024 | ||
33 | CustomActionEntryPoint025________________________________________________=CustomActionEntryPoint025 | ||
34 | CustomActionEntryPoint026________________________________________________=CustomActionEntryPoint026 | ||
35 | CustomActionEntryPoint027________________________________________________=CustomActionEntryPoint027 | ||
36 | CustomActionEntryPoint028________________________________________________=CustomActionEntryPoint028 | ||
37 | CustomActionEntryPoint029________________________________________________=CustomActionEntryPoint029 | ||
38 | CustomActionEntryPoint030________________________________________________=CustomActionEntryPoint030 | ||
39 | CustomActionEntryPoint031________________________________________________=CustomActionEntryPoint031 | ||
40 | CustomActionEntryPoint032________________________________________________=CustomActionEntryPoint032 | ||
41 | CustomActionEntryPoint033________________________________________________=CustomActionEntryPoint033 | ||
42 | CustomActionEntryPoint034________________________________________________=CustomActionEntryPoint034 | ||
43 | CustomActionEntryPoint035________________________________________________=CustomActionEntryPoint035 | ||
44 | CustomActionEntryPoint036________________________________________________=CustomActionEntryPoint036 | ||
45 | CustomActionEntryPoint037________________________________________________=CustomActionEntryPoint037 | ||
46 | CustomActionEntryPoint038________________________________________________=CustomActionEntryPoint038 | ||
47 | CustomActionEntryPoint039________________________________________________=CustomActionEntryPoint039 | ||
48 | CustomActionEntryPoint040________________________________________________=CustomActionEntryPoint040 | ||
49 | CustomActionEntryPoint041________________________________________________=CustomActionEntryPoint041 | ||
50 | CustomActionEntryPoint042________________________________________________=CustomActionEntryPoint042 | ||
51 | CustomActionEntryPoint043________________________________________________=CustomActionEntryPoint043 | ||
52 | CustomActionEntryPoint044________________________________________________=CustomActionEntryPoint044 | ||
53 | CustomActionEntryPoint045________________________________________________=CustomActionEntryPoint045 | ||
54 | CustomActionEntryPoint046________________________________________________=CustomActionEntryPoint046 | ||
55 | CustomActionEntryPoint047________________________________________________=CustomActionEntryPoint047 | ||
56 | CustomActionEntryPoint048________________________________________________=CustomActionEntryPoint048 | ||
57 | CustomActionEntryPoint049________________________________________________=CustomActionEntryPoint049 | ||
58 | CustomActionEntryPoint050________________________________________________=CustomActionEntryPoint050 | ||
59 | CustomActionEntryPoint051________________________________________________=CustomActionEntryPoint051 | ||
60 | CustomActionEntryPoint052________________________________________________=CustomActionEntryPoint052 | ||
61 | CustomActionEntryPoint053________________________________________________=CustomActionEntryPoint053 | ||
62 | CustomActionEntryPoint054________________________________________________=CustomActionEntryPoint054 | ||
63 | CustomActionEntryPoint055________________________________________________=CustomActionEntryPoint055 | ||
64 | CustomActionEntryPoint056________________________________________________=CustomActionEntryPoint056 | ||
65 | CustomActionEntryPoint057________________________________________________=CustomActionEntryPoint057 | ||
66 | CustomActionEntryPoint058________________________________________________=CustomActionEntryPoint058 | ||
67 | CustomActionEntryPoint059________________________________________________=CustomActionEntryPoint059 | ||
68 | CustomActionEntryPoint060________________________________________________=CustomActionEntryPoint060 | ||
69 | CustomActionEntryPoint061________________________________________________=CustomActionEntryPoint061 | ||
70 | CustomActionEntryPoint062________________________________________________=CustomActionEntryPoint062 | ||
71 | CustomActionEntryPoint063________________________________________________=CustomActionEntryPoint063 | ||
72 | CustomActionEntryPoint064________________________________________________=CustomActionEntryPoint064 | ||
73 | CustomActionEntryPoint065________________________________________________=CustomActionEntryPoint065 | ||
74 | CustomActionEntryPoint066________________________________________________=CustomActionEntryPoint066 | ||
75 | CustomActionEntryPoint067________________________________________________=CustomActionEntryPoint067 | ||
76 | CustomActionEntryPoint068________________________________________________=CustomActionEntryPoint068 | ||
77 | CustomActionEntryPoint069________________________________________________=CustomActionEntryPoint069 | ||
78 | CustomActionEntryPoint070________________________________________________=CustomActionEntryPoint070 | ||
79 | CustomActionEntryPoint071________________________________________________=CustomActionEntryPoint071 | ||
80 | CustomActionEntryPoint072________________________________________________=CustomActionEntryPoint072 | ||
81 | CustomActionEntryPoint073________________________________________________=CustomActionEntryPoint073 | ||
82 | CustomActionEntryPoint074________________________________________________=CustomActionEntryPoint074 | ||
83 | CustomActionEntryPoint075________________________________________________=CustomActionEntryPoint075 | ||
84 | CustomActionEntryPoint076________________________________________________=CustomActionEntryPoint076 | ||
85 | CustomActionEntryPoint077________________________________________________=CustomActionEntryPoint077 | ||
86 | CustomActionEntryPoint078________________________________________________=CustomActionEntryPoint078 | ||
87 | CustomActionEntryPoint079________________________________________________=CustomActionEntryPoint079 | ||
88 | CustomActionEntryPoint080________________________________________________=CustomActionEntryPoint080 | ||
89 | CustomActionEntryPoint081________________________________________________=CustomActionEntryPoint081 | ||
90 | CustomActionEntryPoint082________________________________________________=CustomActionEntryPoint082 | ||
91 | CustomActionEntryPoint083________________________________________________=CustomActionEntryPoint083 | ||
92 | CustomActionEntryPoint084________________________________________________=CustomActionEntryPoint084 | ||
93 | CustomActionEntryPoint085________________________________________________=CustomActionEntryPoint085 | ||
94 | CustomActionEntryPoint086________________________________________________=CustomActionEntryPoint086 | ||
95 | CustomActionEntryPoint087________________________________________________=CustomActionEntryPoint087 | ||
96 | CustomActionEntryPoint088________________________________________________=CustomActionEntryPoint088 | ||
97 | CustomActionEntryPoint089________________________________________________=CustomActionEntryPoint089 | ||
98 | CustomActionEntryPoint090________________________________________________=CustomActionEntryPoint090 | ||
99 | CustomActionEntryPoint091________________________________________________=CustomActionEntryPoint091 | ||
100 | CustomActionEntryPoint092________________________________________________=CustomActionEntryPoint092 | ||
101 | CustomActionEntryPoint093________________________________________________=CustomActionEntryPoint093 | ||
102 | CustomActionEntryPoint094________________________________________________=CustomActionEntryPoint094 | ||
103 | CustomActionEntryPoint095________________________________________________=CustomActionEntryPoint095 | ||
104 | CustomActionEntryPoint096________________________________________________=CustomActionEntryPoint096 | ||
105 | CustomActionEntryPoint097________________________________________________=CustomActionEntryPoint097 | ||
106 | CustomActionEntryPoint098________________________________________________=CustomActionEntryPoint098 | ||
107 | CustomActionEntryPoint099________________________________________________=CustomActionEntryPoint099 | ||
108 | CustomActionEntryPoint100________________________________________________=CustomActionEntryPoint100 | ||
109 | CustomActionEntryPoint101________________________________________________=CustomActionEntryPoint101 | ||
110 | CustomActionEntryPoint102________________________________________________=CustomActionEntryPoint102 | ||
111 | CustomActionEntryPoint103________________________________________________=CustomActionEntryPoint103 | ||
112 | CustomActionEntryPoint104________________________________________________=CustomActionEntryPoint104 | ||
113 | CustomActionEntryPoint105________________________________________________=CustomActionEntryPoint105 | ||
114 | CustomActionEntryPoint106________________________________________________=CustomActionEntryPoint106 | ||
115 | CustomActionEntryPoint107________________________________________________=CustomActionEntryPoint107 | ||
116 | CustomActionEntryPoint108________________________________________________=CustomActionEntryPoint108 | ||
117 | CustomActionEntryPoint109________________________________________________=CustomActionEntryPoint109 | ||
118 | CustomActionEntryPoint110________________________________________________=CustomActionEntryPoint110 | ||
119 | CustomActionEntryPoint111________________________________________________=CustomActionEntryPoint111 | ||
120 | CustomActionEntryPoint112________________________________________________=CustomActionEntryPoint112 | ||
121 | CustomActionEntryPoint113________________________________________________=CustomActionEntryPoint113 | ||
122 | CustomActionEntryPoint114________________________________________________=CustomActionEntryPoint114 | ||
123 | CustomActionEntryPoint115________________________________________________=CustomActionEntryPoint115 | ||
124 | CustomActionEntryPoint116________________________________________________=CustomActionEntryPoint116 | ||
125 | CustomActionEntryPoint117________________________________________________=CustomActionEntryPoint117 | ||
126 | CustomActionEntryPoint118________________________________________________=CustomActionEntryPoint118 | ||
127 | CustomActionEntryPoint119________________________________________________=CustomActionEntryPoint119 | ||
128 | CustomActionEntryPoint120________________________________________________=CustomActionEntryPoint120 | ||
129 | CustomActionEntryPoint121________________________________________________=CustomActionEntryPoint121 | ||
130 | CustomActionEntryPoint122________________________________________________=CustomActionEntryPoint122 | ||
131 | CustomActionEntryPoint123________________________________________________=CustomActionEntryPoint123 | ||
132 | CustomActionEntryPoint124________________________________________________=CustomActionEntryPoint124 | ||
133 | CustomActionEntryPoint125________________________________________________=CustomActionEntryPoint125 | ||
134 | CustomActionEntryPoint126________________________________________________=CustomActionEntryPoint126 | ||
135 | CustomActionEntryPoint127________________________________________________=CustomActionEntryPoint127 | ||
136 | |||
137 | zzzzInvokeManagedCustomActionOutOfProcW=InvokeManagedCustomActionOutOfProc | ||
138 | zzzInitializeEmbeddedUI=InitializeEmbeddedUI | ||
139 | zzzEmbeddedUIHandler=EmbeddedUIHandler | ||
140 | zzzShutdownEmbeddedUI=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 | |||
3 | int 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 \ | ||
24 | L"\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" \ | ||
25 | L"\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" \ | ||
26 | L"\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" \ | ||
27 | L"\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 | |||
33 | TEMPLATE_CA_ENTRYPOINT(000,L"000"); | ||
34 | TEMPLATE_CA_ENTRYPOINT(001,L"001"); | ||
35 | TEMPLATE_CA_ENTRYPOINT(002,L"002"); | ||
36 | TEMPLATE_CA_ENTRYPOINT(003,L"003"); | ||
37 | TEMPLATE_CA_ENTRYPOINT(004,L"004"); | ||
38 | TEMPLATE_CA_ENTRYPOINT(005,L"005"); | ||
39 | TEMPLATE_CA_ENTRYPOINT(006,L"006"); | ||
40 | TEMPLATE_CA_ENTRYPOINT(007,L"007"); | ||
41 | TEMPLATE_CA_ENTRYPOINT(008,L"008"); | ||
42 | TEMPLATE_CA_ENTRYPOINT(009,L"009"); | ||
43 | TEMPLATE_CA_ENTRYPOINT(010,L"010"); | ||
44 | TEMPLATE_CA_ENTRYPOINT(011,L"011"); | ||
45 | TEMPLATE_CA_ENTRYPOINT(012,L"012"); | ||
46 | TEMPLATE_CA_ENTRYPOINT(013,L"013"); | ||
47 | TEMPLATE_CA_ENTRYPOINT(014,L"014"); | ||
48 | TEMPLATE_CA_ENTRYPOINT(015,L"015"); | ||
49 | TEMPLATE_CA_ENTRYPOINT(016,L"016"); | ||
50 | TEMPLATE_CA_ENTRYPOINT(017,L"017"); | ||
51 | TEMPLATE_CA_ENTRYPOINT(018,L"018"); | ||
52 | TEMPLATE_CA_ENTRYPOINT(019,L"019"); | ||
53 | TEMPLATE_CA_ENTRYPOINT(020,L"020"); | ||
54 | TEMPLATE_CA_ENTRYPOINT(021,L"021"); | ||
55 | TEMPLATE_CA_ENTRYPOINT(022,L"022"); | ||
56 | TEMPLATE_CA_ENTRYPOINT(023,L"023"); | ||
57 | TEMPLATE_CA_ENTRYPOINT(024,L"024"); | ||
58 | TEMPLATE_CA_ENTRYPOINT(025,L"025"); | ||
59 | TEMPLATE_CA_ENTRYPOINT(026,L"026"); | ||
60 | TEMPLATE_CA_ENTRYPOINT(027,L"027"); | ||
61 | TEMPLATE_CA_ENTRYPOINT(028,L"028"); | ||
62 | TEMPLATE_CA_ENTRYPOINT(029,L"029"); | ||
63 | TEMPLATE_CA_ENTRYPOINT(030,L"030"); | ||
64 | TEMPLATE_CA_ENTRYPOINT(031,L"031"); | ||
65 | TEMPLATE_CA_ENTRYPOINT(032,L"032"); | ||
66 | TEMPLATE_CA_ENTRYPOINT(033,L"033"); | ||
67 | TEMPLATE_CA_ENTRYPOINT(034,L"034"); | ||
68 | TEMPLATE_CA_ENTRYPOINT(035,L"035"); | ||
69 | TEMPLATE_CA_ENTRYPOINT(036,L"036"); | ||
70 | TEMPLATE_CA_ENTRYPOINT(037,L"037"); | ||
71 | TEMPLATE_CA_ENTRYPOINT(038,L"038"); | ||
72 | TEMPLATE_CA_ENTRYPOINT(039,L"039"); | ||
73 | TEMPLATE_CA_ENTRYPOINT(040,L"040"); | ||
74 | TEMPLATE_CA_ENTRYPOINT(041,L"041"); | ||
75 | TEMPLATE_CA_ENTRYPOINT(042,L"042"); | ||
76 | TEMPLATE_CA_ENTRYPOINT(043,L"043"); | ||
77 | TEMPLATE_CA_ENTRYPOINT(044,L"044"); | ||
78 | TEMPLATE_CA_ENTRYPOINT(045,L"045"); | ||
79 | TEMPLATE_CA_ENTRYPOINT(046,L"046"); | ||
80 | TEMPLATE_CA_ENTRYPOINT(047,L"047"); | ||
81 | TEMPLATE_CA_ENTRYPOINT(048,L"048"); | ||
82 | TEMPLATE_CA_ENTRYPOINT(049,L"049"); | ||
83 | TEMPLATE_CA_ENTRYPOINT(050,L"050"); | ||
84 | TEMPLATE_CA_ENTRYPOINT(051,L"051"); | ||
85 | TEMPLATE_CA_ENTRYPOINT(052,L"052"); | ||
86 | TEMPLATE_CA_ENTRYPOINT(053,L"053"); | ||
87 | TEMPLATE_CA_ENTRYPOINT(054,L"054"); | ||
88 | TEMPLATE_CA_ENTRYPOINT(055,L"055"); | ||
89 | TEMPLATE_CA_ENTRYPOINT(056,L"056"); | ||
90 | TEMPLATE_CA_ENTRYPOINT(057,L"057"); | ||
91 | TEMPLATE_CA_ENTRYPOINT(058,L"058"); | ||
92 | TEMPLATE_CA_ENTRYPOINT(059,L"059"); | ||
93 | TEMPLATE_CA_ENTRYPOINT(060,L"060"); | ||
94 | TEMPLATE_CA_ENTRYPOINT(061,L"061"); | ||
95 | TEMPLATE_CA_ENTRYPOINT(062,L"062"); | ||
96 | TEMPLATE_CA_ENTRYPOINT(063,L"063"); | ||
97 | TEMPLATE_CA_ENTRYPOINT(064,L"064"); | ||
98 | TEMPLATE_CA_ENTRYPOINT(065,L"065"); | ||
99 | TEMPLATE_CA_ENTRYPOINT(066,L"066"); | ||
100 | TEMPLATE_CA_ENTRYPOINT(067,L"067"); | ||
101 | TEMPLATE_CA_ENTRYPOINT(068,L"068"); | ||
102 | TEMPLATE_CA_ENTRYPOINT(069,L"069"); | ||
103 | TEMPLATE_CA_ENTRYPOINT(070,L"070"); | ||
104 | TEMPLATE_CA_ENTRYPOINT(071,L"071"); | ||
105 | TEMPLATE_CA_ENTRYPOINT(072,L"072"); | ||
106 | TEMPLATE_CA_ENTRYPOINT(073,L"073"); | ||
107 | TEMPLATE_CA_ENTRYPOINT(074,L"074"); | ||
108 | TEMPLATE_CA_ENTRYPOINT(075,L"075"); | ||
109 | TEMPLATE_CA_ENTRYPOINT(076,L"076"); | ||
110 | TEMPLATE_CA_ENTRYPOINT(077,L"077"); | ||
111 | TEMPLATE_CA_ENTRYPOINT(078,L"078"); | ||
112 | TEMPLATE_CA_ENTRYPOINT(079,L"079"); | ||
113 | TEMPLATE_CA_ENTRYPOINT(080,L"080"); | ||
114 | TEMPLATE_CA_ENTRYPOINT(081,L"081"); | ||
115 | TEMPLATE_CA_ENTRYPOINT(082,L"082"); | ||
116 | TEMPLATE_CA_ENTRYPOINT(083,L"083"); | ||
117 | TEMPLATE_CA_ENTRYPOINT(084,L"084"); | ||
118 | TEMPLATE_CA_ENTRYPOINT(085,L"085"); | ||
119 | TEMPLATE_CA_ENTRYPOINT(086,L"086"); | ||
120 | TEMPLATE_CA_ENTRYPOINT(087,L"087"); | ||
121 | TEMPLATE_CA_ENTRYPOINT(088,L"088"); | ||
122 | TEMPLATE_CA_ENTRYPOINT(089,L"089"); | ||
123 | TEMPLATE_CA_ENTRYPOINT(090,L"090"); | ||
124 | TEMPLATE_CA_ENTRYPOINT(091,L"091"); | ||
125 | TEMPLATE_CA_ENTRYPOINT(092,L"092"); | ||
126 | TEMPLATE_CA_ENTRYPOINT(093,L"093"); | ||
127 | TEMPLATE_CA_ENTRYPOINT(094,L"094"); | ||
128 | TEMPLATE_CA_ENTRYPOINT(095,L"095"); | ||
129 | TEMPLATE_CA_ENTRYPOINT(096,L"096"); | ||
130 | TEMPLATE_CA_ENTRYPOINT(097,L"097"); | ||
131 | TEMPLATE_CA_ENTRYPOINT(098,L"098"); | ||
132 | TEMPLATE_CA_ENTRYPOINT(099,L"099"); | ||
133 | TEMPLATE_CA_ENTRYPOINT(100,L"100"); | ||
134 | TEMPLATE_CA_ENTRYPOINT(101,L"101"); | ||
135 | TEMPLATE_CA_ENTRYPOINT(102,L"102"); | ||
136 | TEMPLATE_CA_ENTRYPOINT(103,L"103"); | ||
137 | TEMPLATE_CA_ENTRYPOINT(104,L"104"); | ||
138 | TEMPLATE_CA_ENTRYPOINT(105,L"105"); | ||
139 | TEMPLATE_CA_ENTRYPOINT(106,L"106"); | ||
140 | TEMPLATE_CA_ENTRYPOINT(107,L"107"); | ||
141 | TEMPLATE_CA_ENTRYPOINT(108,L"108"); | ||
142 | TEMPLATE_CA_ENTRYPOINT(109,L"109"); | ||
143 | TEMPLATE_CA_ENTRYPOINT(110,L"110"); | ||
144 | TEMPLATE_CA_ENTRYPOINT(111,L"111"); | ||
145 | TEMPLATE_CA_ENTRYPOINT(112,L"112"); | ||
146 | TEMPLATE_CA_ENTRYPOINT(113,L"113"); | ||
147 | TEMPLATE_CA_ENTRYPOINT(114,L"114"); | ||
148 | TEMPLATE_CA_ENTRYPOINT(115,L"115"); | ||
149 | TEMPLATE_CA_ENTRYPOINT(116,L"116"); | ||
150 | TEMPLATE_CA_ENTRYPOINT(117,L"117"); | ||
151 | TEMPLATE_CA_ENTRYPOINT(118,L"118"); | ||
152 | TEMPLATE_CA_ENTRYPOINT(119,L"119"); | ||
153 | TEMPLATE_CA_ENTRYPOINT(120,L"120"); | ||
154 | TEMPLATE_CA_ENTRYPOINT(121,L"121"); | ||
155 | TEMPLATE_CA_ENTRYPOINT(122,L"122"); | ||
156 | TEMPLATE_CA_ENTRYPOINT(123,L"123"); | ||
157 | TEMPLATE_CA_ENTRYPOINT(124,L"124"); | ||
158 | TEMPLATE_CA_ENTRYPOINT(125,L"125"); | ||
159 | TEMPLATE_CA_ENTRYPOINT(126,L"126"); | ||
160 | TEMPLATE_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. | ||
13 | static HFDI g_hfdi; | ||
14 | |||
15 | // FDI is not unicode-aware, so avoid passing these paths through the callbacks. | ||
16 | static const wchar_t* g_szExtractDir; | ||
17 | static 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. | ||
21 | static 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> | ||
44 | static 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> | ||
81 | static 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> | ||
98 | static 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> | ||
151 | static 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> | ||
179 | static 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> | ||
259 | int 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 | // | ||
12 | static __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 | |||
35 | typedef int (WINAPI *PMsiFunc_I_I)(int in1, __out int* out1); | ||
36 | typedef int (WINAPI *PMsiFunc_II_I)(int in1, int in2, __out int* out1); | ||
37 | typedef int (WINAPI *PMsiFunc_IS_I)(int in1, __in_z wchar_t* in2, __out int* out1); | ||
38 | typedef int (WINAPI *PMsiFunc_ISI_I)(int in1, __in_z wchar_t* in2, int in3, __out int* out1); | ||
39 | typedef int (WINAPI *PMsiFunc_ISII_I)(int in1, __in_z wchar_t* in2, int in3, int in4, __out int* out1); | ||
40 | typedef int (WINAPI *PMsiFunc_IS_II)(int in1, __in_z wchar_t* in2, __out int* out1, __out int* out2); | ||
41 | typedef MSIDBERROR (WINAPI *PMsiEFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
42 | typedef int (WINAPI *PMsiFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
43 | typedef int (WINAPI *PMsiFunc_II_S)(int in1, int in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
44 | typedef int (WINAPI *PMsiFunc_IS_S)(int in1, __in_z wchar_t* in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
45 | typedef 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 | |||
47 | UINT 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 | |||
60 | UINT 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 | |||
74 | UINT 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 | |||
88 | UINT 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 | |||
103 | UINT 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 | |||
119 | UINT 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 | |||
135 | UINT 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 | |||
157 | MSIDBERROR 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 | |||
178 | UINT 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 | |||
201 | UINT 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 | |||
224 | UINT 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 | |||
254 | void 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 | // | ||
12 | class RemoteMsiSession | ||
13 | { | ||
14 | public: | ||
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 | |||
91 | public: | ||
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 | |||
300 | private: | ||
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 | |||
858 | private: | ||
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 | |||
9 | HMODULE g_hModule; | ||
10 | bool g_fRunningOutOfProc = false; | ||
11 | |||
12 | RemoteMsiSession* g_pRemote = NULL; | ||
13 | |||
14 | // Prototypes for local functions. | ||
15 | // See the function definitions for comments. | ||
16 | |||
17 | bool 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> | ||
29 | extern "C" | ||
30 | void __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> | ||
69 | int 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> | ||
152 | void __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 "AssemblyName!Namespace.Class.Method" | ||
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> | ||
182 | int 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> | ||
253 | BOOL 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 "AssemblyName!Namespace.Class.Method" | ||
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> | ||
287 | bool 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 | |||
350 | LExit: | ||
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> | ||
10 | void 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> | ||
81 | bool 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 | |||
114 | bool 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) | ||
141 | bool 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 | |||
5 | void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...); | ||
6 | |||
7 | int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir); | ||
8 | |||
9 | bool DeleteDirectory(const wchar_t* szDir); | ||
10 | |||
11 | __success(return != false) | ||
12 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, | ||
13 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf); | ||
14 | |||
15 | bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile, | ||
16 | const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost); | ||
17 | |||
18 | bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost, | ||
19 | const wchar_t* szName, const wchar_t* szAppBase, | ||
20 | const wchar_t* szConfigFile, _AppDomain** ppAppDomain); | ||
21 | |||
22 | bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain, | ||
23 | const wchar_t* szAssembly, const wchar_t* szClass, | ||
24 | const wchar_t* szMethod, _MethodInfo** ppCAMethod); | ||
25 | |||
26 | extern HMODULE g_hModule; | ||
27 | extern bool g_fRunningOutOfProc; | ||
28 | |||
29 | extern 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") | ||
18 | using 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> | ||