v1.09 — Fix ODT download, selective removal, Display Level Full

- Direct CDN download (officecdn.microsoft.com/pr/wsus/setup.exe) instead of broken fwlink redirect
- HttpClient with proper redirect handling, content-type check, file size validation
- Selective C2R removal: each product listed separately, only checked items removed
- OfficeDetector splits ProductReleaseIds into individual entries
- Display Level="Full" for removal (shows Microsoft progress UI)
- Confirmation dialog before removal on all pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hariel1985
2026-03-31 07:33:19 +02:00
szülő f41b821d24
commit eac8813cc0
7 fájl változott, egészen pontosan 221 új sor hozzáadva és 122 régi sor törölve

Fájl megtekintése

@@ -1,16 +1,16 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace InstaSoftOfficeTool.Services
{
public class OdtDownloader
{
private const string OdtDownloadUrl = "https://go.microsoft.com/fwlink/p/?LinkID=626065";
// Direct CDN link to ODT setup.exe (no self-extractor needed)
private const string OdtDownloadUrl = "https://officecdn.microsoft.com/pr/wsus/setup.exe";
public event Action<string> StatusChanged;
public event Action<int> ProgressChanged;
public string OdtFolder { get; private set; }
public string SetupExePath { get; private set; }
@@ -24,53 +24,70 @@ namespace InstaSoftOfficeTool.Services
{
try
{
Directory.CreateDirectory(OdtFolder);
var odtExePath = Path.Combine(OdtFolder, "officedeploymenttool.exe");
SetupExePath = Path.Combine(OdtFolder, "setup.exe");
// Check if valid setup.exe already exists
if (File.Exists(SetupExePath))
{
StatusChanged?.Invoke("Az ODT setup.exe m\u00e1r el\u00e9rhet\u0151, let\u00f6lt\u00e9s kihagyva.");
return true;
}
StatusChanged?.Invoke("Office Deployment Tool let\u00f6lt\u00e9se...");
using (var client = new WebClient())
{
client.DownloadProgressChanged += (s, e) =>
var existingSize = new FileInfo(SetupExePath).Length;
if (existingSize > 1000000) // valid setup.exe is ~7MB
{
ProgressChanged?.Invoke(e.ProgressPercentage);
};
StatusChanged?.Invoke("Az ODT setup.exe m\u00e1r el\u00e9rhet\u0151 (" +
(existingSize / 1024 / 1024) + " MB).");
return true;
}
await client.DownloadFileTaskAsync(new Uri(OdtDownloadUrl), odtExePath);
// Delete corrupted file
StatusChanged?.Invoke("S\u00e9r\u00fclt setup.exe t\u00f6rl\u00e9se...");
File.Delete(SetupExePath);
}
StatusChanged?.Invoke("ODT kicsomagol\u00e1sa...");
Directory.CreateDirectory(OdtFolder);
var runner = new ProcessRunner();
var exitCode = await runner.RunAsync(odtExePath,
"/extract:\"" + OdtFolder + "\" /quiet");
// Download setup.exe directly from CDN
StatusChanged?.Invoke("Office Deployment Tool let\u00f6lt\u00e9se...");
StatusChanged?.Invoke("URL: " + OdtDownloadUrl);
if (exitCode != 0)
using (var handler = new HttpClientHandler { AllowAutoRedirect = true })
using (var client = new HttpClient(handler))
{
StatusChanged?.Invoke("Hiba: Az ODT kicsomagol\u00e1sa sikertelen (k\u00f3d: " + exitCode + ")");
client.DefaultRequestHeaders.Add("User-Agent", "InstaSoftOfficeTool/1.0");
client.Timeout = TimeSpan.FromMinutes(5);
var response = await client.GetAsync(OdtDownloadUrl);
if (!response.IsSuccessStatusCode)
{
StatusChanged?.Invoke("Hiba: HTTP " + (int)response.StatusCode + " " + response.StatusCode);
return false;
}
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
if (contentType.Contains("text/html"))
{
StatusChanged?.Invoke("Hiba: HTML oldal \u00e9rkezett exe helyett.");
StatusChanged?.Invoke("T\u00f6ltse le manu\u00e1lisan: https://www.microsoft.com/en-us/download/details.aspx?id=49117");
return false;
}
var bytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes(SetupExePath, bytes);
StatusChanged?.Invoke("Let\u00f6ltve: " + (bytes.Length / 1024) + " KB");
}
if (!File.Exists(SetupExePath) || new FileInfo(SetupExePath).Length < 1000000)
{
StatusChanged?.Invoke("Hiba: A let\u00f6lt\u00f6tt f\u00e1jl s\u00e9r\u00fclt vagy t\u00fal kicsi.");
return false;
}
if (!File.Exists(SetupExePath))
{
StatusChanged?.Invoke("Hiba: A setup.exe nem tal\u00e1lhat\u00f3 a kicsomagol\u00e1s ut\u00e1n.");
return false;
}
StatusChanged?.Invoke("ODT sikeresen let\u00f6ltve \u00e9s kicsomagolva.");
StatusChanged?.Invoke("ODT setup.exe k\u00e9sz.");
return true;
}
catch (Exception ex)
{
StatusChanged?.Invoke("Hiba a let\u00f6lt\u00e9s sor\u00e1n: " + ex.Message);
StatusChanged?.Invoke("Hiba: " + ex.GetType().Name + ": " + ex.Message);
return false;
}
}

Fájl megtekintése

@@ -46,7 +46,31 @@ namespace InstaSoftOfficeTool.Services
new XElement("Remove", new XAttribute("All", "TRUE")),
new XElement("Display",
new XAttribute("Level", "Full"),
new XAttribute("AcceptEULA", "TRUE")));
new XAttribute("AcceptEULA", "TRUE")),
new XElement("Property",
new XAttribute("Name", "FORCEAPPSHUTDOWN"),
new XAttribute("Value", "TRUE")));
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), configuration);
return doc.Declaration + "\n" + doc.Root;
}
public static string GenerateRemoveProducts(string[] productIds)
{
var remove = new XElement("Remove");
foreach (var id in productIds)
{
remove.Add(new XElement("Product", new XAttribute("ID", id)));
}
var configuration = new XElement("Configuration",
remove,
new XElement("Display",
new XAttribute("Level", "Full"),
new XAttribute("AcceptEULA", "TRUE")),
new XElement("Property",
new XAttribute("Name", "FORCEAPPSHUTDOWN"),
new XAttribute("Value", "TRUE")));
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), configuration);
return doc.Declaration + "\n" + doc.Root;

Fájl megtekintése

@@ -31,12 +31,20 @@ namespace InstaSoftOfficeTool.Services
var productIds = key.GetValue("ProductReleaseIds") as string;
var versionToReport = key.GetValue("VersionToReport") as string;
if (!string.IsNullOrEmpty(productIds))
if (string.IsNullOrEmpty(productIds)) return;
// Split into individual products (e.g. "O365BusinessRetail,VisioProRetail")
var ids = productIds.Split(',');
foreach (var id in ids)
{
var trimmedId = id.Trim();
if (string.IsNullOrEmpty(trimmedId)) continue;
results.Add(new InstalledOffice
{
DisplayName = "Microsoft Office Click-to-Run (" + productIds + ")",
DisplayName = "Microsoft Office Click-to-Run (" + trimmedId + ")",
Version = versionToReport ?? "",
ProductCode = trimmedId,
IsClickToRun = true,
IsSelected = true
});