Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#387 Patch pack-uris in CLR strings #390

Merged
merged 5 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ILRepack.IntegrationTests/ILRepack.IntegrationTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<ItemGroup>
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="FSharp.Core" Version="8.0.300" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="3.14.0" />
Expand Down
6 changes: 6 additions & 0 deletions ILRepack.IntegrationTests/Scenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public void GivenNetCore3WpfAppUsesImageResources_MergedCore3WpfApplicationRunsS
RunScenario("WPFSampleApplicationCore");
}

[Test]
public void GivenLibraryWithWpfPackUrisInClrStrings_MergedWpfApplicationRunsSuccessfully()
{
RunScenario("WPFPackUrisInClrStringsApplicationCore");
}

private void RunScenario(string scenarioName)
{
string scenarioExecutable = GetScenarioExecutable(scenarioName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Windows;

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<ui:FluentWindow x:Class="WPFPackUrisInClrStringsApplicationCore.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFPackUrisInClrStringsApplicationCore"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
mc:Ignorable="d"
Title="WPFPackUrisInClrStringsApplicationCore" Height="450" Width="800"
Loaded="FluentWindow_Loaded">
<ui:FluentWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- ThemesDictionary and ControlsDictionary only works if pack-uris inside CLR strings are also patched -->
<ui:ThemesDictionary Theme="Dark" />
<ui:ControlsDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ui:FluentWindow.Resources>
<StackPanel>
<ui:TitleBar Title="WPFPackUrisInClrStringsApplicationCore"/>
<ui:Card Margin="8">
<ui:Button Content="Change Theme" Icon="{ui:SymbolIcon Fluent24}" Click="Button_Click" />
</ui:Card>
</StackPanel>
</ui:FluentWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Threading.Tasks;
using System.Windows;
using Wpf.Ui;
using Wpf.Ui.Appearance;
using Wpf.Ui.Controls;

namespace WPFPackUrisInClrStringsApplicationCore
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : FluentWindow
{
public MainWindow()
{
InitializeComponent();

ApplicationThemeManager.Changed += ApplicationThemeManager_Changed;

ApplicationThemeManager.ApplySystemTheme(true);
}

private void ApplicationThemeManager_Changed(ApplicationTheme currentApplicationTheme, System.Windows.Media.Color systemAccent)
{
var frameworkElement = this;
ApplicationThemeManager.Apply(frameworkElement);
if (frameworkElement is Window window)
{
if (window != UiApplication.Current.MainWindow)
{
WindowBackgroundManager.UpdateBackground(
window,
currentApplicationTheme,
WindowBackdropType.None
);
}
}
}

private async void FluentWindow_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(300);
Close();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
ChangeTheme();
}

public static void ChangeTheme()
{
var currentApplicationTheme = ApplicationThemeManager.GetAppTheme();
var applicationTheme =
currentApplicationTheme == ApplicationTheme.Light
? ApplicationTheme.Dark
: ApplicationTheme.Light;

ApplicationThemeManager.Apply(applicationTheme, backgroundEffect: WindowBackdropType.Auto, updateAccent: false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Windows;

namespace WPFPackUrisInClrStringsApplicationCore
{
public class Program
{
[STAThread]
public static int Main()
{
try
{
new Application().Run(new MainWindow());
return 0;
}
catch (Exception e)
{
Console.WriteLine(e);
return 1;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<NoWarn>$(NoWarn);NU1702</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="WPF-UI" Version="4.0.0" />
</ItemGroup>

<Import Project="..\..\Repack.targets" />

</Project>
2 changes: 2 additions & 0 deletions ILRepack.Tests/ILRepack.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<ItemGroup>
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions ILRepack.sln
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Pdb", "cecil\sym
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cecil", "Cecil", "{3C913262-6FDC-43C2-9BC5-054A4016FE2E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFPackUrisInClrStringsApplicationCore", "ILRepack.IntegrationTests\Scenarios\WPFPackUrisInClrStringsApplicationCore\WPFPackUrisInClrStringsApplicationCore.csproj", "{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -136,6 +138,10 @@ Global
{2FD46DD0-A43E-4DFC-AE1F-E587141FC227}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FD46DD0-A43E-4DFC-AE1F-E587141FC227}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FD46DD0-A43E-4DFC-AE1F-E587141FC227}.Release|Any CPU.Build.0 = Release|Any CPU
{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -156,6 +162,7 @@ Global
{7B3B8608-1E8D-5830-1D03-E2ABB5C782DE} = {04819B25-ABEA-46F7-90D5-149C8304F67F}
{13529DEB-9921-4FD0-B50D-F202B9500F53} = {3C913262-6FDC-43C2-9BC5-054A4016FE2E}
{2FD46DD0-A43E-4DFC-AE1F-E587141FC227} = {3C913262-6FDC-43C2-9BC5-054A4016FE2E}
{3A114A93-FC8C-4C84-ADA6-F4E84BAEC443} = {04819B25-ABEA-46F7-90D5-149C8304F67F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54C7CB02-6231-428F-BBD0-113CC9852908}
Expand Down
59 changes: 41 additions & 18 deletions ILRepack/Steps/XamlResourcePathPatcherStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,54 @@ public XamlResourcePathPatcherStep(ILogger logger, IRepackContext repackContext)

public void Perform()
{
var types = _repackContext.TargetAssemblyDefinition.Modules.SelectMany(m => m.Types);
var relevantTypes = GetTypesWhichMayContainPackUris();

_logger.Verbose("Processing XAML resource paths ...");
foreach (var type in types)
foreach (var type in relevantTypes)
{
PatchIComponentConnector(type);
PatchWpfPackUrisInClrStrings(type);
PatchWpfToolkitVersionResourceDictionary(type);
}
}

private IEnumerable<TypeDefinition> GetTypesWhichMayContainPackUris()
{
var types = _repackContext.TargetAssemblyDefinition.Modules.SelectMany(m => m.Types);

var isModuleReferencingWpfMap = new Dictionary<ModuleDefinition, bool>();

foreach (var type in types)
{
var originalModule = _repackContext.MappingHandler.GetOriginalModule(type);
if (!isModuleReferencingWpfMap.TryGetValue(originalModule, out var isReferencingWpf))
{
isModuleReferencingWpfMap[originalModule] = isReferencingWpf = IsModuleDefinitionReferencingWpf(originalModule);
}

if (!isReferencingWpf)
{
continue;
}

yield return type;
}
}

private bool IsModuleDefinitionReferencingWpf(ModuleDefinition module)
{
// checking for PresentationFramework instead of PresentationCore, as for example
// AnotherClassLibrary only references PresenationFramework but not PresentationCore
return module.AssemblyReferences.Any(y => y.Name == "PresentationFramework");
}

private void PatchWpfPackUrisInClrStrings(TypeDefinition type)
{
foreach (var method in type.Methods.Where(x => x.HasBody))
{
PatchMethod(method);
}
}

private void PatchWpfToolkitVersionResourceDictionary(TypeDefinition type)
{
// Extended WPF toolkit has a nasty way of including the xamls in the generic.xaml
Expand All @@ -68,21 +106,6 @@ private void PatchWpfToolkitVersionResourceDictionary(TypeDefinition type)
PatchWpfToolkitEndInitMethod(endInitMethod);
}

private void PatchIComponentConnector(TypeDefinition type)
{
if (!type.Interfaces.Any(t => t.InterfaceType.FullName == "System.Windows.Markup.IComponentConnector"))
return;

var initializeMethod = type.Methods.FirstOrDefault(m =>
m.Name == "InitializeComponent" && m.Parameters.Count == 0);

if (initializeMethod == null || !initializeMethod.HasBody)
return;

_logger.Verbose(" - Patching type " + type.FullName);
PatchMethod(initializeMethod);
}

private void PatchWpfToolkitEndInitMethod(MethodDefinition method)
{
const string ComponentPathString = "{0};v{1};component/{2}";
Expand Down