mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-05-07 02:00:21 +08:00
Add MSP installer support (info panel/parser)
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
<UserControl x:Class="QuickLook.Plugin.AppViewer.InfoPanels.MspInfoPanel"
|
||||
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:local="clr-namespace:QuickLook.Plugin.AppViewer"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Height="230"
|
||||
FontSize="14"
|
||||
UseLayoutRounding="True"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="15" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="image"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Width="128"
|
||||
Height="128"
|
||||
Margin="0,-20,0,0"
|
||||
Opacity="0"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
SnapsToDevicePixels="True"
|
||||
Stretch="Fill"
|
||||
UseLayoutRounding="True">
|
||||
<Image.Style>
|
||||
<Style TargetType="{x:Type Image}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Source, ElementName=image}" Value="{x:Null}">
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation BeginTime="0:0:0"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.05" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0"
|
||||
MaxHeight="60"
|
||||
Padding="3"
|
||||
FontSize="19"
|
||||
FontWeight="SemiBold"
|
||||
LineHeight="25"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="Wrap">
|
||||
<TextBlock.Inlines>
|
||||
<Run x:Name="filename" Text="FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext" />
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<!-- Product Name -->
|
||||
<TextBlock x:Name="productNameTitle"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Product Name" />
|
||||
<TextBlock x:Name="productName"
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Searching..."
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="Wrap" />
|
||||
<!-- Product Version -->
|
||||
<TextBlock x:Name="productVersionTitle"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Product Version" />
|
||||
<TextBlock x:Name="productVersion"
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Searching..."
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="Wrap" />
|
||||
<!-- Manufacturer -->
|
||||
<TextBlock x:Name="manufacturerTitle"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Manufacturer" />
|
||||
<TextBlock x:Name="manufacturer"
|
||||
Grid.Row="5"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Searching..."
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="Wrap" />
|
||||
<!-- Total Size -->
|
||||
<TextBlock x:Name="totalSizeTitle"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Total Size" />
|
||||
<TextBlock x:Name="totalSize"
|
||||
Grid.Row="6"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Calculating size..." />
|
||||
<!-- Last Modified -->
|
||||
<TextBlock x:Name="modDateTitle"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Last Modified" />
|
||||
<TextBlock x:Name="modDate"
|
||||
Grid.Row="7"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
Padding="3"
|
||||
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||
Text="Searching..."
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using QuickLook.Common.ExtensionMethods;
|
||||
using QuickLook.Common.Helpers;
|
||||
using QuickLook.Common.Plugin;
|
||||
using QuickLook.Plugin.AppViewer.PackageParsers.Msp;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace QuickLook.Plugin.AppViewer.InfoPanels;
|
||||
|
||||
public partial class MspInfoPanel : UserControl, IAppInfoPanel
|
||||
{
|
||||
private readonly ContextObject _context;
|
||||
|
||||
public MspInfoPanel(ContextObject context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
|
||||
productNameTitle.Text = TranslationHelper.Get("PRODUCT_NAME", translationFile);
|
||||
// MSP has no product version; show the patch description instead.
|
||||
productVersionTitle.Text = TranslationHelper.Get("DESCRIPTION", translationFile);
|
||||
manufacturerTitle.Text = TranslationHelper.Get("MANUFACTURER", translationFile);
|
||||
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
|
||||
modDateTitle.Text = TranslationHelper.Get("LAST_MODIFIED", translationFile);
|
||||
}
|
||||
|
||||
public void DisplayInfo(string path)
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
var scale = DisplayDeviceHelper.GetCurrentScaleFactor();
|
||||
|
||||
var icon =
|
||||
WindowsThumbnailProvider.GetThumbnail(path,
|
||||
(int)(128 * scale.Horizontal),
|
||||
(int)(128 * scale.Vertical),
|
||||
ThumbnailOptions.ScaleUp);
|
||||
|
||||
var source = icon?.ToBitmapSource();
|
||||
icon?.Dispose();
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() => image.Source = source));
|
||||
});
|
||||
|
||||
var name = Path.GetFileName(path);
|
||||
filename.Text = string.IsNullOrEmpty(name) ? path : name;
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var size = new FileInfo(path).Length;
|
||||
MspInfo mspInfo = MspParser.Parse(path);
|
||||
var last = File.GetLastWriteTime(path);
|
||||
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
productName.Text = mspInfo.DisplayName;
|
||||
productVersion.Text = mspInfo.Description;
|
||||
manufacturer.Text = mspInfo.Manufacturer;
|
||||
totalSize.Text = size.ToPrettySize(2);
|
||||
modDate.Text = last.ToString(CultureInfo.CurrentCulture);
|
||||
|
||||
_context.IsBusy = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace QuickLook.Plugin.AppViewer.PackageParsers.Msp;
|
||||
|
||||
public sealed class MspInfo
|
||||
{
|
||||
/// <summary>Summary Information Subject — patch display name.</summary>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>Summary Information Comments — patch description.</summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>Summary Information Author — manufacturer / creator.</summary>
|
||||
public string Manufacturer { get; set; }
|
||||
|
||||
/// <summary>Summary Information RevisionNumber — patch GUID.</summary>
|
||||
public string PatchCode { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using WixToolset.Dtf.WindowsInstaller;
|
||||
|
||||
namespace QuickLook.Plugin.AppViewer.PackageParsers.Msp;
|
||||
|
||||
public static class MspParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads metadata from an MSP patch file via the Summary Information stream.
|
||||
/// MSP files cannot be opened with <see cref="Database"/>; use
|
||||
/// <see cref="SummaryInfo"/> (wraps MsiGetSummaryInformation) instead.
|
||||
/// </summary>
|
||||
public static MspInfo Parse(string path)
|
||||
{
|
||||
MspInfo info = new();
|
||||
|
||||
using var si = new SummaryInfo(path, enableWrite: false);
|
||||
|
||||
// Subject is the human-readable patch name; fall back to Title if absent.
|
||||
info.DisplayName = string.IsNullOrWhiteSpace(si.Subject)
|
||||
? (si.Title ?? string.Empty)
|
||||
: si.Subject;
|
||||
|
||||
info.Description = si.Comments ?? string.Empty;
|
||||
info.Manufacturer = si.Author ?? string.Empty;
|
||||
info.PatchCode = si.RevisionNumber ?? string.Empty;
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public sealed class Plugin : IViewer
|
||||
// Windows
|
||||
".appx", ".appxbundle", // Windows APPX installer
|
||||
".msi", // Windows MSI installer
|
||||
".msp", // Windows MSP installer
|
||||
".msix", ".msixbundle", // Windows MSIX installer
|
||||
|
||||
// macOS
|
||||
@@ -79,7 +80,7 @@ public sealed class Plugin : IViewer
|
||||
".apk" or ".aab" => new Size { Width = 600, Height = 510 },
|
||||
".ipa" => new Size { Width = 560, Height = 510 },
|
||||
".hap" => new Size { Width = 560, Height = 500 },
|
||||
".msi" => new Size { Width = 560, Height = 230 },
|
||||
".msi" or ".msp" => new Size { Width = 560, Height = 230 },
|
||||
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new Size { Width = 560, Height = 328 },
|
||||
".ddeb" or ".deb" => new Size { Width = 600, Height = 345 },
|
||||
".dmg" => new Size { Width = 560, Height = 510 },
|
||||
@@ -104,6 +105,7 @@ public sealed class Plugin : IViewer
|
||||
".ipa" => new IpaInfoPanel(context),
|
||||
".hap" => new HapInfoPanel(context),
|
||||
".msi" => new MsiInfoPanel(context),
|
||||
".msp" => new MspInfoPanel(context),
|
||||
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new AppxInfoPanel(context),
|
||||
".ddeb" or ".deb" => new DebInfoPanel(context),
|
||||
".dmg" => new DmgInfoPanel(context),
|
||||
|
||||
Reference in New Issue
Block a user