Update to net8.0-windows and C# 12

This commit is contained in:
Yoshi Askharoun
2024-05-20 21:53:52 -05:00
parent a2cf29e1ed
commit 49479d4cd9
11 changed files with 989 additions and 1042 deletions
+69 -72
View File
@@ -6,85 +6,82 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using ZuneModCore.Mods; using ZuneModCore.Mods;
namespace ZuneModCore namespace ZuneModCore;
public abstract class Mod
{ {
public abstract class Mod /// <summary>
/// A list of all available mods
/// </summary>
public static readonly IReadOnlyList<Type> AvailableModTypes = new List<Type>
{ {
/// <summary> typeof(FeaturesOverrideMod),
/// A list of all available mods typeof(VideoSyncMod),
/// </summary> typeof(WebservicesMod),
public static readonly IReadOnlyList<Type> AvailableModTypes = new List<Type> typeof(BackgroundImageMod),
typeof(MbidLocatorMod),
}.AsReadOnly();
public static string DefaultZuneInstallDir { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
public string ZuneInstallDir { get; set; } = DefaultZuneInstallDir;
private static List<Mod>? _mods;
public static IReadOnlyList<Mod> AvailableMods
{
get
{ {
typeof(FeaturesOverrideMod), if (_mods is null)
typeof(VideoSyncMod),
typeof(WebservicesMod),
typeof(BackgroundImageMod),
typeof(MbidLocatorMod),
}.AsReadOnly();
public static string DefaultZuneInstallDir { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
public string ZuneInstallDir { get; set; } = DefaultZuneInstallDir;
private static List<Mod>? _mods;
public static IReadOnlyList<Mod> AvailableMods
{
get
{ {
if (_mods is null) _mods = new(AvailableModTypes.Count);
var services = CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default;
foreach (var modType in AvailableModTypes)
{ {
_mods = new(AvailableModTypes.Count); var instance = ActivatorUtilities.CreateInstance(services, modType);
var services = CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default; if (instance is Mod mod)
foreach (var modType in AvailableModTypes) _mods.Add(mod);
{
var instance = ActivatorUtilities.CreateInstance(services, modType);
if (instance is Mod mod)
_mods.Add(mod);
}
} }
return _mods;
} }
return _mods;
} }
public abstract string Id { get; }
public abstract string Title { get; }
public abstract string Description { get; }
public abstract string Author { get; }
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
public virtual Task Init() => Task.CompletedTask;
public abstract Task<string?> Apply();
public abstract Task<string?> Reset();
private AbstractUICollection? _OptionsUI;
public AbstractUICollection? OptionsUI
{
get
{
if (_OptionsUI == null)
_OptionsUI = GetDefaultOptionsUI();
return _OptionsUI;
}
set => _OptionsUI = value;
}
public string StorageDirectory
{
get
{
string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZuneModCore", Id);
// Create the directory just in case the consumer assumes the folder exists already
Directory.CreateDirectory(dir);
return dir;
}
}
public abstract IReadOnlyList<Type>? DependentMods { get; }
} }
public abstract string Id { get; }
public abstract string Title { get; }
public abstract string Description { get; }
public abstract string Author { get; }
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
public virtual Task Init() => Task.CompletedTask;
public abstract Task<string?> Apply();
public abstract Task<string?> Reset();
private AbstractUICollection? _OptionsUI;
public AbstractUICollection? OptionsUI
{
get
{
return _OptionsUI ??= GetDefaultOptionsUI();
}
set => _OptionsUI = value;
}
public string StorageDirectory
{
get
{
string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZuneModCore", Id);
// Create the directory just in case the consumer assumes the folder exists already
Directory.CreateDirectory(dir);
return dir;
}
}
public abstract IReadOnlyList<Type>? DependentMods { get; }
} }
+104 -105
View File
@@ -7,126 +7,125 @@ using System.Threading.Tasks;
using Vestris.ResourceLib; using Vestris.ResourceLib;
using ZuneModCore.Win32; using ZuneModCore.Win32;
namespace ZuneModCore.Mods namespace ZuneModCore.Mods;
public class BackgroundImageMod : Mod
{ {
public class BackgroundImageMod : Mod public override string Id => nameof(BackgroundImageMod);
public override string Title => "Background Image";
public override string Description => "Replaces the \"Zero\" background with an image of your choice.";
public override string Author => "Joshua \"Yoshi\" Askharoun";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
public override string Id => nameof(BackgroundImageMod); AbstractUICollection optionsUi = new(nameof(BackgroundImageMod))
public override string Title => "Background Image";
public override string Description => "Replaces the \"Zero\" background with an image of your choice.";
public override string Author => "Joshua \"Yoshi\" Askharoun";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
AbstractUICollection optionsUi = new(nameof(BackgroundImageMod)) new AbstractTextBox("fileBox", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
{ };
new AbstractTextBox("fileBox", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)), optionsUi.Title = "select background";
}; optionsUi.Subtitle = "CHOOSE A BACKGROUND IMAGE OF YOUR CHOICE.";
optionsUi.Title = "select background"; return optionsUi;
optionsUi.Subtitle = "CHOOSE A BACKGROUND IMAGE OF YOUR CHOICE."; }
return optionsUi;
public override IReadOnlyList<Type>? DependentMods => null;
public override async Task<string?> Apply()
{
string bgimgPath = ((AbstractTextBox)OptionsUI![0]).Value;
FileInfo bgimgInfo = new(bgimgPath);
if (!bgimgInfo.Exists)
{
return $"The file '{bgimgInfo.FullName}' does not exist.";
} }
public override IReadOnlyList<Type>? DependentMods => null; FileInfo zsrDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"));
if (!zsrDllInfo.Exists)
public override async Task<string?> Apply()
{ {
string bgimgPath = ((AbstractTextBox)OptionsUI![0]).Value; return $"The file '{zsrDllInfo.FullName}' does not exist.";
FileInfo bgimgInfo = new(bgimgPath); }
if (!bgimgInfo.Exists)
// Make a backup if it doesn't already exist
FileInfo zsrDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"));
if (!zsrDllBackupInfo.Exists)
{
File.Copy(zsrDllInfo.FullName, zsrDllBackupInfo.FullName, true);
}
string zsrDllTempPath = Path.Combine(StorageDirectory, zsrDllInfo.Name);
// Take ownership of DLL
TokenManipulator.TakeOwnership(zsrDllInfo);
try
{
using (ResourceInfo vi = new())
{ {
return $"The file '{bgimgInfo.FullName}' does not exist."; // Load the RCDATA section
} vi.Load(zsrDllInfo.FullName);
ResourceId? resId = vi.ResourceTypes.Find(id =>
FileInfo zsrDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"));
if (!zsrDllInfo.Exists)
{
return $"The file '{zsrDllInfo.FullName}' does not exist.";
}
// Make a backup if it doesn't already exist
FileInfo zsrDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"));
if (!zsrDllBackupInfo.Exists)
{
File.Copy(zsrDllInfo.FullName, zsrDllBackupInfo.FullName, true);
}
string zsrDllTempPath = Path.Combine(StorageDirectory, zsrDllInfo.Name);
// Take ownership of DLL
TokenManipulator.TakeOwnership(zsrDllInfo);
try
{
using (ResourceInfo vi = new())
{ {
// Load the RCDATA section // This must be wrapped in a try-catch block,
vi.Load(zsrDllInfo.FullName); // otherwise resourcelib will error when attempting
ResourceId? resId = vi.ResourceTypes.Find(id => // to read the WAVE type (or any other non-default type).
try
{ {
// This must be wrapped in a try-catch block, return id.ResourceType == Kernel32.ResourceTypes.RT_RCDATA;
// otherwise resourcelib will error when attempting }
// to read the WAVE type (or any other non-default type). catch
try {
{ return false;
return id.ResourceType == Kernel32.ResourceTypes.RT_RCDATA; }
} });
catch if (resId == null)
{ return $"Failed to locate image resources in Zune software.";
return false;
}
});
if (resId == null)
return $"Failed to locate image resources in Zune software.";
// Locate the image resource // Locate the image resource
// TODO: Allow users to specify which background they want to replace // TODO: Allow users to specify which background they want to replace
List<Resource> RCDATA = vi.Resources[resId]; List<Resource> RCDATA = vi.Resources[resId];
int idx = RCDATA.FindIndex(r => r.Name.Name == "FRAMEBACKGROUND06.PNG"); int idx = RCDATA.FindIndex(r => r.Name.Name == "FRAMEBACKGROUND06.PNG");
if (idx < 0) if (idx < 0)
return $"Failed to locate background image resource in Zune software."; return $"Failed to locate background image resource in Zune software.";
GenericResource ogRes = (GenericResource)RCDATA[idx]; GenericResource ogRes = (GenericResource)RCDATA[idx];
// Replace image // Replace image
byte[] data = await File.ReadAllBytesAsync(bgimgInfo.FullName); byte[] data = await File.ReadAllBytesAsync(bgimgInfo.FullName);
BitmapResource newRes = new(IntPtr.Zero, IntPtr.Zero, resId, ogRes.Name, ogRes.Language, data.Length); BitmapResource newRes = new(IntPtr.Zero, IntPtr.Zero, resId, ogRes.Name, ogRes.Language, data.Length);
newRes.Bitmap = new(data); newRes.Bitmap = new(data);
RCDATA[idx] = newRes; RCDATA[idx] = newRes;
File.Copy(zsrDllInfo.FullName, zsrDllTempPath, true); File.Copy(zsrDllInfo.FullName, zsrDllTempPath, true);
newRes.SaveTo(zsrDllTempPath); newRes.SaveTo(zsrDllTempPath);
} }
File.Copy(zsrDllTempPath, zsrDllInfo.FullName, true); File.Copy(zsrDllTempPath, zsrDllInfo.FullName, true);
return null; return null;
}
catch (IOException)
{
return $"Unable to replace '{zsrDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return ex.Message;
}
} }
catch (IOException)
public override Task<string?> Reset()
{ {
try return $"Unable to replace '{zsrDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
{ }
// Copy backup to application folder catch (Exception ex)
File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"), {
Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true); Debug.WriteLine(ex.ToString());
return ex.Message;
}
}
return Task.FromResult<string?>(null); public override Task<string?> Reset()
} {
catch (Exception ex) try
{ {
return Task.FromResult(ex.Message)!; // Copy backup to application folder
} File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"),
Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true);
return Task.FromResult<string?>(null);
}
catch (Exception ex)
{
return Task.FromResult(ex.Message)!;
} }
} }
} }
+89 -90
View File
@@ -4,116 +4,115 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using ZuneModCore.Win32; using ZuneModCore.Win32;
namespace ZuneModCore.Mods namespace ZuneModCore.Mods;
public class FeaturesOverrideMod : Mod
{ {
public class FeaturesOverrideMod : Mod private const string ZUNE_FEATURESOVERRIDE_REGKEY = RegEdit.ZUNE_REG_PATH + "FeaturesOverride";
public override string Id => nameof(FeaturesOverrideMod);
public override string Title => "Features Override";
public override string Description => "Re-enables access to some features disabled by Microsoft, such as the Social and Marketplace tabs.\r\n" +
"Does not restore functionality of those features, but shows them in the software.";
public override string Author => "Rafael Rivera";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
private const string ZUNE_FEATURESOVERRIDE_REGKEY = RegEdit.ZUNE_REG_PATH + "FeaturesOverride"; AbstractUICollection optionsUi = new(nameof(FeaturesOverrideMod))
public override string Id => nameof(FeaturesOverrideMod);
public override string Title => "Features Override";
public override string Description => "Re-enables access to some features disabled by Microsoft, such as the Social and Marketplace tabs.\r\n" +
"Does not restore functionality of those features, but shows them in the software.";
public override string Author => "Rafael Rivera";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
AbstractUICollection optionsUi = new(nameof(FeaturesOverrideMod)) // We don't know what some of these overrides do exactly, so hide them from the user.
{ // The ID is the name of the registry key, the label is the display name
// We don't know what some of these overrides do exactly, so hide them from the user.
// The ID is the name of the registry key, the label is the display name
new AbstractBoolean("Apps", "Apps"), new AbstractBoolean("Apps", "Apps"),
new AbstractBoolean("Art", "Art"), new AbstractBoolean("Art", "Art"),
new AbstractBoolean("Channels", "Channels"), new AbstractBoolean("Channels", "Channels"),
new AbstractBoolean("FirstLaunchIntroVideo", "First Launch Intro Video"), new AbstractBoolean("FirstLaunchIntroVideo", "First Launch Intro Video"),
new AbstractBoolean("Games", "Games"), new AbstractBoolean("Games", "Games"),
new AbstractBoolean("Marketplace", "Marketplace"), new AbstractBoolean("Marketplace", "Marketplace"),
new AbstractBoolean("MBRPreview", "[Marketplace] Media Preview"), new AbstractBoolean("MBRPreview", "[Marketplace] Media Preview"),
new AbstractBoolean("MBRPurchase", "[Marketplace] Media Purchase"), new AbstractBoolean("MBRPurchase", "[Marketplace] Media Purchase"),
new AbstractBoolean("MBRRental", "[Marketplace] Media Rental"), new AbstractBoolean("MBRRental", "[Marketplace] Media Rental"),
new AbstractBoolean("Music", "Music"), new AbstractBoolean("Music", "Music"),
new AbstractBoolean("MusicVideos", "Music Videos"), new AbstractBoolean("MusicVideos", "Music Videos"),
new AbstractBoolean("Nowplaying", "Now Playing"), new AbstractBoolean("Nowplaying", "Now Playing"),
new AbstractBoolean("NowplayingArt", "Now Playing Art"), new AbstractBoolean("NowplayingArt", "Now Playing Art"),
new AbstractBoolean("Picks", "Picks"), new AbstractBoolean("Picks", "Picks"),
new AbstractBoolean("Podcasts", "Podcasts"), new AbstractBoolean("Podcasts", "Podcasts"),
new AbstractBoolean("QuickMixLocal", "Quick Mix (Local)"), new AbstractBoolean("QuickMixLocal", "Quick Mix (Local)"),
new AbstractBoolean("QuickMixZMP", "Quick Mix (ZMP)"), new AbstractBoolean("QuickMixZMP", "Quick Mix (ZMP)"),
new AbstractBoolean("Quickplay", "Quickplay"), new AbstractBoolean("Quickplay", "Quickplay"),
new AbstractBoolean("Sign In Available", "Sign In"), new AbstractBoolean("Sign In Available", "Sign In"),
new AbstractBoolean("Social", "Social"), new AbstractBoolean("Social", "Social"),
new AbstractBoolean("SocialMarketplace", "Social Marketplace"), new AbstractBoolean("SocialMarketplace", "Social Marketplace"),
new AbstractBoolean("SubscriptionFreeTracks", "Subscription Free Tracks"), new AbstractBoolean("SubscriptionFreeTracks", "Subscription Free Tracks"),
new AbstractBoolean("Videos", "Videos"), new AbstractBoolean("Videos", "Videos"),
}; };
optionsUi.Title = "select features"; optionsUi.Title = "select features";
optionsUi.Subtitle = "CHOOSE WHICH ZUNE FEATURES YOU WISH TO ENABLE."; optionsUi.Subtitle = "CHOOSE WHICH ZUNE FEATURES YOU WISH TO ENABLE.";
return optionsUi; return optionsUi;
} }
public override IReadOnlyList<Type>? DependentMods => null; public override IReadOnlyList<Type>? DependentMods => null;
public override Task Init() public override Task Init()
{
foreach (AbstractUIElement uiElem in OptionsUI!)
{ {
foreach (AbstractUIElement uiElem in OptionsUI!) if (uiElem is AbstractBoolean boolElem)
{ {
if (uiElem is AbstractBoolean boolElem) bool? featureOverride = GetFeatureOverride(boolElem.Id);
{ boolElem.State = featureOverride ?? false;
bool? featureOverride = GetFeatureOverride(boolElem.Id);
boolElem.State = featureOverride ?? false;
}
} }
return Task.CompletedTask;
} }
public override async Task<string?> Apply() => await Apply(false); return Task.CompletedTask;
}
public async Task<string?> Apply(bool applyAll = false) public override async Task<string?> Apply() => await Apply(false);
public async Task<string?> Apply(bool applyAll = false)
{
// Use user choices from AbstractUI
foreach (AbstractUIElement uiElem in OptionsUI!)
{ {
// Use user choices from AbstractUI if (uiElem is AbstractBoolean boolElem && (boolElem.State || applyAll))
foreach (AbstractUIElement uiElem in OptionsUI!)
{ {
if (uiElem is AbstractBoolean boolElem && (boolElem.State || applyAll)) bool isSuccess = SetFeatureOverride(boolElem.Id, true);
if (!isSuccess)
{ {
bool isSuccess = SetFeatureOverride(boolElem.Id, true); string? resetStatus = await Reset();
if (!isSuccess) if (resetStatus != null)
{ {
string? resetStatus = await Reset(); // The reset failed as well, return both errors
if (resetStatus != null) return "Failed to set registry keys. Unable to clean up partial overrides:\r\n" + resetStatus;
{ }
// The reset failed as well, return both errors else
return "Failed to set registry keys. Unable to clean up partial overrides:\r\n" + resetStatus; {
} return "Failed to set registry keys. Automatically cleaned up partial changes.";
else
{
return "Failed to set registry keys. Automatically cleaned up partial changes.";
}
} }
} }
} }
return null;
} }
public override async Task<string?> Reset() return null;
{
RegEdit.CurrentUserDeleteKey(ZUNE_FEATURESOVERRIDE_REGKEY);
return null;
}
public static bool SetFeatureOverride(string feature, bool value) =>
RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value);
public static bool? GetFeatureOverride(string feature) =>
RegEdit.CurrentUserGetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
public static void ResetFeatureOverride(string feature) =>
RegEdit.CurrentUserDeleteValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
} }
public override async Task<string?> Reset()
{
RegEdit.CurrentUserDeleteKey(ZUNE_FEATURESOVERRIDE_REGKEY);
return null;
}
public static bool SetFeatureOverride(string feature, bool value) =>
RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value);
public static bool? GetFeatureOverride(string feature) =>
RegEdit.CurrentUserGetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
public static void ResetFeatureOverride(string feature) =>
RegEdit.CurrentUserDeleteValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
} }
+139 -143
View File
@@ -5,167 +5,163 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ZuneModCore.Mods namespace ZuneModCore.Mods;
public class MbidLocatorMod : Mod
{ {
public class MbidLocatorMod : Mod public const string ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME = "ZuneAlbumArtistMediaID";
public const string ZUNE_ALBUM_MEDIA_ID_NAME = "ZuneAlbumMediaID";
public const string ZUNE_MEDIA_ID_NAME = "ZuneMediaID";
public const string ZUNE_COLLECTION_ID_NAME = "ZuneCollectionID";
private static readonly string[] KNOWN_EXTS = [".mp3", ".mp4", ".m4a", ".wav"];
public override string Id => nameof(MbidLocatorMod);
public override string Title => "MusicBrainz ID Locator";
public override string Description => "Puts MusicBrainz IDs added by MusicBrainz Picard where the Zune " +
"software can use it to show additional information provided by Community Webservices.\r\n" +
"Note that this will only have an effect if you have used MusicBrainz Picard on your music library.";
public override string Author => "Joshua \"Yoshi\" Askharoun";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
public const string ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME = "ZuneAlbumArtistMediaID"; AbstractUICollection optionsUi = new(nameof(MbidLocatorMod))
public const string ZUNE_ALBUM_MEDIA_ID_NAME = "ZuneAlbumMediaID";
public const string ZUNE_MEDIA_ID_NAME = "ZuneMediaID";
public const string ZUNE_COLLECTION_ID_NAME = "ZuneCollectionID";
private static readonly string[] KNOWN_EXTS = new[]
{ {
".mp3", ".mp4", ".m4a", ".wav" new AbstractTextBox("folderBox", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)),
new AbstractBoolean("recursiveBox", "Search recursively")
}; };
optionsUi.Title = "select music";
optionsUi.Subtitle = "CHOOSE YOUR MUSIC FOLDER.";
return optionsUi;
}
public override string Id => nameof(MbidLocatorMod); public override IReadOnlyList<Type>? DependentMods => null;
public override string Title => "MusicBrainz ID Locator"; public override async Task<string?> Apply()
{
// Use user choices from AbstractUI
string folderPath = ((AbstractTextBox)OptionsUI![0]).Value;
bool recursive = ((AbstractBoolean)OptionsUI[1]).State;
string errorString = string.Empty;
public override string Description => "Puts MusicBrainz IDs added by MusicBrainz Picard where the Zune " + // Verify that the folder exists
"software can use it to show additional information provided by Community Webservices.\r\n" + DirectoryInfo folder = new(folderPath);
"Note that this will only have an effect if you have used MusicBrainz Picard on your music library."; if (!folder.Exists)
public override string Author => "Joshua \"Yoshi\" Askharoun";
public override AbstractUICollection? GetDefaultOptionsUI()
{ {
AbstractUICollection optionsUi = new(nameof(MbidLocatorMod)) return $"The folder '{folder.FullName}' does not exist.";
{
new AbstractTextBox("folderBox", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)),
new AbstractBoolean("recursiveBox", "Search recursively")
};
optionsUi.Title = "select music";
optionsUi.Subtitle = "CHOOSE YOUR MUSIC FOLDER.";
return optionsUi;
} }
public override IReadOnlyList<Type>? DependentMods => null; foreach (FileInfo file in folder.GetFiles("*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
public override async Task<string?> Apply()
{ {
// Use user choices from AbstractUI try
string folderPath = ((AbstractTextBox)OptionsUI![0]).Value;
bool recursive = ((AbstractBoolean)OptionsUI[1]).State;
string errorString = string.Empty;
// Verify that the folder exists
DirectoryInfo folder = new(folderPath);
if (!folder.Exists)
{ {
return $"The folder '{folder.FullName}' does not exist."; if (KNOWN_EXTS.Contains(file.Extension))
} UpdateMbidInFile(file);
foreach (FileInfo file in folder.GetFiles("*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
{
try
{
if (KNOWN_EXTS.Contains(file.Extension))
UpdateMbidInFile(file);
#if DEBUG #if DEBUG
System.Diagnostics.Debug.WriteLine(file); System.Diagnostics.Debug.WriteLine(file);
#endif #endif
}
catch (Exception ex)
{
errorString += ex.Message + "\r\n";
continue;
}
} }
catch (Exception ex)
if (errorString.Length != 0)
return errorString;
else
return null;
}
public override Task<string?> Reset()
{
return Task.FromResult<string?>(null);
}
public static void UpdateMbidInFile(FileInfo file)
{
var tfile = TagLib.File.Create(file.FullName);
if (tfile.Tag is TagLib.Asf.Tag asfTag)
{ {
var albumArtistIdDesc = asfTag.ExtendedContentDescriptionObject errorString += ex.Message + "\r\n";
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME); continue;
if (albumArtistIdDesc == null && asfTag.MusicBrainzReleaseArtistId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseArtistId));
}
var albumIdDesc = asfTag.ExtendedContentDescriptionObject
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_MEDIA_ID_NAME);
if (albumIdDesc == null && asfTag.MusicBrainzReleaseId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_ALBUM_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseId));
}
var trackIdDesc = asfTag.ExtendedContentDescriptionObject
.FirstOrDefault(cd => cd.Name == ZUNE_MEDIA_ID_NAME);
if (trackIdDesc == null && asfTag.MusicBrainzTrackId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_MEDIA_ID_NAME, asfTag.MusicBrainzTrackId));
}
} }
else if (tfile.Tag is TagLib.NonContainer.Tag ncTag)
{
var tag = ncTag.Tags.FirstOrDefault(t => t.TagTypes == TagLib.TagTypes.Id3v2);
if (tag is TagLib.Id3v2.Tag id3v2)
{
var frames = id3v2.GetFrames<TagLib.Id3v2.PrivateFrame>();
// Note the use of .ToList(): this is because calling RemoveFrame() will
// modify the underlying collection, causing an exception to be thrown
// when attempting the next iteration of the for loop
if (tag.MusicBrainzReleaseArtistId != null)
{
foreach (var oldAlbumArtistFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldAlbumArtistFrame);
TagLib.Id3v2.PrivateFrame albumArtistFrame = new(ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzReleaseArtistId).ToByteArray())
};
id3v2.AddFrame(albumArtistFrame);
}
if (tag.MusicBrainzReleaseId != null)
{
foreach (var oldAlbumFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldAlbumFrame);
TagLib.Id3v2.PrivateFrame albumFrame = new(ZUNE_ALBUM_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzReleaseId).ToByteArray())
};
id3v2.AddFrame(albumFrame);
}
if (tag.MusicBrainzTrackId != null)
{
foreach (var oldTrackFrame in frames.Where(f => f.Owner == ZUNE_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldTrackFrame);
TagLib.Id3v2.PrivateFrame trackFrame = new(ZUNE_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzTrackId).ToByteArray())
};
id3v2.AddFrame(trackFrame);
}
}
}
tfile.Save();
} }
if (errorString.Length != 0)
return errorString;
else
return null;
}
public override Task<string?> Reset()
{
return Task.FromResult<string?>(null);
}
public static void UpdateMbidInFile(FileInfo file)
{
var tfile = TagLib.File.Create(file.FullName);
if (tfile.Tag is TagLib.Asf.Tag asfTag)
{
var albumArtistIdDesc = asfTag.ExtendedContentDescriptionObject
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME);
if (albumArtistIdDesc == null && asfTag.MusicBrainzReleaseArtistId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseArtistId));
}
var albumIdDesc = asfTag.ExtendedContentDescriptionObject
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_MEDIA_ID_NAME);
if (albumIdDesc == null && asfTag.MusicBrainzReleaseId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_ALBUM_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseId));
}
var trackIdDesc = asfTag.ExtendedContentDescriptionObject
.FirstOrDefault(cd => cd.Name == ZUNE_MEDIA_ID_NAME);
if (trackIdDesc == null && asfTag.MusicBrainzTrackId != null)
{
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
ZUNE_MEDIA_ID_NAME, asfTag.MusicBrainzTrackId));
}
}
else if (tfile.Tag is TagLib.NonContainer.Tag ncTag)
{
var tag = ncTag.Tags.FirstOrDefault(t => t.TagTypes == TagLib.TagTypes.Id3v2);
if (tag is TagLib.Id3v2.Tag id3v2)
{
var frames = id3v2.GetFrames<TagLib.Id3v2.PrivateFrame>();
// Note the use of .ToList(): this is because calling RemoveFrame() will
// modify the underlying collection, causing an exception to be thrown
// when attempting the next iteration of the for loop
if (tag.MusicBrainzReleaseArtistId != null)
{
foreach (var oldAlbumArtistFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldAlbumArtistFrame);
TagLib.Id3v2.PrivateFrame albumArtistFrame = new(ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzReleaseArtistId).ToByteArray())
};
id3v2.AddFrame(albumArtistFrame);
}
if (tag.MusicBrainzReleaseId != null)
{
foreach (var oldAlbumFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldAlbumFrame);
TagLib.Id3v2.PrivateFrame albumFrame = new(ZUNE_ALBUM_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzReleaseId).ToByteArray())
};
id3v2.AddFrame(albumFrame);
}
if (tag.MusicBrainzTrackId != null)
{
foreach (var oldTrackFrame in frames.Where(f => f.Owner == ZUNE_MEDIA_ID_NAME).ToList())
id3v2.RemoveFrame(oldTrackFrame);
TagLib.Id3v2.PrivateFrame trackFrame = new(ZUNE_MEDIA_ID_NAME)
{
PrivateData = new(new Guid(tag.MusicBrainzTrackId).ToByteArray())
};
id3v2.AddFrame(trackFrame);
}
}
}
tfile.Save();
} }
} }
+74 -75
View File
@@ -4,92 +4,91 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ZuneModCore.Mods namespace ZuneModCore.Mods;
public class VideoSyncMod : Mod
{ {
public class VideoSyncMod : Mod private const int ZUNEENCENG_WMVCORA_OFFSET = 0x161E;
public override string Title => "Fix Video Sync";
public override string Description =>
"Resolves \"Error C00D11CD\" when attempting to sync video to a Zune device using Windows 10 1607 (Anniversary Update) or newer";
public override string Author => "sylvathemoth";
public override string Id => nameof(VideoSyncMod);
public override async Task<string?> Apply()
{ {
private const int ZUNEENCENG_WMVCORA_OFFSET = 0x161E; // Verify that ZuneEncEng.dll exists
FileInfo zeeDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneEncEng.dll"));
if (!zeeDllInfo.Exists)
return $"The file '{zeeDllInfo.FullName}' does not exist.";
public override string Title => "Fix Video Sync"; // Make a backup if it doesn't already exist
FileInfo zeeDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"));
if (!zeeDllBackupInfo.Exists)
File.Copy(zeeDllInfo.FullName, zeeDllBackupInfo.FullName, true);
public override string Description => try
"Resolves \"Error C00D11CD\" when attempting to sync video to a Zune device using Windows 10 1607 (Anniversary Update) or newer";
public override string Author => "sylvathemoth";
public override string Id => nameof(VideoSyncMod);
public override async Task<string?> Apply()
{ {
// Verify that ZuneEncEng.dll exists // Verify that the DLL is from v4.8 (other versions not tested)
FileInfo zeeDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneEncEng.dll")); var zeeDllVersion = FileVersionInfo.GetVersionInfo(zeeDllInfo.FullName);
if (!zeeDllInfo.Exists) if (zeeDllVersion is null || zeeDllVersion.FileMajorPart != 4 || zeeDllVersion.FileMinorPart != 8)
return $"The file '{zeeDllInfo.FullName}' does not exist."; return "This mod has not been tested on versions earlier than 4.8.";
// Make a backup if it doesn't already exist // Open the file
FileInfo zeeDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll")); using (FileStream zeeDll = zeeDllInfo.Open(FileMode.Open))
if (!zeeDllBackupInfo.Exists) using (BinaryWriter zeeDllWriter = new(zeeDll))
File.Copy(zeeDllInfo.FullName, zeeDllBackupInfo.FullName, true);
try
{ {
// Verify that the DLL is from v4.8 (other versions not tested) // Patch ZuneEncEng.dll to point to the local WMVCORE.DLL
var zeeDllVersion = FileVersionInfo.GetVersionInfo(zeeDllInfo.FullName); zeeDllWriter.Seek(ZUNEENCENG_WMVCORA_OFFSET, SeekOrigin.Begin);
if (zeeDllVersion is null || zeeDllVersion.FileMajorPart != 4 || zeeDllVersion.FileMinorPart != 8) zeeDllWriter.Write((byte)'a');
return "This mod has not been tested on versions earlier than 4.8.";
// Open the file zeeDllWriter.Flush();
using (FileStream zeeDll = zeeDllInfo.Open(FileMode.Open))
using (BinaryWriter zeeDllWriter = new(zeeDll))
{
// Patch ZuneEncEng.dll to point to the local WMVCORE.DLL
zeeDllWriter.Seek(ZUNEENCENG_WMVCORA_OFFSET, SeekOrigin.Begin);
zeeDllWriter.Write((byte)'a');
zeeDllWriter.Flush();
}
// Get the working WMVCORE.dll
byte[] wmvDllAniv = ModResources.WMVCORE;
// Copy the WMVCore files to the Zune directory.
// Only ZuneEncEng.dll searches in System32 first; all other dependent
// DLLs will search the Zune folder.
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcora.dll"), wmvDllAniv);
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcore.dll"), wmvDllAniv);
return null;
}
catch (IOException)
{
return $"Unable to replace '{zeeDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
}
catch (Exception ex)
{
return ex.Message;
} }
// Get the working WMVCORE.dll
byte[] wmvDllAniv = ModResources.WMVCORE;
// Copy the WMVCore files to the Zune directory.
// Only ZuneEncEng.dll searches in System32 first; all other dependent
// DLLs will search the Zune folder.
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcora.dll"), wmvDllAniv);
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcore.dll"), wmvDllAniv);
return null;
} }
catch (IOException)
public override Task<string?> Reset()
{ {
var zeeDllPath = Path.Combine(ZuneInstallDir, "ZuneEncEng.dll"); return $"Unable to replace '{zeeDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
try }
{ catch (Exception ex)
// Copy backup to application folder {
File.Copy(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"), zeeDllPath, true); return ex.Message;
// Delete WMVCore files
File.Delete(Path.Combine(ZuneInstallDir, "wmvcora.dll"));
File.Delete(Path.Combine(ZuneInstallDir, "wmvcore.dll"));
return Task.FromResult<string?>(null);
}
catch (Exception ex)
{
return Task.FromResult(ex.Message)!;
}
} }
public override IReadOnlyList<Type>? DependentMods => null;
} }
public override Task<string?> Reset()
{
var zeeDllPath = Path.Combine(ZuneInstallDir, "ZuneEncEng.dll");
try
{
// Copy backup to application folder
File.Copy(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"), zeeDllPath, true);
// Delete WMVCore files
File.Delete(Path.Combine(ZuneInstallDir, "wmvcora.dll"));
File.Delete(Path.Combine(ZuneInstallDir, "wmvcore.dll"));
return Task.FromResult<string?>(null);
}
catch (Exception ex)
{
return Task.FromResult(ex.Message)!;
}
}
public override IReadOnlyList<Type>? DependentMods => null;
} }
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -1,7 +1,6 @@
namespace ZuneModCore.Services namespace ZuneModCore.Services;
public interface IModCoreConfig
{ {
public interface IModCoreConfig string ZuneInstallDir { get; }
{
string ZuneInstallDir { get; }
}
} }
+120 -128
View File
@@ -1,151 +1,143 @@
using System; namespace ZuneModCore.Win32;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ZuneModCore.Win32 public static partial class FileUtil
{ {
using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)]
using System.Diagnostics; struct RM_UNIQUE_PROCESS
using System;
using System.Collections.Generic;
static public class FileUtil
{ {
[StructLayout(LayoutKind.Sequential)] public int dwProcessId;
struct RM_UNIQUE_PROCESS public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle,
uint nFiles,
string[] rgsFilenames,
uint nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
uint nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[LibraryImport("rstrtmgr.dll")]
private static partial int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public List<Process> WhoIsLocking(string path)
{
string key = Guid.NewGuid().ToString();
List<Process> processes = [];
int res = RmStartSession(out uint handle, 0, key);
if (res != 0)
throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{ {
public int dwProcessId; const int ERROR_MORE_DATA = 234;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; uint pnProcInfo = 0,
} lpdwRebootReasons = RmRebootReasonNone;
const int RmRebootReasonNone = 0; string[] resources = [path]; // Just checking on one resource.
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] if (res != 0) throw new Exception("Could not register resource.");
struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] //Note: there's a race condition here -- the first call to RmGetList() returns
public string strAppName; // the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] if (res == ERROR_MORE_DATA)
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{ {
const int ERROR_MORE_DATA = 234; // Create an array to store the process results
uint pnProcInfoNeeded = 0, RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = 0, pnProcInfo = pnProcInfoNeeded;
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { path }; // Just checking on one resource. // Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); if (res == 0)
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == ERROR_MORE_DATA)
{ {
// Create an array to store the process results processes = new List<Process>((int)pnProcInfo);
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list // Enumerate all of the results and add them to the
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); // list to be returned
if (res == 0) for (int i = 0; i < pnProcInfo; i++)
{ {
processes = new List<Process>((int)pnProcInfo); try
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++)
{ {
try processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
} }
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
} }
else throw new Exception("Could not list processes locking resource.");
} }
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result."); else throw new Exception("Could not list processes locking resource.");
} }
finally else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
{
RmEndSession(handle);
}
return processes;
} }
finally
{
RmEndSession(handle);
}
return processes;
} }
} }
+54 -62
View File
@@ -1,73 +1,65 @@
using Microsoft.Win32; using Microsoft.Win32;
namespace ZuneModCore.Win32 namespace ZuneModCore.Win32;
public class RegEdit
{ {
public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\";
#pragma warning disable CA1416 // Validate platform compatibility public static bool CurrentUserSetBoolValue(string key, string name, bool value)
public class RegEdit
{ {
public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\"; RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
regKey ??= Registry.CurrentUser.CreateSubKey(key, true);
public static bool CurrentUserSetBoolValue(string key, string name, bool value) regKey.SetValue(name, value, RegistryValueKind.DWord);
{ regKey.Close();
RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true); regKey.Dispose();
if (regKey == null)
regKey = Registry.CurrentUser.CreateSubKey(key, true);
regKey.SetValue(name, value, RegistryValueKind.DWord); // Read the key to make sure it was set properly
regKey.Close(); if (CurrentUserGetBoolValue(key, name) == null)
regKey.Dispose(); return false;
return true;
// Read the key to make sure it was set properly
if (CurrentUserGetBoolValue(key, name) == null)
return false;
return true;
}
public static bool? CurrentUserGetBoolValue(string key, string name)
{
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
if (regKey == null)
return false;
// Return null if a boolean value couldn't be read from the key
int? value = regKey.GetValue(name, false) as int?;
if (!value.HasValue)
return null;
return value != 0;
}
public static void CurrentUserDeleteKey(string key)
{
using RegistryKey? targetKey = Registry.CurrentUser.OpenSubKey(key, true);
if (targetKey == null)
// Key is already deleted
return;
// Delete target subkeys
if (targetKey.SubKeyCount > 0)
targetKey.DeleteSubKeyTree(key, false);
// Delete target values
string[] values = targetKey.GetValueNames();
foreach (string value in values)
targetKey.DeleteValue(value, false);
// Delete target key
Registry.CurrentUser.DeleteSubKey(key);
}
public static void CurrentUserDeleteValue(string key, string name)
{
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
if (regKey == null)
return;
regKey.DeleteValue(name, false);
}
} }
#pragma warning restore CA1416 // Validate platform compatibility public static bool? CurrentUserGetBoolValue(string key, string name)
{
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
if (regKey == null)
return false;
// Return null if a boolean value couldn't be read from the key
int? value = regKey.GetValue(name, false) as int?;
if (!value.HasValue)
return null;
return value != 0;
}
public static void CurrentUserDeleteKey(string key)
{
using RegistryKey? targetKey = Registry.CurrentUser.OpenSubKey(key, true);
if (targetKey == null)
// Key is already deleted
return;
// Delete target subkeys
if (targetKey.SubKeyCount > 0)
targetKey.DeleteSubKeyTree(key, false);
// Delete target values
string[] values = targetKey.GetValueNames();
foreach (string value in values)
targetKey.DeleteValue(value, false);
// Delete target key
Registry.CurrentUser.DeleteSubKey(key);
}
public static void CurrentUserDeleteValue(string key, string name)
{
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
if (regKey == null)
return;
regKey.DeleteValue(name, false);
}
} }
+101 -105
View File
@@ -1,123 +1,119 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Security.Principal; using System.Security.Principal;
using Vanara.PInvoke; using Vanara.PInvoke;
namespace ZuneModCore.Win32 namespace ZuneModCore.Win32;
public class TokenManipulator
{ {
public class TokenManipulator public static bool AddPrivilege(string privilege)
{ {
public static bool AddPrivilege(string privilege) try
{ {
try bool retVal;
{ var hproc = Kernel32.GetCurrentProcess();
bool retVal;
var hproc = Kernel32.GetCurrentProcess();
retVal = AdvApi32.OpenProcessToken(hproc, retVal = AdvApi32.OpenProcessToken(hproc,
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok); AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid); retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_ENABLED); AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_ENABLED);
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed(); AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
return retVal; return retVal;
}
catch
{
throw;
}
} }
public static bool RemovePrivilege(string privilege) catch
{ {
try throw;
{
bool retVal;
var hproc = Kernel32.GetCurrentProcess();
retVal = AdvApi32.OpenProcessToken(hproc,
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
return retVal;
}
catch
{
throw;
}
}
#pragma warning disable CA1416 // Validate platform compatibility
public static void TakeOwnership(FileInfo file)
{
EscalateToAdmin();
// Get access control
FileSecurity security = file.GetAccessControl();
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
// Set owner to current user
security.SetOwner(cu);
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
// Update the Access Control on the original file
file.SetAccessControl(security);
}
public static void TakeOwnership(DirectoryInfo dir)
{
EscalateToAdmin();
// Get access control
DirectorySecurity security = dir.GetAccessControl();
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
// Set owner to current user
security.SetOwner(cu);
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
// Update the Access Control on the original file
dir.SetAccessControl(security);
}
public static void TakeOwnership(FileSystemInfo info)
{
if (info is FileInfo file)
TakeOwnership(file);
else if (info is DirectoryInfo dir)
TakeOwnership(dir);
}
#pragma warning restore CA1416 // Validate platform compatibility
public static bool TryTakeOwnership(FileInfo file, out Exception? exception)
{
try
{
TakeOwnership(file);
exception = null;
return true;
}
catch (Exception ex)
{
exception = ex;
return false;
}
}
public static void EscalateToAdmin()
{
// Activate necessary admin privileges to make changes without NTFS perms
AddPrivilege("SeRestorePrivilege"); // Necessary to set Owner Permissions
AddPrivilege("SeBackupPrivilege"); // Necessary to bypass Traverse Checking
AddPrivilege("SeTakeOwnershipPrivilege"); // Necessary to override FilePermissions
} }
} }
public static bool RemovePrivilege(string privilege)
{
try
{
bool retVal;
var hproc = Kernel32.GetCurrentProcess();
retVal = AdvApi32.OpenProcessToken(hproc,
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
return retVal;
}
catch
{
throw;
}
}
public static void TakeOwnership(FileInfo file)
{
EscalateToAdmin();
// Get access control
FileSecurity security = file.GetAccessControl();
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
// Set owner to current user
security.SetOwner(cu);
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
// Update the Access Control on the original file
file.SetAccessControl(security);
}
public static void TakeOwnership(DirectoryInfo dir)
{
EscalateToAdmin();
// Get access control
DirectorySecurity security = dir.GetAccessControl();
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
// Set owner to current user
security.SetOwner(cu);
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
// Update the Access Control on the original file
dir.SetAccessControl(security);
}
public static void TakeOwnership(FileSystemInfo info)
{
if (info is FileInfo file)
TakeOwnership(file);
else if (info is DirectoryInfo dir)
TakeOwnership(dir);
}
public static bool TryTakeOwnership(FileInfo file, out Exception? exception)
{
try
{
TakeOwnership(file);
exception = null;
return true;
}
catch (Exception ex)
{
exception = ex;
return false;
}
}
public static void EscalateToAdmin()
{
// Activate necessary admin privileges to make changes without NTFS perms
AddPrivilege("SeRestorePrivilege"); // Necessary to set Owner Permissions
AddPrivilege("SeBackupPrivilege"); // Necessary to bypass Traverse Checking
AddPrivilege("SeTakeOwnershipPrivilege"); // Necessary to override FilePermissions
}
} }
+2 -2
View File
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<LangVersion>9</LangVersion> <LangVersion>12</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;x86</Platforms> <Platforms>AnyCPU;x64;x86</Platforms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>