mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
Fix bugs.
Update PkgCLI.
This commit is contained in:
@@ -348,8 +348,18 @@ namespace AppxPackage
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value))
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
base [key] = value;
|
base [key] = value;
|
||||||
@@ -370,8 +380,18 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
public string At (string key)
|
public string At (string key)
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value)) throw new KeyNotFoundException ($"PRBaseApplication.At: key \"{key}\" not found");
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind) throw new KeyNotFoundException ($"MRApplication: cannot find key \"{key}\"");
|
||||||
if (!EnablePri ()) return value;
|
if (!EnablePri ()) return value;
|
||||||
if (PriFileHelper.IsMsResourcePrefix (value))
|
if (PriFileHelper.IsMsResourcePrefix (value))
|
||||||
{
|
{
|
||||||
@@ -383,8 +403,18 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
public string NewAt (string key, bool toPriString)
|
public string NewAt (string key, bool toPriString)
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value))
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
base [key] = value;
|
base [key] = value;
|
||||||
|
|||||||
@@ -741,8 +741,18 @@ namespace AppxPackage
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value))
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
base [key] = value;
|
base [key] = value;
|
||||||
@@ -763,8 +773,18 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
public string At (string key)
|
public string At (string key)
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value)) throw new KeyNotFoundException ($"PRBaseApplication.At: key \"{key}\" not found");
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind) throw new KeyNotFoundException ($"MRApplication: cannot find key \"{key}\"");
|
||||||
if (!EnablePri ()) return value;
|
if (!EnablePri ()) return value;
|
||||||
if (PriFileHelper.IsMsResourcePrefix (value))
|
if (PriFileHelper.IsMsResourcePrefix (value))
|
||||||
{
|
{
|
||||||
@@ -776,8 +796,18 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
public string NewAt (string key, bool toPriString)
|
public string NewAt (string key, bool toPriString)
|
||||||
{
|
{
|
||||||
string value;
|
string value = null;
|
||||||
if (!TryGetValue (key, out value))
|
var isfind = false;
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
if (kv.Key?.Trim ()?.ToLowerInvariant () == key?.Trim ()?.ToLowerInvariant ())
|
||||||
|
{
|
||||||
|
value = kv.Value;
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
base [key] = value;
|
base [key] = value;
|
||||||
@@ -1780,6 +1810,753 @@ namespace AppxPackage
|
|||||||
UsePri = parsePri;
|
UsePri = parsePri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool SaveJsonFileCS (string savefilepath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#region
|
||||||
|
using (var fs = File.Create (savefilepath))
|
||||||
|
{
|
||||||
|
using (var zos = new ZipOutputStream (fs))
|
||||||
|
{
|
||||||
|
zos.SetLevel (9);
|
||||||
|
bool parsePri = UsePri;
|
||||||
|
bool usePri = EnablePri;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UsePri = false;
|
||||||
|
EnablePri = false;
|
||||||
|
object packageInfo = null;
|
||||||
|
#region file
|
||||||
|
var typestr = "unknown";
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case PackageType.Appx: typestr = "appx"; break;
|
||||||
|
case PackageType.Bundle: typestr = "bundle"; break;
|
||||||
|
default:
|
||||||
|
case PackageType.Unknown: typestr = "unknown"; break;
|
||||||
|
}
|
||||||
|
var rolestr = "unknown";
|
||||||
|
switch (Role)
|
||||||
|
{
|
||||||
|
case PackageRole.Application: rolestr = "application"; break;
|
||||||
|
case PackageRole.Framework: rolestr = "framework"; break;
|
||||||
|
case PackageRole.Resource: rolestr = "resource"; break;
|
||||||
|
default:
|
||||||
|
case PackageRole.Unknown: rolestr = "unknown"; break;
|
||||||
|
}
|
||||||
|
var pkgfile = new {
|
||||||
|
path = FilePath,
|
||||||
|
valid = IsValid,
|
||||||
|
type = typestr,
|
||||||
|
role = rolestr
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region id
|
||||||
|
var id = Identity;
|
||||||
|
var pkgid = new {
|
||||||
|
name = id.Name,
|
||||||
|
publisher = id.Publisher,
|
||||||
|
version = id.Version.ToString (),
|
||||||
|
realVersion = id.RealVersion.ToString (),
|
||||||
|
architecture = id.ProcessArchitecture.Select (e => {
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case Architecture.ARM: return "arm";
|
||||||
|
case Architecture.ARM64: return "arm64";
|
||||||
|
case Architecture.Neutral: return "neutral";
|
||||||
|
case Architecture.x64: return "x64";
|
||||||
|
case Architecture.x86: return "x86";
|
||||||
|
default:
|
||||||
|
case Architecture.Unknown: return "unknown";
|
||||||
|
}
|
||||||
|
}).ToList (),
|
||||||
|
familyName = id.FamilyName,
|
||||||
|
fullName = id.FullName,
|
||||||
|
resourceId = id.ResourceId
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region prerequistes
|
||||||
|
var preq = Prerequisites;
|
||||||
|
var pkgpreq = new {
|
||||||
|
osMinVersion = preq.OSMinVersion.ToString (),
|
||||||
|
osMaxVersionTested = preq.OSMaxVersionTested.ToString (),
|
||||||
|
osMinVersionDescription = preq.OSMinVersionDescription,
|
||||||
|
osMaxVersionTestedDescription = preq.OSMaxVersionDescription
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region resources
|
||||||
|
var res = Resources;
|
||||||
|
var pkgres = new {
|
||||||
|
languages = res.Languages,
|
||||||
|
scales = res.Scales,
|
||||||
|
dxFeatureLevels = res.DXFeatures.Select (d => {
|
||||||
|
switch (d)
|
||||||
|
{
|
||||||
|
case DXFeatureLevel.Level9: return 9;
|
||||||
|
case DXFeatureLevel.Level10: return 10;
|
||||||
|
case DXFeatureLevel.Level11: return 11;
|
||||||
|
case DXFeatureLevel.Level12: return 12;
|
||||||
|
default:
|
||||||
|
case DXFeatureLevel.Unspecified: return -1;
|
||||||
|
}
|
||||||
|
}).ToList ()
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region capabilities
|
||||||
|
var caps = Capabilities;
|
||||||
|
var pkgcaps = new {
|
||||||
|
capabilities = caps.Capabilities,
|
||||||
|
deviceCapabilities = caps.DeviceCapabilities
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region dependencies
|
||||||
|
var deps = Dependencies;
|
||||||
|
var pkgdeps = Dependencies.Select (d => new {
|
||||||
|
name = d.Name,
|
||||||
|
publisher = d.Publisher,
|
||||||
|
minVersion = d.Version.ToString ()
|
||||||
|
}).ToList ();
|
||||||
|
#endregion
|
||||||
|
using (var priAllRes = new PriAllValuesReader (this))
|
||||||
|
{
|
||||||
|
priAllRes.Init ();
|
||||||
|
#region prop
|
||||||
|
var prop = Properties;
|
||||||
|
var dispname = prop.DisplayName;
|
||||||
|
var dispNameDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (dispname))
|
||||||
|
dispNameDict = priAllRes.LocaleResourceAllValues (dispname);
|
||||||
|
dispNameDict ["root"] = dispname;
|
||||||
|
var desc = prop.Description;
|
||||||
|
var descDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (desc))
|
||||||
|
descDict = priAllRes.LocaleResourceAllValues (desc);
|
||||||
|
descDict ["root"] = desc;
|
||||||
|
var disppub = prop.Publisher;
|
||||||
|
var dispPubDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (disppub))
|
||||||
|
dispPubDict = priAllRes.LocaleResourceAllValues (disppub);
|
||||||
|
dispPubDict ["root"] = disppub;
|
||||||
|
var logoList = priAllRes.FileResourceAllValues (prop.Logo, zos)
|
||||||
|
.Select (kv => {
|
||||||
|
string contrast = "";
|
||||||
|
switch (kv.Key.Contrast)
|
||||||
|
{
|
||||||
|
case PriResourceKey.PriContrast.None: contrast = ""; break;
|
||||||
|
case PriResourceKey.PriContrast.Black: contrast = "black"; break;
|
||||||
|
case PriResourceKey.PriContrast.White: contrast = "white"; break;
|
||||||
|
case PriResourceKey.PriContrast.High: contrast = "high"; break;
|
||||||
|
case PriResourceKey.PriContrast.Low: contrast = "low"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kv.Key.IsTargetSize)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
targetSize = kv.Key.Value,
|
||||||
|
contrast = contrast,
|
||||||
|
name = kv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (kv.Key.IsScale)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
scale = kv.Key.Value,
|
||||||
|
contrast = contrast,
|
||||||
|
name = kv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (kv.Key.IsString)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
language = kv.Key.Value,
|
||||||
|
name = kv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (dynamic)null; // 不满足条件时返回 null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Where (item => item != null) // 过滤掉 null
|
||||||
|
.ToList ();
|
||||||
|
var pkgprop = new {
|
||||||
|
displayName = dispNameDict,
|
||||||
|
publisherDisplayName = dispPubDict,
|
||||||
|
description = descDict,
|
||||||
|
logo = logoList,
|
||||||
|
framework = prop.Framework,
|
||||||
|
resourcePackage = prop.ResourcePackage
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
#region apps
|
||||||
|
var apps = Applications;
|
||||||
|
var pkgapps = new List<object> ();
|
||||||
|
foreach (var app in apps)
|
||||||
|
{
|
||||||
|
dynamic obj = new ExpandoObject ();
|
||||||
|
var dict = (IDictionary<string, object>)obj;
|
||||||
|
foreach (var kv in app)
|
||||||
|
{
|
||||||
|
if (Utils.AppFileProperties.Contains (kv.Key, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
dict [Utils.PascalToCamel (kv.Key)] = priAllRes.FileResourceAllValues (kv.Value, zos)
|
||||||
|
.Select (skv => {
|
||||||
|
string contrast = "";
|
||||||
|
switch (skv.Key.Contrast)
|
||||||
|
{
|
||||||
|
case PriResourceKey.PriContrast.None: contrast = ""; break;
|
||||||
|
case PriResourceKey.PriContrast.Black: contrast = "black"; break;
|
||||||
|
case PriResourceKey.PriContrast.White: contrast = "white"; break;
|
||||||
|
case PriResourceKey.PriContrast.High: contrast = "high"; break;
|
||||||
|
case PriResourceKey.PriContrast.Low: contrast = "low"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skv.Key.IsTargetSize)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
targetSize = skv.Key.Value,
|
||||||
|
contrast = contrast,
|
||||||
|
name = skv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (skv.Key.IsScale)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
scale = skv.Key.Value,
|
||||||
|
contrast = contrast,
|
||||||
|
name = skv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (skv.Key.IsString)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
language = skv.Key.Value,
|
||||||
|
name = skv.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (dynamic)null; // 不满足条件时返回 null
|
||||||
|
}
|
||||||
|
}).Where (item => item != null).ToList ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (kv.Value))
|
||||||
|
{
|
||||||
|
var itemDict = new Dictionary<string, string> ();
|
||||||
|
itemDict = priAllRes.LocaleResourceAllValues (kv.Value);
|
||||||
|
itemDict ["root"] = kv.Value;
|
||||||
|
dict [Utils.PascalToCamel (kv.Key)] = itemDict;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dict [Utils.PascalToCamel (kv.Key)] = kv.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkgapps.Add (obj);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
packageInfo = new {
|
||||||
|
file = pkgfile,
|
||||||
|
identity = pkgid,
|
||||||
|
properties = pkgprop,
|
||||||
|
resources = pkgres,
|
||||||
|
prerequisites = pkgpreq,
|
||||||
|
applications = pkgapps,
|
||||||
|
capabilities = pkgcaps,
|
||||||
|
dependencies = pkgdeps
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var jsonstr = Newtonsoft.Json.JsonConvert.SerializeObject (packageInfo, Newtonsoft.Json.Formatting.None);
|
||||||
|
var ze = new ZipEntry ("info.json");
|
||||||
|
ze.DateTime = DateTime.Now;
|
||||||
|
zos.PutNextEntry (ze);
|
||||||
|
var bytes = Encoding.UTF8.GetBytes (jsonstr);
|
||||||
|
zos.Write (bytes, 0, bytes.Length);
|
||||||
|
zos.CloseEntry ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EnablePri = usePri;
|
||||||
|
UsePri = parsePri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool SaveXmlFileCS (string savefilepath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#region
|
||||||
|
using (var fs = File.Create (savefilepath))
|
||||||
|
{
|
||||||
|
using (var zos = new ZipOutputStream (fs))
|
||||||
|
{
|
||||||
|
zos.SetLevel (9);
|
||||||
|
bool parsePri = UsePri;
|
||||||
|
bool usePri = EnablePri;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UsePri = false;
|
||||||
|
EnablePri = false;
|
||||||
|
var xml = new XmlDocument ();
|
||||||
|
var decl = xml.CreateXmlDeclaration ("1.0", "utf-8", "yes");
|
||||||
|
xml.AppendChild (decl);
|
||||||
|
var root = xml.CreateElement ("Package");
|
||||||
|
xml.AppendChild (root);
|
||||||
|
#region file
|
||||||
|
var typestr = "unknown";
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case PackageType.Appx: typestr = "appx"; break;
|
||||||
|
case PackageType.Bundle: typestr = "bundle"; break;
|
||||||
|
default:
|
||||||
|
case PackageType.Unknown: typestr = "unknown"; break;
|
||||||
|
}
|
||||||
|
var rolestr = "unknown";
|
||||||
|
switch (Role)
|
||||||
|
{
|
||||||
|
case PackageRole.Application: rolestr = "application"; break;
|
||||||
|
case PackageRole.Framework: rolestr = "framework"; break;
|
||||||
|
case PackageRole.Resource: rolestr = "resource"; break;
|
||||||
|
default:
|
||||||
|
case PackageRole.Unknown: rolestr = "unknown"; break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var nodefile = xml.CreateElement ("File");
|
||||||
|
var nodefilepath = xml.CreateElement ("Path");
|
||||||
|
nodefilepath.InnerText = FilePath;
|
||||||
|
var nodefilevalid = xml.CreateElement ("Valid");
|
||||||
|
nodefilevalid.InnerText = IsValid ? "true" : "false";
|
||||||
|
var nodefiletype = xml.CreateElement ("Type");
|
||||||
|
nodefiletype.InnerText = typestr;
|
||||||
|
var nodefilerole = xml.CreateElement ("Role");
|
||||||
|
nodefilerole.InnerText = rolestr;
|
||||||
|
nodefile.AppendChild (nodefilepath);
|
||||||
|
nodefile.AppendChild (nodefilevalid);
|
||||||
|
nodefile.AppendChild (nodefiletype);
|
||||||
|
nodefile.AppendChild (nodefilerole);
|
||||||
|
root.AppendChild (nodefile);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region id
|
||||||
|
var id = Identity;
|
||||||
|
{
|
||||||
|
var nodeid = xml.CreateElement ("Identity");
|
||||||
|
var nodeidname = xml.CreateElement ("Name");
|
||||||
|
nodeidname.InnerText = id.Name;
|
||||||
|
var nodeidpub = xml.CreateElement ("Publisher");
|
||||||
|
nodeidpub.InnerText = id.Publisher;
|
||||||
|
var nodeidver = xml.CreateElement ("Version");
|
||||||
|
nodeidver.InnerText = id.Version.ToString ();
|
||||||
|
var nodeidrealver = xml.CreateElement ("RealVersion");
|
||||||
|
nodeidrealver.InnerText = id.RealVersion.ToString ();
|
||||||
|
var nodeidarchs = xml.CreateElement ("ProcessorArchitectures");
|
||||||
|
foreach (var a in id.ProcessArchitecture)
|
||||||
|
{
|
||||||
|
var astr = "";
|
||||||
|
switch (a)
|
||||||
|
{
|
||||||
|
case Architecture.ARM: astr = "arm"; break;
|
||||||
|
case Architecture.ARM64: astr = "arm64"; break;
|
||||||
|
case Architecture.Neutral: astr = "neutral"; break;
|
||||||
|
case Architecture.x64: astr = "x64"; break;
|
||||||
|
case Architecture.x86: astr = "x86"; break;
|
||||||
|
default:
|
||||||
|
case Architecture.Unknown: astr = "unknown"; break;
|
||||||
|
}
|
||||||
|
var nodeidarch = xml.CreateElement ("ProcessorArchitecture");
|
||||||
|
nodeidarch.SetAttribute ("Value", astr);
|
||||||
|
nodeidarchs.AppendChild (nodeidarch);
|
||||||
|
}
|
||||||
|
var nodeidfamily = xml.CreateElement ("FamilyName");
|
||||||
|
nodeidfamily.InnerText = id.FamilyName;
|
||||||
|
var nodeidfull = xml.CreateElement ("FullName");
|
||||||
|
nodeidfull.InnerText = id.FullName;
|
||||||
|
var nodeidresid = xml.CreateElement ("ResourceId");
|
||||||
|
nodeidresid.InnerText = id.ResourceId;
|
||||||
|
nodeid.AppendChild (nodeidname);
|
||||||
|
nodeid.AppendChild (nodeidpub);
|
||||||
|
nodeid.AppendChild (nodeidver);
|
||||||
|
nodeid.AppendChild (nodeidrealver);
|
||||||
|
nodeid.AppendChild (nodeidarchs);
|
||||||
|
nodeid.AppendChild (nodeidfamily);
|
||||||
|
nodeid.AppendChild (nodeidfamily);
|
||||||
|
nodeid.AppendChild (nodeidresid);
|
||||||
|
root.AppendChild (nodeid);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
using (var priAllRes = new PriAllValuesReader (this))
|
||||||
|
{
|
||||||
|
priAllRes.Init ();
|
||||||
|
#region prop
|
||||||
|
var prop = Properties;
|
||||||
|
var dispname = prop.DisplayName;
|
||||||
|
var dispNameDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (dispname))
|
||||||
|
dispNameDict = priAllRes.LocaleResourceAllValues (dispname);
|
||||||
|
dispNameDict ["root"] = dispname;
|
||||||
|
var desc = prop.Description;
|
||||||
|
var descDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (desc))
|
||||||
|
descDict = priAllRes.LocaleResourceAllValues (desc);
|
||||||
|
descDict ["root"] = desc;
|
||||||
|
var disppub = prop.Publisher;
|
||||||
|
var dispPubDict = new Dictionary<string, string> ();
|
||||||
|
if (PriFileHelper.IsMsResourcePrefix (disppub))
|
||||||
|
dispPubDict = priAllRes.LocaleResourceAllValues (disppub);
|
||||||
|
dispPubDict ["root"] = disppub;
|
||||||
|
{
|
||||||
|
var nodeprop = xml.CreateElement ("Properties");
|
||||||
|
var nodepropname = xml.CreateElement ("DisplayName");
|
||||||
|
foreach (var kv in dispNameDict)
|
||||||
|
{
|
||||||
|
var nodelocale = xml.CreateElement ("LocaleResource");
|
||||||
|
nodelocale.SetAttribute ("Lang", kv.Key);
|
||||||
|
nodelocale.InnerText = kv.Value;
|
||||||
|
nodepropname.AppendChild (nodelocale);
|
||||||
|
}
|
||||||
|
var nodeproppub = xml.CreateElement ("PublisherDisplayName");
|
||||||
|
foreach (var kv in dispPubDict)
|
||||||
|
{
|
||||||
|
var nodelocale = xml.CreateElement ("LocaleResource");
|
||||||
|
nodelocale.SetAttribute ("Lang", kv.Key);
|
||||||
|
nodelocale.InnerText = kv.Value;
|
||||||
|
nodeproppub.AppendChild (nodelocale);
|
||||||
|
}
|
||||||
|
var nodepropdesc = xml.CreateElement ("Description");
|
||||||
|
foreach (var kv in descDict)
|
||||||
|
{
|
||||||
|
var nodelocale = xml.CreateElement ("LocaleResource");
|
||||||
|
nodelocale.SetAttribute ("Lang", kv.Key);
|
||||||
|
nodelocale.InnerText = kv.Value;
|
||||||
|
nodepropdesc.AppendChild (nodelocale);
|
||||||
|
}
|
||||||
|
var nodeproplogo = xml.CreateElement ("Logo");
|
||||||
|
foreach (var kv in priAllRes.FileResourceAllValues (prop.Logo, zos))
|
||||||
|
{
|
||||||
|
#region logo
|
||||||
|
var key = kv.Key;
|
||||||
|
var value = kv.Value;
|
||||||
|
var nodefile = xml.CreateElement ("File");
|
||||||
|
var attrName = xml.CreateAttribute ("Name");
|
||||||
|
attrName.Value = value;
|
||||||
|
nodefile.Attributes.Append (attrName);
|
||||||
|
if (key.Contrast != PriResourceKey.PriContrast.None)
|
||||||
|
{
|
||||||
|
string contrast = "";
|
||||||
|
switch (key.Contrast)
|
||||||
|
{
|
||||||
|
case PriResourceKey.PriContrast.Black: contrast = "black"; break;
|
||||||
|
case PriResourceKey.PriContrast.White: contrast = "white"; break;
|
||||||
|
case PriResourceKey.PriContrast.High: contrast = "high"; break;
|
||||||
|
case PriResourceKey.PriContrast.Low: contrast = "low"; break;
|
||||||
|
}
|
||||||
|
var attrContrast = xml.CreateAttribute ("Contrast");
|
||||||
|
attrContrast.Value = contrast;
|
||||||
|
nodefile.Attributes.Append (attrContrast);
|
||||||
|
}
|
||||||
|
if (key.IsTargetSize)
|
||||||
|
{
|
||||||
|
var attr = xml.CreateAttribute ("TargetSize");
|
||||||
|
attr.Value = key.Value.ToString ();
|
||||||
|
nodefile.Attributes.Append (attr);
|
||||||
|
}
|
||||||
|
else if (key.IsScale)
|
||||||
|
{
|
||||||
|
var attr = xml.CreateAttribute ("Scale");
|
||||||
|
attr.Value = key.Value.ToString ();
|
||||||
|
nodefile.Attributes.Append (attr);
|
||||||
|
}
|
||||||
|
else if (key.IsString)
|
||||||
|
{
|
||||||
|
var attr = xml.CreateAttribute ("Language");
|
||||||
|
attr.Value = key.Value.ToString ();
|
||||||
|
nodefile.Attributes.Append (attr);
|
||||||
|
}
|
||||||
|
nodeproplogo.AppendChild (nodefile);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
var nodepropfrw = xml.CreateElement ("Framework");
|
||||||
|
nodepropfrw.InnerText = prop.Framework ? "true" : "false";
|
||||||
|
var nodepropres = xml.CreateElement ("ResourcePackage");
|
||||||
|
nodepropres.InnerText = prop.ResourcePackage ? "true" : "false";
|
||||||
|
nodeprop.AppendChild (nodepropname);
|
||||||
|
nodeprop.AppendChild (nodeproppub);
|
||||||
|
nodeprop.AppendChild (nodepropdesc);
|
||||||
|
nodeprop.AppendChild (nodeproplogo);
|
||||||
|
nodeprop.AppendChild (nodepropfrw);
|
||||||
|
nodeprop.AppendChild (nodepropres);
|
||||||
|
root.AppendChild (nodeprop);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region apps
|
||||||
|
// Applications 节点
|
||||||
|
XmlElement nodeApplications = xml.CreateElement ("Applications");
|
||||||
|
|
||||||
|
foreach (var app in Applications)
|
||||||
|
{
|
||||||
|
XmlElement nodeApp = xml.CreateElement ("Application");
|
||||||
|
|
||||||
|
// AppUserModelId 作为属性
|
||||||
|
string aumidObj = app ["AppUserModelId"];
|
||||||
|
if (aumidObj == null || aumidObj.Trim ().Length == 0) aumidObj = app ["AppUserModelID"];
|
||||||
|
nodeApp.SetAttribute ("AppUserModelId", aumidObj);
|
||||||
|
foreach (var kv in app)
|
||||||
|
{
|
||||||
|
string key = kv.Key;
|
||||||
|
string value = kv.Value;
|
||||||
|
|
||||||
|
// 跳过 AppUserModelId,因为已经作为属性
|
||||||
|
if (string.Equals (key, "AppUserModelId", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
XmlElement nodeProp = xml.CreateElement (key);
|
||||||
|
|
||||||
|
// 文件资源类型
|
||||||
|
if (Utils.AppFileProperties.Contains (key, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreach (var skv in priAllRes.FileResourceAllValues (value, zos))
|
||||||
|
{
|
||||||
|
var fileKey = skv.Key;
|
||||||
|
var fileValue = skv.Value;
|
||||||
|
|
||||||
|
XmlElement nodeFile = xml.CreateElement ("File");
|
||||||
|
|
||||||
|
// Name 必有
|
||||||
|
XmlAttribute attrName = xml.CreateAttribute ("Name");
|
||||||
|
attrName.Value = fileValue;
|
||||||
|
nodeFile.Attributes.Append (attrName);
|
||||||
|
|
||||||
|
// Contrast 可选
|
||||||
|
if (fileKey.Contrast != PriResourceKey.PriContrast.None)
|
||||||
|
{
|
||||||
|
string contrast = "";
|
||||||
|
switch (fileKey.Contrast)
|
||||||
|
{
|
||||||
|
case PriResourceKey.PriContrast.Black: contrast = "black"; break;
|
||||||
|
case PriResourceKey.PriContrast.White: contrast = "white"; break;
|
||||||
|
case PriResourceKey.PriContrast.High: contrast = "high"; break;
|
||||||
|
case PriResourceKey.PriContrast.Low: contrast = "low"; break;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty (contrast))
|
||||||
|
{
|
||||||
|
XmlAttribute attrContrast = xml.CreateAttribute ("Contrast");
|
||||||
|
attrContrast.Value = contrast;
|
||||||
|
nodeFile.Attributes.Append (attrContrast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetSize
|
||||||
|
if (fileKey.IsTargetSize)
|
||||||
|
{
|
||||||
|
XmlAttribute attrTS = xml.CreateAttribute ("TargetSize");
|
||||||
|
attrTS.Value = fileKey.Value.ToString ();
|
||||||
|
nodeFile.Attributes.Append (attrTS);
|
||||||
|
}
|
||||||
|
// Scale
|
||||||
|
else if (fileKey.IsScale)
|
||||||
|
{
|
||||||
|
XmlAttribute attrS = xml.CreateAttribute ("Scale");
|
||||||
|
attrS.Value = fileKey.Value.ToString ();
|
||||||
|
nodeFile.Attributes.Append (attrS);
|
||||||
|
}
|
||||||
|
// Language
|
||||||
|
else if (fileKey.IsString)
|
||||||
|
{
|
||||||
|
XmlAttribute attrL = xml.CreateAttribute ("Lang");
|
||||||
|
attrL.Value = fileKey.Value.ToString ();
|
||||||
|
nodeFile.Attributes.Append (attrL);
|
||||||
|
}
|
||||||
|
nodeProp.AppendChild (nodeFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 多语言资源
|
||||||
|
else if (PriFileHelper.IsMsResourcePrefix (value))
|
||||||
|
{
|
||||||
|
Dictionary<string, string> localeDict = priAllRes.LocaleResourceAllValues (value);
|
||||||
|
localeDict ["root"] = value.ToString ();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> lkv in localeDict)
|
||||||
|
{
|
||||||
|
XmlElement nodeLocale = xml.CreateElement ("LocaleResource");
|
||||||
|
nodeLocale.SetAttribute ("Lang", lkv.Key);
|
||||||
|
nodeLocale.InnerText = lkv.Value;
|
||||||
|
nodeProp.AppendChild (nodeLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeProp.InnerText = value.ToString ();
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeApp.AppendChild (nodeProp);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeApplications.AppendChild (nodeApp);
|
||||||
|
}
|
||||||
|
root.AppendChild (nodeApplications);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#region prerequistes
|
||||||
|
var preq = Prerequisites;
|
||||||
|
{
|
||||||
|
var nodepreq = xml.CreateElement ("Prerequisites");
|
||||||
|
var nodeosmin = xml.CreateElement ("OSMinVersion");
|
||||||
|
nodeosmin.InnerText = preq.OSMinVersion.ToString ();
|
||||||
|
nodeosmin.SetAttribute ("Description", preq.OSMinVersionDescription);
|
||||||
|
var nodeosmaxt = xml.CreateElement ("OSMaxVersionTested");
|
||||||
|
nodeosmaxt.InnerText = preq.OSMaxVersionTested.ToString ();
|
||||||
|
nodeosmaxt.SetAttribute ("Description", preq.OSMaxVersionDescription);
|
||||||
|
nodepreq.AppendChild (nodeosmin);
|
||||||
|
nodepreq.AppendChild (nodeosmaxt);
|
||||||
|
root.AppendChild (nodepreq);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region resources
|
||||||
|
XmlElement nodeResources = xml.CreateElement ("Resources");
|
||||||
|
var res = Resources;
|
||||||
|
if (res.Languages != null)
|
||||||
|
{
|
||||||
|
foreach (var lang in res.Languages)
|
||||||
|
{
|
||||||
|
XmlElement nodeRes = xml.CreateElement ("Resource");
|
||||||
|
XmlAttribute attrLang = xml.CreateAttribute ("Language");
|
||||||
|
attrLang.Value = lang;
|
||||||
|
nodeRes.Attributes.Append (attrLang);
|
||||||
|
nodeResources.AppendChild (nodeRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res.Scales != null)
|
||||||
|
{
|
||||||
|
foreach (var scale in res.Scales)
|
||||||
|
{
|
||||||
|
XmlElement nodeRes = xml.CreateElement ("Resource");
|
||||||
|
XmlAttribute attrScale = xml.CreateAttribute ("Scale");
|
||||||
|
attrScale.Value = scale.ToString ();
|
||||||
|
nodeRes.Attributes.Append (attrScale);
|
||||||
|
nodeResources.AppendChild (nodeRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res.DXFeatures != null)
|
||||||
|
{
|
||||||
|
foreach (var d in res.DXFeatures)
|
||||||
|
{
|
||||||
|
int level = -1;
|
||||||
|
switch (d)
|
||||||
|
{
|
||||||
|
case DXFeatureLevel.Level9: level = 9; break;
|
||||||
|
case DXFeatureLevel.Level10: level = 10; break;
|
||||||
|
case DXFeatureLevel.Level11: level = 11; break;
|
||||||
|
case DXFeatureLevel.Level12: level = 12; break;
|
||||||
|
case DXFeatureLevel.Unspecified:
|
||||||
|
default: level = -1; break;
|
||||||
|
}
|
||||||
|
XmlElement nodeRes = xml.CreateElement ("Resource");
|
||||||
|
XmlAttribute attrDX = xml.CreateAttribute ("DXFeatureLevel");
|
||||||
|
attrDX.Value = level.ToString ();
|
||||||
|
nodeRes.Attributes.Append (attrDX);
|
||||||
|
nodeResources.AppendChild (nodeRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.AppendChild (nodeResources);
|
||||||
|
#endregion
|
||||||
|
#region capabilities
|
||||||
|
XmlElement nodeCapabilities = xml.CreateElement ("Capabilities");
|
||||||
|
var caps = Capabilities;
|
||||||
|
if (caps.Capabilities != null)
|
||||||
|
{
|
||||||
|
foreach (var cap in caps.Capabilities)
|
||||||
|
{
|
||||||
|
XmlElement nodeCap = xml.CreateElement ("Capability");
|
||||||
|
XmlAttribute attrName = xml.CreateAttribute ("Name");
|
||||||
|
attrName.Value = cap;
|
||||||
|
nodeCap.Attributes.Append (attrName);
|
||||||
|
nodeCapabilities.AppendChild (nodeCap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (caps.DeviceCapabilities != null)
|
||||||
|
{
|
||||||
|
foreach (var devCap in caps.DeviceCapabilities)
|
||||||
|
{
|
||||||
|
XmlElement nodeDevCap = xml.CreateElement ("DeviceCapability");
|
||||||
|
XmlAttribute attrName = xml.CreateAttribute ("Name");
|
||||||
|
attrName.Value = devCap;
|
||||||
|
nodeDevCap.Attributes.Append (attrName);
|
||||||
|
nodeCapabilities.AppendChild (nodeDevCap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.AppendChild (nodeCapabilities);
|
||||||
|
#endregion
|
||||||
|
#region dependencies
|
||||||
|
XmlElement nodeDependencies = xml.CreateElement ("Dependencies");
|
||||||
|
var deps = Dependencies;
|
||||||
|
if (deps != null)
|
||||||
|
{
|
||||||
|
foreach (var dep in deps)
|
||||||
|
{
|
||||||
|
XmlElement nodeDep = xml.CreateElement ("Dependency");
|
||||||
|
XmlAttribute attrName = xml.CreateAttribute ("Name");
|
||||||
|
attrName.Value = dep.Name ?? "";
|
||||||
|
nodeDep.Attributes.Append (attrName);
|
||||||
|
XmlAttribute attrPublisher = xml.CreateAttribute ("Publisher");
|
||||||
|
attrPublisher.Value = dep.Publisher ?? "";
|
||||||
|
nodeDep.Attributes.Append (attrPublisher);
|
||||||
|
XmlAttribute attrMinVersion = xml.CreateAttribute ("MinVersion");
|
||||||
|
attrMinVersion.Value = dep.Version != null ? dep.Version.ToString () : "";
|
||||||
|
nodeDep.Attributes.Append (attrMinVersion);
|
||||||
|
nodeDependencies.AppendChild (nodeDep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.AppendChild (nodeDependencies);
|
||||||
|
#endregion
|
||||||
|
var ze = new ZipEntry ("info.xml");
|
||||||
|
ze.DateTime = DateTime.Now;
|
||||||
|
zos.PutNextEntry (ze);
|
||||||
|
byte [] bytes;
|
||||||
|
using (var ms = new MemoryStream ())
|
||||||
|
{
|
||||||
|
var settings = new XmlWriterSettings {
|
||||||
|
Encoding = Encoding.UTF8,
|
||||||
|
Indent = true,
|
||||||
|
OmitXmlDeclaration = false
|
||||||
|
};
|
||||||
|
using (var writer = XmlWriter.Create (ms, settings))
|
||||||
|
{
|
||||||
|
xml.Save (writer);
|
||||||
|
}
|
||||||
|
bytes = ms.ToArray ();
|
||||||
|
}
|
||||||
|
zos.Write (bytes, 0, bytes.Length);
|
||||||
|
zos.CloseEntry ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EnablePri = usePri;
|
||||||
|
UsePri = parsePri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public void SaveJsonFile (string savefilepath, object resolve, object reject)
|
public void SaveJsonFile (string savefilepath, object resolve, object reject)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -2060,7 +2837,7 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (reject != null) JSHelper.CallJS (reject, ex);
|
if (reject != null) JSHelper.CallJS (reject, new DataUtils._I_Exception (ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void SaveJsonFileAsync (string savefilepath, object resolve, object reject)
|
public void SaveJsonFileAsync (string savefilepath, object resolve, object reject)
|
||||||
@@ -2531,7 +3308,7 @@ namespace AppxPackage
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (reject != null) JSHelper.CallJS (reject, ex);
|
if (reject != null) JSHelper.CallJS (reject, new DataUtils._I_Exception (ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void SaveXmlFileAsync (string savefilepath, object resolve, object reject)
|
public void SaveXmlFileAsync (string savefilepath, object resolve, object reject)
|
||||||
|
|||||||
230
PkgCLI/CliParsing.cs
Normal file
230
PkgCLI/CliParsing.cs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PkgCLI
|
||||||
|
{
|
||||||
|
public class NormalizeStringComparer: IEqualityComparer<string>
|
||||||
|
{
|
||||||
|
public bool Equals (string x, string y) => Polyfill.NEquals (x, y);
|
||||||
|
public int GetHashCode (string obj) => obj.NNormalize ().GetHashCode ();
|
||||||
|
}
|
||||||
|
public class NormalizeCharacterComparer: IEqualityComparer<char>
|
||||||
|
{
|
||||||
|
public bool Equals (char x, char y) => char.ToLowerInvariant (x) == char.ToLowerInvariant (y);
|
||||||
|
public int GetHashCode (char obj) => char.ToLowerInvariant (obj).GetHashCode ();
|
||||||
|
}
|
||||||
|
public class StringNSet: HashSet<string>
|
||||||
|
{
|
||||||
|
public StringNSet () : base (new NormalizeStringComparer ()) { }
|
||||||
|
public StringNSet (IEnumerable<string> list) : base (list, new NormalizeStringComparer ()) { }
|
||||||
|
}
|
||||||
|
public class CharNSet: HashSet<char>
|
||||||
|
{
|
||||||
|
public CharNSet () : base (new NormalizeCharacterComparer ()) { }
|
||||||
|
public CharNSet (IEnumerable<char> list) : base (list, new NormalizeCharacterComparer ()) { }
|
||||||
|
}
|
||||||
|
public static class CliParsingConst
|
||||||
|
{
|
||||||
|
public static readonly char [] defaultPrefixs = new char [] { '/', '-' };
|
||||||
|
public static readonly char [] defaultPostfixs = new char [] { '=', ':' };
|
||||||
|
public static readonly string [] emptyStringArray = new string [] { };
|
||||||
|
}
|
||||||
|
public class CmdParamName: IEquatable<CmdParamName>, IComparable<CmdParamName>, IDisposable
|
||||||
|
{
|
||||||
|
private string _id = "";
|
||||||
|
private string _name = "";
|
||||||
|
/// <summary>
|
||||||
|
/// 命令唯一标识,但不作为命令名,仅用于内部识别。命令 ID 可以作为命令名或别名。
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get { return _id.NNormalize (); } set { _id = value; } }
|
||||||
|
/// <summary>
|
||||||
|
/// 命令名,唯一。命令名不能与别名重复。首字符不能为前缀中的字符,尾字符不能为后缀中的字符。
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get { return _name.NNormalize (); } set { _name = value; } }
|
||||||
|
/// <summary>
|
||||||
|
/// 命令别名,唯一。不能与命令名重复。首字符不能为前缀中的字符,尾字符不能为后缀中的字符。
|
||||||
|
/// </summary>
|
||||||
|
/// </summary>
|
||||||
|
public StringNSet Aliases { get; private set; } = new StringNSet ();
|
||||||
|
/// <summary>
|
||||||
|
/// 命令前缀,为一个字符,标点符号。不能为命令名或别名的首字符。
|
||||||
|
/// </summary>
|
||||||
|
public CharNSet Prefixs { get; set; } = new CharNSet (CliParsingConst.defaultPrefixs);
|
||||||
|
/// <summary>
|
||||||
|
/// 命令后缀,为一个字符,标点符号。不能为命令名或别名的尾字符。
|
||||||
|
/// </summary>
|
||||||
|
public CharNSet Postfixs { get; set; } = new CharNSet (CliParsingConst.defaultPostfixs);
|
||||||
|
public bool Equals (CmdParamName other)
|
||||||
|
{
|
||||||
|
if (other == null) return false;
|
||||||
|
if (ReferenceEquals (this, other)) return true;
|
||||||
|
return string.Equals (this.Id, other.Id, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
public int CompareTo (CmdParamName other)
|
||||||
|
{
|
||||||
|
if (other == null) return 1;
|
||||||
|
if (ReferenceEquals (this, other)) return 0;
|
||||||
|
return string.Compare (this.Id, other.Id, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
public override bool Equals (object obj) => Equals (obj as CmdParamName);
|
||||||
|
public override int GetHashCode () => this.Id?.GetHashCode () ?? 0;
|
||||||
|
public bool ParamContains (string param)
|
||||||
|
{
|
||||||
|
var ret = Name.NEquals (param);
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
foreach (var alias in Aliases)
|
||||||
|
{
|
||||||
|
if (alias.NEquals (param)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public void Dispose ()
|
||||||
|
{
|
||||||
|
Aliases?.Clear ();
|
||||||
|
Prefixs?.Clear ();
|
||||||
|
}
|
||||||
|
public CmdParamName (string id, string name, IEnumerable<string> aliases, IEnumerable<char> prefixs, IEnumerable<char> postfixs)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Aliases = new StringNSet (aliases);
|
||||||
|
Prefixs = new CharNSet (prefixs);
|
||||||
|
Postfixs = new CharNSet (postfixs);
|
||||||
|
}
|
||||||
|
public CmdParamName (string name, IEnumerable<string> aliases) : this (name, name, aliases, CliParsingConst.defaultPrefixs, CliParsingConst.defaultPostfixs) { }
|
||||||
|
public CmdParamName (string name) : this (name, CliParsingConst.emptyStringArray) { }
|
||||||
|
public CmdParamName () { }
|
||||||
|
}
|
||||||
|
public class CommandParam: IEquatable<string>
|
||||||
|
{
|
||||||
|
private string _id = "";
|
||||||
|
public string Id { get { return _id.NNormalize (); } set { _id = value; } }
|
||||||
|
public string Value = "";
|
||||||
|
public bool Equals (string other)
|
||||||
|
{
|
||||||
|
return (_id ?? "").NEquals (other);
|
||||||
|
}
|
||||||
|
public override bool Equals (object obj)
|
||||||
|
{
|
||||||
|
if (obj is string) return Equals (obj as string);
|
||||||
|
else if (obj is CommandParam) return Equals ((obj as CommandParam).Id);
|
||||||
|
else return base.Equals (obj);
|
||||||
|
}
|
||||||
|
public override int GetHashCode ()
|
||||||
|
{
|
||||||
|
return Id.GetHashCode ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class CliParsing: IDisposable
|
||||||
|
{
|
||||||
|
public HashSet<CmdParamName> Params { get; set; } = new HashSet<CmdParamName> ();
|
||||||
|
public void Dispose ()
|
||||||
|
{
|
||||||
|
Params = null;
|
||||||
|
}
|
||||||
|
public List<CommandParam> Parse (string [] args)
|
||||||
|
{
|
||||||
|
var ret = new List<CommandParam> ();
|
||||||
|
CommandParam last = new CommandParam ();
|
||||||
|
for (long i = 0; i < args.LongLength; i++)
|
||||||
|
{
|
||||||
|
var arg = args [i]?.Trim () ?? "";
|
||||||
|
var item = args [i]?.NNormalize () ?? "";
|
||||||
|
if (string.IsNullOrWhiteSpace (item)) continue;
|
||||||
|
var first = item [0];
|
||||||
|
bool isfind = false;
|
||||||
|
foreach (var param in Params)
|
||||||
|
{
|
||||||
|
if (param.Prefixs.Contains (first))
|
||||||
|
{
|
||||||
|
var minser = param.Postfixs.Select (e => {
|
||||||
|
var index = item.IndexOf (e);
|
||||||
|
return index == -1 ? int.MaxValue : index;
|
||||||
|
}).Min ();
|
||||||
|
string paramPart, postfixPart;
|
||||||
|
if (minser == int.MaxValue)
|
||||||
|
{
|
||||||
|
paramPart = arg.Substring (1);
|
||||||
|
postfixPart = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
paramPart = arg.Substring (1, minser - 1);
|
||||||
|
postfixPart = arg.Substring (minser + 1);
|
||||||
|
}
|
||||||
|
if (param.ParamContains (paramPart))
|
||||||
|
{
|
||||||
|
isfind = true;
|
||||||
|
var cmdParam = new CommandParam ();
|
||||||
|
cmdParam.Id = param.Id;
|
||||||
|
if (!string.IsNullOrEmpty (postfixPart))
|
||||||
|
cmdParam.Value = postfixPart;
|
||||||
|
last = cmdParam;
|
||||||
|
ret.Add (cmdParam);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind)
|
||||||
|
{
|
||||||
|
var valueparam = new CommandParam ();
|
||||||
|
valueparam.Value = arg;
|
||||||
|
ret.Add (valueparam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class CliPasingUtils
|
||||||
|
{
|
||||||
|
public static bool ParamContains (this List<CommandParam> cpl, string id)
|
||||||
|
{
|
||||||
|
foreach (var i in cpl)
|
||||||
|
{
|
||||||
|
if (i.Id.NEquals (id)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool ParamContains (this List<CommandParam> cpl, CmdParamName param)
|
||||||
|
{
|
||||||
|
foreach (var i in cpl)
|
||||||
|
{
|
||||||
|
if (i.Id.NEquals (param.Id)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool ParamsContainsOr (this List<CommandParam> cpl, params string [] ids)
|
||||||
|
{
|
||||||
|
foreach (var i in cpl)
|
||||||
|
{
|
||||||
|
foreach (var j in ids)
|
||||||
|
{
|
||||||
|
if (i.Id.NEquals (j)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool ParamsContainsAnd (this List<CommandParam> cpl, params string [] ids)
|
||||||
|
{
|
||||||
|
if (ids == null || ids.Length == 0) return true;
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
if (!ParamContains (cpl, id)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static CommandParam GetFromId (this List <CommandParam> cpl, string id)
|
||||||
|
{
|
||||||
|
foreach (var c in cpl)
|
||||||
|
{
|
||||||
|
if (c.Id.NEquals (id)) return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,10 @@
|
|||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@@ -59,19 +63,29 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="CliParsing.cs" />
|
||||||
|
<Compile Include="Polyfill.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Text.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AppxPackage\AppxPackage.csproj">
|
<ProjectReference Include="..\AppxPackage\AppxPackage.csproj">
|
||||||
<Project>{bd681a4f-eb60-4bb8-90b5-65968fc7da59}</Project>
|
<Project>{bd681a4f-eb60-4bb8-90b5-65968fc7da59}</Project>
|
||||||
<Name>AppxPackage</Name>
|
<Name>AppxPackage</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\DataUtils\DataUtils.csproj">
|
||||||
|
<Project>{ffd3fd52-37a8-4f43-883c-de8d996cb0e0}</Project>
|
||||||
|
<Name>DataUtils</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PrivateInit\PrivateInit.csproj">
|
<ProjectReference Include="..\PrivateInit\PrivateInit.csproj">
|
||||||
<Project>{8e708d9a-6325-4aa9-b5a5-d1b5eca8eef7}</Project>
|
<Project>{8e708d9a-6325-4aa9-b5a5-d1b5eca8eef7}</Project>
|
||||||
<Name>PrivateInit</Name>
|
<Name>PrivateInit</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|||||||
979
PkgCLI/Polyfill.cs
Normal file
979
PkgCLI/Polyfill.cs
Normal file
@@ -0,0 +1,979 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using AppxPackage;
|
||||||
|
using AppxPackage.Info;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PkgCLI
|
||||||
|
{
|
||||||
|
public static class Polyfill
|
||||||
|
{
|
||||||
|
static public bool NEquals (this string s1, string s2)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (s1) && string.IsNullOrWhiteSpace (s2)) return true;
|
||||||
|
else return s1?.Trim ()?.ToLowerInvariant () == s2?.Trim ()?.ToLowerInvariant ();
|
||||||
|
}
|
||||||
|
static public string NNormalize (this string s1)
|
||||||
|
{
|
||||||
|
return s1?.Trim ()?.ToLowerInvariant ();
|
||||||
|
}
|
||||||
|
static public string Join<T> (this IEnumerable<T> ie, string divide = ", ")
|
||||||
|
{
|
||||||
|
var ret = "";
|
||||||
|
for (var i = 0; i < ie.Count (); i++)
|
||||||
|
{
|
||||||
|
if (i != 0) ret += divide;
|
||||||
|
var item = ie.ElementAt (i);
|
||||||
|
if (item == null) ret += "(null)";
|
||||||
|
else ret += item.ToString ();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 将对象通过指定的选择器转换为目标类型。
|
||||||
|
/// 类似于 LINQ 的 Select,但适用于单一对象。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">目标类型</typeparam>
|
||||||
|
/// <param name="obj">源对象</param>
|
||||||
|
/// <param name="selector">转换委托,输入源对象,输出目标对象</param>
|
||||||
|
/// <returns>转换后的结果</returns>
|
||||||
|
public static T ToOther<T> (this object obj, Func<object, T> selector)
|
||||||
|
{
|
||||||
|
if (selector == null)
|
||||||
|
throw new ArgumentNullException (nameof (selector));
|
||||||
|
return selector (obj);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 将字典格式化为对齐的文本,每个键值对一行,分隔符垂直对齐。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dict">要格式化的字典</param>
|
||||||
|
/// <param name="separator">分隔符(如 ":", "="),默认为 ":"</param>
|
||||||
|
/// <param name="indent">每行前的缩进字符串,默认为空</param>
|
||||||
|
/// <param name="sortKeys">是否按键名排序(不区分大小写),默认为 false</param>
|
||||||
|
/// <returns>格式化后的字符串,例如:
|
||||||
|
/// Name : Alice
|
||||||
|
/// Age : 30
|
||||||
|
/// </returns>
|
||||||
|
public static string FormatDictionaryAligned (
|
||||||
|
this IDictionary<string, string> dict,
|
||||||
|
string separator = ":",
|
||||||
|
string indent = "",
|
||||||
|
bool sortKeys = false)
|
||||||
|
{
|
||||||
|
if (dict == null || dict.Count == 0)
|
||||||
|
return string.Empty;
|
||||||
|
var keys = sortKeys
|
||||||
|
? dict.Keys.OrderBy (k => k, StringComparer.OrdinalIgnoreCase).ToList ()
|
||||||
|
: dict.Keys.ToList ();
|
||||||
|
int maxKeyLength = keys.Max (k => k.Length);
|
||||||
|
var sb = new StringBuilder ();
|
||||||
|
foreach (string key in keys)
|
||||||
|
{
|
||||||
|
string value = dict [key] ?? string.Empty;
|
||||||
|
sb.AppendLine ($"{indent}{key.PadRight (maxKeyLength)} {separator} {value}");
|
||||||
|
}
|
||||||
|
return sb.ToString ().TrimEnd (Environment.NewLine.ToCharArray ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class PackageReaderExt
|
||||||
|
{
|
||||||
|
static public object GetJsonObjectForCli (this PackageReader pr)
|
||||||
|
{
|
||||||
|
var id = pr.Identity;
|
||||||
|
var prop = pr.Properties;
|
||||||
|
var pre = pr.Prerequisites;
|
||||||
|
var apps = pr.Applications;
|
||||||
|
var caps = pr.Capabilities;
|
||||||
|
var deps = pr.Dependencies;
|
||||||
|
dynamic obj = new {
|
||||||
|
valid = pr.IsValid,
|
||||||
|
type = pr.Type.ToOther (e => {
|
||||||
|
switch ((AppxPackage.Info.PackageType)e)
|
||||||
|
{
|
||||||
|
case AppxPackage.Info.PackageType.Appx: return "appx";
|
||||||
|
case AppxPackage.Info.PackageType.Bundle: return "bundle";
|
||||||
|
default:
|
||||||
|
case AppxPackage.Info.PackageType.Unknown: return "unknown";
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
role = pr.Role.ToOther (o => {
|
||||||
|
switch ((AppxPackage.Info.PackageRole)o)
|
||||||
|
{
|
||||||
|
case AppxPackage.Info.PackageRole.Application: return "application";
|
||||||
|
case AppxPackage.Info.PackageRole.Framework: return "framework";
|
||||||
|
case AppxPackage.Info.PackageRole.Resource: return "resource";
|
||||||
|
default:
|
||||||
|
case AppxPackage.Info.PackageRole.Unknown: return "unknown";
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
identity = new {
|
||||||
|
name = id.Name,
|
||||||
|
publisher = id.Publisher,
|
||||||
|
version = id.Version.Expression,
|
||||||
|
realVersion = id.RealVersion.Expression,
|
||||||
|
architecture = id.ProcessArchitecture.Select (e => {
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case AppxPackage.Info.Architecture.ARM: return "arm";
|
||||||
|
case AppxPackage.Info.Architecture.ARM64: return "arm64";
|
||||||
|
case AppxPackage.Info.Architecture.Neutral: return "neutral";
|
||||||
|
default:
|
||||||
|
case AppxPackage.Info.Architecture.Unknown: return "unknown";
|
||||||
|
case AppxPackage.Info.Architecture.x64: return "x64";
|
||||||
|
case AppxPackage.Info.Architecture.x86: return "x86";
|
||||||
|
}
|
||||||
|
}).ToList (),
|
||||||
|
familyName = id.FamilyName,
|
||||||
|
fullName = id.FullName,
|
||||||
|
resourceId = id.ResourceId
|
||||||
|
},
|
||||||
|
properties = new {
|
||||||
|
displayName = prop.DisplayName,
|
||||||
|
publisherDisplayName = prop.Publisher,
|
||||||
|
description = prop.Description,
|
||||||
|
logo = prop.Logo,
|
||||||
|
framework = prop.Framework,
|
||||||
|
resourcePackage = prop.ResourcePackage
|
||||||
|
},
|
||||||
|
prerequisite = new {
|
||||||
|
osMinVersion = pre.OSMinVersion.ToString (),
|
||||||
|
osMaxVersionTested = pre.OSMaxVersionTested.ToString (),
|
||||||
|
_osMinVersion = pre.OSMinVersionDescription,
|
||||||
|
_osMaxVersionTested = pre.OSMaxVersionDescription
|
||||||
|
},
|
||||||
|
applications = apps.Select (e => {
|
||||||
|
var dict = new Dictionary<string, string> ();
|
||||||
|
foreach (var kv in e)
|
||||||
|
{
|
||||||
|
if (kv.Key.IndexOf ("Base64") >= 0) continue;
|
||||||
|
var value = e.NewAt (kv.Key, pr.EnablePri);
|
||||||
|
if (string.IsNullOrWhiteSpace (value))
|
||||||
|
value = e.At (kv.Key);
|
||||||
|
dict [kv.Key] = value;
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}).ToList (),
|
||||||
|
capabilities = new {
|
||||||
|
capabilities = caps.Capabilities,
|
||||||
|
deviceCapabilities = caps.DeviceCapabilities
|
||||||
|
},
|
||||||
|
dependencies = deps.Select (e => new {
|
||||||
|
name = e.Name,
|
||||||
|
publisher = e.Publisher,
|
||||||
|
minVersion = e.Version.ToString ()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 从 PackageReader 中获取指定路径的值。
|
||||||
|
/// 支持格式:路径中可使用 '.' 或 ':' 分隔,支持索引器 '[index]',支持 '.length'。
|
||||||
|
/// 大小写不敏感,自动去除首尾空白。
|
||||||
|
/// 示例:
|
||||||
|
/// "Identity" -> 返回 Identity 字典
|
||||||
|
/// "Identity.Name" -> 返回名称
|
||||||
|
/// "Properties.Publisher" -> 返回发布者显示名称(别名)
|
||||||
|
/// "applications[0]" -> 返回第一个应用字典
|
||||||
|
/// "applications.length" -> 返回应用个数
|
||||||
|
/// "Applications[0].LogoBase64" -> 返回第一个应用的 LogoBase64
|
||||||
|
/// </summary>
|
||||||
|
public static object GetItem (this PackageReader pr, string item)
|
||||||
|
{
|
||||||
|
if (pr == null) throw new ArgumentNullException ("pr");
|
||||||
|
if (string.IsNullOrWhiteSpace (item)) return null;
|
||||||
|
|
||||||
|
string path = item.Trim ().Replace (':', '.');
|
||||||
|
object result = ResolvePath (pr, path);
|
||||||
|
return SerializeObject (result, pr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ResolvePath (object target, string path)
|
||||||
|
{
|
||||||
|
if (target == null) return null;
|
||||||
|
if (string.IsNullOrEmpty (path)) return target;
|
||||||
|
|
||||||
|
string [] segments = path.Split ('.');
|
||||||
|
object current = target;
|
||||||
|
|
||||||
|
foreach (string seg in segments)
|
||||||
|
{
|
||||||
|
if (current == null) return null;
|
||||||
|
|
||||||
|
if (seg.Contains ("[") && seg.Contains ("]"))
|
||||||
|
{
|
||||||
|
int bracketStart = seg.IndexOf ('[');
|
||||||
|
string propName = seg.Substring (0, bracketStart);
|
||||||
|
string indexStr = seg.Substring (bracketStart + 1, seg.Length - bracketStart - 2);
|
||||||
|
int index;
|
||||||
|
if (!int.TryParse (indexStr, out index))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
object collection = GetPropertyValue (current, propName);
|
||||||
|
if (collection == null) return null;
|
||||||
|
current = GetIndexedValue (collection, index);
|
||||||
|
}
|
||||||
|
else if (string.Equals (seg, "length", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals (seg, "count", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
current = GetLength (current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = GetPropertyValue (current, seg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetPropertyValue (object target, string propName)
|
||||||
|
{
|
||||||
|
if (target == null) return null;
|
||||||
|
Type type = target.GetType ();
|
||||||
|
|
||||||
|
// 根对象 PackageReader 的属性名映射(不区分大小写)
|
||||||
|
if (type == typeof (PackageReader))
|
||||||
|
{
|
||||||
|
string mapped = null;
|
||||||
|
string lower = propName.ToLowerInvariant ();
|
||||||
|
switch (lower)
|
||||||
|
{
|
||||||
|
case "pkgid":
|
||||||
|
case "id":
|
||||||
|
case "identity": mapped = "Identity"; break;
|
||||||
|
case "prop":
|
||||||
|
case "properties": mapped = "Properties"; break;
|
||||||
|
case "prerequistes":
|
||||||
|
case "prerequisite":
|
||||||
|
case "prerequisites": mapped = "Prerequisites"; break;
|
||||||
|
case "res":
|
||||||
|
case "resources": mapped = "Resources"; break;
|
||||||
|
case "apps":
|
||||||
|
case "applications": mapped = "Applications"; break;
|
||||||
|
case "caps":
|
||||||
|
case "capabilities": mapped = "Capabilities"; break;
|
||||||
|
case "deps":
|
||||||
|
case "dependencies": mapped = "Dependencies"; break;
|
||||||
|
case "type": mapped = "Type"; break;
|
||||||
|
case "role": mapped = "Role"; break;
|
||||||
|
case "valid":
|
||||||
|
case "isvalid": mapped = "IsValid"; break;
|
||||||
|
case "filepath": mapped = "FilePath"; break;
|
||||||
|
}
|
||||||
|
if (mapped != null)
|
||||||
|
propName = mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRProperties 别名:Publisher / PublisherDisplayName 都映射到 Publisher 属性
|
||||||
|
if (type == typeof (PRProperties))
|
||||||
|
{
|
||||||
|
if (string.Equals (propName, "Publisher", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals (propName, "PublisherDisplayName", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
propName = "Publisher";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRApplication 中以 Base64 结尾的属性 -> 调用 NewAtBase64
|
||||||
|
if (type == typeof (PRApplication) && propName.EndsWith ("Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
string baseKey = propName.Substring (0, propName.Length - 6);
|
||||||
|
PRApplication app = (PRApplication)target;
|
||||||
|
MethodInfo method = typeof (PRApplication).GetMethod ("NewAtBase64", BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
return method.Invoke (app, new object [] { baseKey });
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRApplication 普通属性访问(字典键,大小写不敏感)
|
||||||
|
if (type == typeof (PRApplication))
|
||||||
|
{
|
||||||
|
PRApplication app = (PRApplication)target;
|
||||||
|
// 直接使用索引器,内部已处理资源解析和大小写不敏感
|
||||||
|
return app [propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRApplication 同理
|
||||||
|
if (type == typeof (MRApplication))
|
||||||
|
{
|
||||||
|
MRApplication app = (MRApplication)target;
|
||||||
|
return app [propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反射属性(不区分大小写)
|
||||||
|
PropertyInfo prop = type.GetProperty (propName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
return prop.GetValue (target, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反射字段
|
||||||
|
FieldInfo field = type.GetField (propName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
return field.GetValue (target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetIndexedValue (object collection, int index)
|
||||||
|
{
|
||||||
|
if (collection == null) return null;
|
||||||
|
|
||||||
|
// PRApplications
|
||||||
|
PRApplications apps = collection as PRApplications;
|
||||||
|
if (apps != null)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < apps.Applications.Count)
|
||||||
|
return apps.Applications [index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IList
|
||||||
|
IList list = collection as IList;
|
||||||
|
if (list != null && index >= 0 && index < list.Count)
|
||||||
|
return list [index];
|
||||||
|
|
||||||
|
// 索引器属性
|
||||||
|
PropertyInfo indexer = collection.GetType ().GetProperty ("Item", new [] { typeof (int) });
|
||||||
|
if (indexer != null)
|
||||||
|
return indexer.GetValue (collection, new object [] { index });
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? GetLength (object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
PRApplications apps = obj as PRApplications;
|
||||||
|
if (apps != null) return apps.Applications.Count;
|
||||||
|
PRDependencies deps = obj as PRDependencies;
|
||||||
|
if (deps != null) return deps.Dependencies.Count;
|
||||||
|
IList list = obj as IList;
|
||||||
|
if (list != null) return list.Count;
|
||||||
|
ICollection coll = obj as ICollection;
|
||||||
|
if (coll != null) return coll.Count;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object SerializeObject (object obj, PackageReader pr, bool filterBase64)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
Type type = obj.GetType ();
|
||||||
|
if (type.IsPrimitive || obj is string || obj is decimal || obj is Enum)
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
// PRIdentity
|
||||||
|
if (type == typeof (PRIdentity))
|
||||||
|
{
|
||||||
|
PRIdentity id = (PRIdentity)obj;
|
||||||
|
return new {
|
||||||
|
name = id.Name,
|
||||||
|
publisher = id.Publisher,
|
||||||
|
version = id.Version.Expression,
|
||||||
|
realVersion = id.RealVersion.Expression,
|
||||||
|
architecture = id.ProcessArchitecture.Select (e => {
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case Architecture.ARM: return "arm";
|
||||||
|
case Architecture.ARM64: return "arm64";
|
||||||
|
case Architecture.Neutral: return "neutral";
|
||||||
|
case Architecture.x64: return "x64";
|
||||||
|
case Architecture.x86: return "x86";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}).ToList (),
|
||||||
|
familyName = id.FamilyName,
|
||||||
|
fullName = id.FullName,
|
||||||
|
resourceId = id.ResourceId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRProperties
|
||||||
|
if (type == typeof (PRProperties))
|
||||||
|
{
|
||||||
|
PRProperties prop = (PRProperties)obj;
|
||||||
|
return new {
|
||||||
|
displayName = prop.DisplayName,
|
||||||
|
publisherDisplayName = prop.Publisher,
|
||||||
|
description = prop.Description,
|
||||||
|
logo = prop.Logo,
|
||||||
|
framework = prop.Framework,
|
||||||
|
resourcePackage = prop.ResourcePackage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRPrerequisites
|
||||||
|
if (type == typeof (PRPrerequisites))
|
||||||
|
{
|
||||||
|
PRPrerequisites pre = (PRPrerequisites)obj;
|
||||||
|
return new {
|
||||||
|
osMinVersion = pre.OSMinVersion,
|
||||||
|
osMaxVersionTested = pre.OSMaxVersionTested,
|
||||||
|
_osMinVersion = pre.OSMinVersionDescription,
|
||||||
|
_osMaxVersionTested = pre.OSMaxVersionDescription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRCapabilities
|
||||||
|
if (type == typeof (PRCapabilities))
|
||||||
|
{
|
||||||
|
PRCapabilities caps = (PRCapabilities)obj;
|
||||||
|
return new {
|
||||||
|
capabilities = caps.Capabilities,
|
||||||
|
deviceCapabilities = caps.DeviceCapabilities
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRDependencies
|
||||||
|
if (type == typeof (PRDependencies))
|
||||||
|
{
|
||||||
|
PRDependencies deps = (PRDependencies)obj;
|
||||||
|
return deps.Select (e => new {
|
||||||
|
name = e.Name,
|
||||||
|
publisher = e.Publisher,
|
||||||
|
minVersion = e.Version.ToString ()
|
||||||
|
}).ToList ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRApplications (集合)
|
||||||
|
if (type == typeof (PRApplications))
|
||||||
|
{
|
||||||
|
PRApplications apps = (PRApplications)obj;
|
||||||
|
return apps.Select (e => SerializeObject (e, pr, filterBase64)).ToList ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRApplication (单个应用)
|
||||||
|
if (type == typeof (PRApplication))
|
||||||
|
{
|
||||||
|
PRApplication app = (PRApplication)obj;
|
||||||
|
var dict = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var kv in app)
|
||||||
|
{
|
||||||
|
if (kv.Key.IndexOf ("Base64", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
continue;
|
||||||
|
string value = app.NewAt (kv.Key, pr.EnablePri);
|
||||||
|
if (string.IsNullOrWhiteSpace (value))
|
||||||
|
value = app.At (kv.Key);
|
||||||
|
dict [kv.Key] = value;
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDictionary
|
||||||
|
IDictionary dictObj = obj as IDictionary;
|
||||||
|
if (dictObj != null)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, object> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (DictionaryEntry entry in dictObj)
|
||||||
|
{
|
||||||
|
string key = entry.Key?.ToString ();
|
||||||
|
if (string.IsNullOrEmpty (key)) continue;
|
||||||
|
if (filterBase64 && key.EndsWith ("_Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
result [key] = SerializeObject (entry.Value, pr, filterBase64);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IEnumerable (非字符串)
|
||||||
|
if (!(obj is string))
|
||||||
|
{
|
||||||
|
IEnumerable enumerable = obj as IEnumerable;
|
||||||
|
if (enumerable != null)
|
||||||
|
{
|
||||||
|
var list = new List<object> ();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
list.Add (SerializeObject (item, pr, filterBase64));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后备:反射属性
|
||||||
|
var props = type.GetProperties (BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
var propDict = new Dictionary<string, object> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
if (prop.GetIndexParameters ().Length > 0) continue;
|
||||||
|
string propName = prop.Name;
|
||||||
|
if (filterBase64 && propName.EndsWith ("Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
object val = prop.GetValue (obj, null);
|
||||||
|
propDict [propName] = SerializeObject (val, pr, filterBase64);
|
||||||
|
}
|
||||||
|
return propDict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class ManifestReaderExt
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用于 CLI 输出的 JSON 对象(与 PackageReaderExt.GetJsonObjectForCli 结构一致)。
|
||||||
|
/// </summary>
|
||||||
|
public static object GetJsonObjectForCli (this ManifestReader mr)
|
||||||
|
{
|
||||||
|
var id = mr.Identity;
|
||||||
|
var prop = mr.Properties;
|
||||||
|
var pre = mr.Prerequisites;
|
||||||
|
var apps = mr.Applications;
|
||||||
|
var caps = mr.Capabilities;
|
||||||
|
var deps = mr.Dependencies;
|
||||||
|
var res = mr.Resources;
|
||||||
|
|
||||||
|
dynamic obj = new {
|
||||||
|
valid = mr.IsValid,
|
||||||
|
type = mr.Type.ToOther (e => {
|
||||||
|
switch ((PackageType)e)
|
||||||
|
{
|
||||||
|
case PackageType.Appx: return "appx";
|
||||||
|
case PackageType.Bundle: return "bundle";
|
||||||
|
default:
|
||||||
|
case PackageType.Unknown: return "unknown";
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
role = mr.Role.ToOther (o => {
|
||||||
|
switch ((PackageRole)o)
|
||||||
|
{
|
||||||
|
case PackageRole.Application: return "application";
|
||||||
|
case PackageRole.Framework: return "framework";
|
||||||
|
case PackageRole.Resource: return "resource";
|
||||||
|
default:
|
||||||
|
case PackageRole.Unknown: return "unknown";
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
identity = new {
|
||||||
|
name = id.Name,
|
||||||
|
publisher = id.Publisher,
|
||||||
|
version = id.Version.Expression,
|
||||||
|
architecture = id.ProcessArchitecture.Select (e => {
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case Architecture.ARM: return "arm";
|
||||||
|
case Architecture.ARM64: return "arm64";
|
||||||
|
case Architecture.Neutral: return "neutral";
|
||||||
|
case Architecture.x64: return "x64";
|
||||||
|
case Architecture.x86: return "x86";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}).ToList (),
|
||||||
|
familyName = id.FamilyName,
|
||||||
|
fullName = id.FullName,
|
||||||
|
resourceId = id.ResourceId
|
||||||
|
},
|
||||||
|
properties = new {
|
||||||
|
displayName = prop.DisplayName,
|
||||||
|
publisherDisplayName = prop.Publisher,
|
||||||
|
description = prop.Description,
|
||||||
|
logo = prop.Logo,
|
||||||
|
framework = prop.Framework,
|
||||||
|
resourcePackage = prop.ResourcePackage
|
||||||
|
},
|
||||||
|
prerequisite = new {
|
||||||
|
osMinVersion = pre.OSMinVersion.ToString (),
|
||||||
|
osMaxVersionTested = pre.OSMaxVersionTested.ToString (),
|
||||||
|
_osMinVersion = pre.OSMinVersionDescription,
|
||||||
|
_osMaxVersionTested = pre.OSMaxVersionDescription
|
||||||
|
},
|
||||||
|
resources = new {
|
||||||
|
languages = res.Languages,
|
||||||
|
scales = res.Scales,
|
||||||
|
dxFeatureLevels = res.DXFeatures.Select (d => {
|
||||||
|
switch (d)
|
||||||
|
{
|
||||||
|
case DXFeatureLevel.Level9: return 9;
|
||||||
|
case DXFeatureLevel.Level10: return 10;
|
||||||
|
case DXFeatureLevel.Level11: return 11;
|
||||||
|
case DXFeatureLevel.Level12: return 12;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}).ToList ()
|
||||||
|
},
|
||||||
|
applications = apps.Select (e => {
|
||||||
|
var dict = new Dictionary<string, string> ();
|
||||||
|
foreach (var kv in e)
|
||||||
|
{
|
||||||
|
if (kv.Key.IndexOf ("Base64", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
continue;
|
||||||
|
string value = e.NewAt (kv.Key, mr.EnablePri);
|
||||||
|
if (string.IsNullOrWhiteSpace (value))
|
||||||
|
value = e.At (kv.Key);
|
||||||
|
dict [kv.Key] = value;
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}).ToList (),
|
||||||
|
capabilities = new {
|
||||||
|
capabilities = caps.Capabilities,
|
||||||
|
deviceCapabilities = caps.DeviceCapabilities
|
||||||
|
},
|
||||||
|
dependencies = deps.Select (e => new {
|
||||||
|
name = e.Name,
|
||||||
|
publisher = e.Publisher,
|
||||||
|
minVersion = e.Version.ToString ()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 ManifestReader 中获取指定路径的值。
|
||||||
|
/// 支持格式:路径中可使用 '.' 或 ':' 分隔,支持索引器 '[index]',支持 '.length'。
|
||||||
|
/// 大小写不敏感,自动去除首尾空白。
|
||||||
|
/// 示例:
|
||||||
|
/// "Identity" -> 返回 Identity 字典
|
||||||
|
/// "Identity.Name" -> 返回名称
|
||||||
|
/// "Properties.Publisher" -> 返回发布者显示名称(别名)
|
||||||
|
/// "applications[0]" -> 返回第一个应用字典
|
||||||
|
/// "applications.length" -> 返回应用个数
|
||||||
|
/// "Applications[0].LogoBase64" -> 返回第一个应用的 LogoBase64
|
||||||
|
/// </summary>
|
||||||
|
public static object GetItem (this ManifestReader mr, string item)
|
||||||
|
{
|
||||||
|
if (mr == null) throw new ArgumentNullException ("mr");
|
||||||
|
if (string.IsNullOrWhiteSpace (item)) return null;
|
||||||
|
|
||||||
|
string path = item.Trim ().Replace (':', '.');
|
||||||
|
object result = ResolvePath (mr, path);
|
||||||
|
return SerializeObject (result, mr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ResolvePath (object target, string path)
|
||||||
|
{
|
||||||
|
if (target == null) return null;
|
||||||
|
if (string.IsNullOrEmpty (path)) return target;
|
||||||
|
|
||||||
|
string [] segments = path.Split ('.');
|
||||||
|
object current = target;
|
||||||
|
|
||||||
|
foreach (string seg in segments)
|
||||||
|
{
|
||||||
|
if (current == null) return null;
|
||||||
|
|
||||||
|
if (seg.Contains ("[") && seg.Contains ("]"))
|
||||||
|
{
|
||||||
|
int bracketStart = seg.IndexOf ('[');
|
||||||
|
string propName = seg.Substring (0, bracketStart);
|
||||||
|
string indexStr = seg.Substring (bracketStart + 1, seg.Length - bracketStart - 2);
|
||||||
|
int index;
|
||||||
|
if (!int.TryParse (indexStr, out index))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
object collection = GetPropertyValue (current, propName);
|
||||||
|
if (collection == null) return null;
|
||||||
|
current = GetIndexedValue (collection, index);
|
||||||
|
}
|
||||||
|
else if (string.Equals (seg, "length", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals (seg, "count", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
current = GetLength (current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = GetPropertyValue (current, seg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetPropertyValue (object target, string propName)
|
||||||
|
{
|
||||||
|
if (target == null) return null;
|
||||||
|
Type type = target.GetType ();
|
||||||
|
|
||||||
|
// 根对象 ManifestReader 的属性名映射(不区分大小写)
|
||||||
|
if (type == typeof (ManifestReader))
|
||||||
|
{
|
||||||
|
string mapped = null;
|
||||||
|
string lower = propName.ToLowerInvariant ();
|
||||||
|
switch (lower)
|
||||||
|
{
|
||||||
|
case "pkgid":
|
||||||
|
case "id":
|
||||||
|
case "identity": mapped = "Identity"; break;
|
||||||
|
case "prop":
|
||||||
|
case "properties": mapped = "Properties"; break;
|
||||||
|
case "prerequistes":
|
||||||
|
case "prerequisite":
|
||||||
|
case "prerequisites": mapped = "Prerequisites"; break;
|
||||||
|
case "res":
|
||||||
|
case "resources": mapped = "Resources"; break;
|
||||||
|
case "apps":
|
||||||
|
case "applications": mapped = "Applications"; break;
|
||||||
|
case "caps":
|
||||||
|
case "capabilities": mapped = "Capabilities"; break;
|
||||||
|
case "deps":
|
||||||
|
case "dependencies": mapped = "Dependencies"; break;
|
||||||
|
case "type": mapped = "Type"; break;
|
||||||
|
case "role": mapped = "Role"; break;
|
||||||
|
case "valid":
|
||||||
|
case "isvalid": mapped = "IsValid"; break;
|
||||||
|
case "file":
|
||||||
|
case "filepath": mapped = "FilePath"; break;
|
||||||
|
case "fileroot": mapped = "FileRoot"; break;
|
||||||
|
}
|
||||||
|
if (mapped != null)
|
||||||
|
propName = mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRProperties 别名:Publisher / PublisherDisplayName 都映射到 Publisher 属性
|
||||||
|
if (type == typeof (MRProperties))
|
||||||
|
{
|
||||||
|
if (string.Equals (propName, "Publisher", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals (propName, "PublisherDisplayName", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
propName = "Publisher";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRApplication 中以 Base64 结尾的属性 -> 调用 NewAtBase64
|
||||||
|
if (type == typeof (MRApplication) && propName.EndsWith ("Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
string baseKey = propName.Substring (0, propName.Length - 6);
|
||||||
|
MRApplication app = (MRApplication)target;
|
||||||
|
MethodInfo method = typeof (MRApplication).GetMethod ("NewAtBase64", BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
return method.Invoke (app, new object [] { baseKey });
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRApplication 普通属性访问(字典键,大小写不敏感)
|
||||||
|
if (type == typeof (MRApplication))
|
||||||
|
{
|
||||||
|
MRApplication app = (MRApplication)target;
|
||||||
|
return app [propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反射属性(不区分大小写)
|
||||||
|
PropertyInfo prop = type.GetProperty (propName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
return prop.GetValue (target, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反射字段
|
||||||
|
FieldInfo field = type.GetField (propName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
return field.GetValue (target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetIndexedValue (object collection, int index)
|
||||||
|
{
|
||||||
|
if (collection == null) return null;
|
||||||
|
|
||||||
|
MRApplications apps = collection as MRApplications;
|
||||||
|
if (apps != null)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < apps.Applications.Count)
|
||||||
|
return apps.Applications [index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IList list = collection as IList;
|
||||||
|
if (list != null && index >= 0 && index < list.Count)
|
||||||
|
return list [index];
|
||||||
|
|
||||||
|
PropertyInfo indexer = collection.GetType ().GetProperty ("Item", new [] { typeof (int) });
|
||||||
|
if (indexer != null)
|
||||||
|
return indexer.GetValue (collection, new object [] { index });
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? GetLength (object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
MRApplications apps = obj as MRApplications;
|
||||||
|
if (apps != null) return apps.Applications.Count;
|
||||||
|
MRDependencies deps = obj as MRDependencies;
|
||||||
|
if (deps != null) return deps.Dependencies.Count;
|
||||||
|
IList list = obj as IList;
|
||||||
|
if (list != null) return list.Count;
|
||||||
|
ICollection coll = obj as ICollection;
|
||||||
|
if (coll != null) return coll.Count;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object SerializeObject (object obj, ManifestReader mr, bool filterBase64)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
Type type = obj.GetType ();
|
||||||
|
if (type.IsPrimitive || obj is string || obj is decimal || obj is Enum)
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
// MRIdentity
|
||||||
|
if (type == typeof (MRIdentity))
|
||||||
|
{
|
||||||
|
MRIdentity id = (MRIdentity)obj;
|
||||||
|
return new {
|
||||||
|
name = id.Name,
|
||||||
|
publisher = id.Publisher,
|
||||||
|
version = id.Version.Expression,
|
||||||
|
architecture = id.ProcessArchitecture.Select (e => {
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case Architecture.ARM: return "arm";
|
||||||
|
case Architecture.ARM64: return "arm64";
|
||||||
|
case Architecture.Neutral: return "neutral";
|
||||||
|
case Architecture.x64: return "x64";
|
||||||
|
case Architecture.x86: return "x86";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}).ToList (),
|
||||||
|
familyName = id.FamilyName,
|
||||||
|
fullName = id.FullName,
|
||||||
|
resourceId = id.ResourceId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRProperties
|
||||||
|
if (type == typeof (MRProperties))
|
||||||
|
{
|
||||||
|
MRProperties prop = (MRProperties)obj;
|
||||||
|
return new {
|
||||||
|
displayName = prop.DisplayName,
|
||||||
|
publisherDisplayName = prop.Publisher,
|
||||||
|
description = prop.Description,
|
||||||
|
logo = prop.Logo,
|
||||||
|
framework = prop.Framework,
|
||||||
|
resourcePackage = prop.ResourcePackage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRPrerequisites
|
||||||
|
if (type == typeof (MRPrerequisites))
|
||||||
|
{
|
||||||
|
MRPrerequisites pre = (MRPrerequisites)obj;
|
||||||
|
return new {
|
||||||
|
osMinVersion = pre.OSMinVersion,
|
||||||
|
osMaxVersionTested = pre.OSMaxVersionTested,
|
||||||
|
_osMinVersion = pre.OSMinVersionDescription,
|
||||||
|
_osMaxVersionTested = pre.OSMaxVersionDescription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRResources
|
||||||
|
if (type == typeof (MRResources))
|
||||||
|
{
|
||||||
|
MRResources res = (MRResources)obj;
|
||||||
|
return new {
|
||||||
|
languages = res.Languages,
|
||||||
|
scales = res.Scales,
|
||||||
|
dxFeatureLevels = res.DXFeatures.Select (d => {
|
||||||
|
switch (d)
|
||||||
|
{
|
||||||
|
case DXFeatureLevel.Level9: return 9;
|
||||||
|
case DXFeatureLevel.Level10: return 10;
|
||||||
|
case DXFeatureLevel.Level11: return 11;
|
||||||
|
case DXFeatureLevel.Level12: return 12;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}).ToList ()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRCapabilities
|
||||||
|
if (type == typeof (MRCapabilities))
|
||||||
|
{
|
||||||
|
MRCapabilities caps = (MRCapabilities)obj;
|
||||||
|
return new {
|
||||||
|
capabilities = caps.Capabilities,
|
||||||
|
deviceCapabilities = caps.DeviceCapabilities
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRDependencies
|
||||||
|
if (type == typeof (MRDependencies))
|
||||||
|
{
|
||||||
|
MRDependencies deps = (MRDependencies)obj;
|
||||||
|
return deps.Select (e => new {
|
||||||
|
name = e.Name,
|
||||||
|
publisher = e.Publisher,
|
||||||
|
minVersion = e.Version.ToString ()
|
||||||
|
}).ToList ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRApplications (集合)
|
||||||
|
if (type == typeof (MRApplications))
|
||||||
|
{
|
||||||
|
MRApplications apps = (MRApplications)obj;
|
||||||
|
return apps.Select (e => SerializeObject (e, mr, filterBase64)).ToList ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MRApplication (单个应用)
|
||||||
|
if (type == typeof (MRApplication))
|
||||||
|
{
|
||||||
|
MRApplication app = (MRApplication)obj;
|
||||||
|
var dict = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var kv in app)
|
||||||
|
{
|
||||||
|
if (kv.Key.IndexOf ("Base64", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
continue;
|
||||||
|
string value = app.NewAt (kv.Key, mr.EnablePri);
|
||||||
|
if (string.IsNullOrWhiteSpace (value))
|
||||||
|
value = app.At (kv.Key);
|
||||||
|
dict [kv.Key] = value;
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDictionary
|
||||||
|
IDictionary dictObj = obj as IDictionary;
|
||||||
|
if (dictObj != null)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, object> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (DictionaryEntry entry in dictObj)
|
||||||
|
{
|
||||||
|
string key = entry.Key?.ToString ();
|
||||||
|
if (string.IsNullOrEmpty (key)) continue;
|
||||||
|
if (filterBase64 && key.EndsWith ("_Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
result [key] = SerializeObject (entry.Value, mr, filterBase64);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IEnumerable (非字符串)
|
||||||
|
if (!(obj is string))
|
||||||
|
{
|
||||||
|
IEnumerable enumerable = obj as IEnumerable;
|
||||||
|
if (enumerable != null)
|
||||||
|
{
|
||||||
|
var list = new List<object> ();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
list.Add (SerializeObject (item, mr, filterBase64));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后备:反射属性
|
||||||
|
var props = type.GetProperties (BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
var propDict = new Dictionary<string, object> (StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
if (prop.GetIndexParameters ().Length > 0) continue;
|
||||||
|
string propName = prop.Name;
|
||||||
|
if (filterBase64 && propName.EndsWith ("Base64", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
object val = prop.GetValue (obj, null);
|
||||||
|
propDict [propName] = SerializeObject (val, mr, filterBase64);
|
||||||
|
}
|
||||||
|
return propDict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,991 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using DataUtils;
|
||||||
|
using AppxPackage;
|
||||||
|
using System.IO;
|
||||||
|
using static AppxPackage.PackageManager;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Win32;
|
||||||
|
|
||||||
namespace PkgCLI
|
namespace PkgCLI
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
|
public delegate _I_HResult PackageOperation (string filePath, IEnumerable<string> depUris, DeploymentOptions options, PackageProgressCallback progress = null);
|
||||||
|
static readonly string [] helpArgs = new string [] {
|
||||||
|
"/?",
|
||||||
|
"-?",
|
||||||
|
"help",
|
||||||
|
"/help",
|
||||||
|
"-help",
|
||||||
|
"/h",
|
||||||
|
"-h"
|
||||||
|
};
|
||||||
|
static bool IsHelpParam (string arg) => helpArgs.Contains (arg.Normalize ());
|
||||||
|
static void PrintVersion ()
|
||||||
|
{
|
||||||
|
var verFilePath = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "version");
|
||||||
|
var verFileInst = new _I_File (verFilePath);
|
||||||
|
var verstr = verFileInst.Content?.Trim () ?? "0.0.0.1";
|
||||||
|
Console.WriteLine (
|
||||||
|
$"Package Manager CLI [Version {verstr}]\n(C) Windows Modern. All rights reserved."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static void PrintTotalHelp ()
|
||||||
|
{
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli <command> [arguments]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
/install Install a package.
|
||||||
|
/update Update a package (previous version must be installed).
|
||||||
|
/register Register an app manifest file.
|
||||||
|
/stage Stage a package.
|
||||||
|
/remove Remove the package.
|
||||||
|
/read Read package information.
|
||||||
|
/get List all installed apps.
|
||||||
|
/find Find installed apps.
|
||||||
|
/active Launch an app.
|
||||||
|
/config Configure settings (omit to show current config).
|
||||||
|
/? Show this help.
|
||||||
|
");
|
||||||
|
}
|
||||||
|
public static bool IsFilePathInList (List<string> filelist, string file)
|
||||||
|
{
|
||||||
|
foreach (var f in filelist)
|
||||||
|
{
|
||||||
|
if (f.NEquals (file)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static void AddFiles (List<string> filelist, string file)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (file)) return;
|
||||||
|
if (!File.Exists (file)) file = Path.Combine (Environment.CurrentDirectory, file);
|
||||||
|
if (!File.Exists (file)) return;
|
||||||
|
var ext = Path.GetExtension (file);
|
||||||
|
if (ext.NEquals (".txt"))
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines (file);
|
||||||
|
foreach (var l in lines)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (l)) continue;
|
||||||
|
var line = l.Trim ();
|
||||||
|
if (!File.Exists (l)) line = Path.Combine (Path.GetDirectoryName (file), l);
|
||||||
|
if (!File.Exists (line)) continue;
|
||||||
|
if (!IsFilePathInList (filelist, line))
|
||||||
|
filelist.Add (line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!IsFilePathInList (filelist, file)) filelist.Add (file);
|
||||||
|
}
|
||||||
|
public static void AddNStringItems (List<string> strlist, string str)
|
||||||
|
{
|
||||||
|
var find = false;
|
||||||
|
foreach (var l in strlist)
|
||||||
|
if (l.NEquals (str))
|
||||||
|
{
|
||||||
|
find = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!find) strlist.Add (str);
|
||||||
|
}
|
||||||
|
public static void ToFormatString (PMPackageInfo pkg, IEnumerable<string> filter)
|
||||||
|
{
|
||||||
|
if (pkg == null) return;
|
||||||
|
var labels = new List<string> ();
|
||||||
|
var values = new List<string> ();
|
||||||
|
|
||||||
|
var id = pkg.Identity;
|
||||||
|
labels.Add ("Identity:Name"); values.Add (id.Name);
|
||||||
|
labels.Add ("Identity:Publisher"); values.Add (id.Publisher);
|
||||||
|
labels.Add ("Identity:FamilyName"); values.Add (id.FamilyName);
|
||||||
|
labels.Add ("Identity:FullName"); values.Add (id.FullName);
|
||||||
|
labels.Add ("Identity:PublisherId"); values.Add (id.PublisherId);
|
||||||
|
labels.Add ("Identity:ResourceId"); values.Add (id.ResourceId);
|
||||||
|
labels.Add ("Identity:Version"); values.Add (id.Version?.ToString () ?? "");
|
||||||
|
labels.Add ("Identity:ProcessArchitecture"); values.Add (string.Join (", ", id.ProcessArchitecture));
|
||||||
|
|
||||||
|
var prop = pkg.Properties;
|
||||||
|
labels.Add ("Properties:DisplayName"); values.Add (prop.DisplayName);
|
||||||
|
labels.Add ("Properties:Description"); values.Add (prop.Description);
|
||||||
|
labels.Add ("Properties:Publisher"); values.Add (prop.Publisher);
|
||||||
|
labels.Add ("Properties:Framework"); values.Add (prop.Framework.ToString ());
|
||||||
|
labels.Add ("Properties:ResourcePackage"); values.Add (prop.ResourcePackage.ToString ());
|
||||||
|
labels.Add ("Properties:Logo"); values.Add (prop.Logo);
|
||||||
|
|
||||||
|
labels.Add ("IsBundle"); values.Add (pkg.IsBundle.ToString ());
|
||||||
|
labels.Add ("DevelopmentMode"); values.Add (pkg.DevelopmentMode.ToString ());
|
||||||
|
labels.Add ("InstallLocation"); values.Add (pkg.InstallLocation);
|
||||||
|
labels.Add ("Users"); values.Add (string.Join ("; ", pkg.Users));
|
||||||
|
|
||||||
|
Console.WriteLine ($"[{pkg.Identity.FullName}]");
|
||||||
|
var indicesToOutput = new List<int> ();
|
||||||
|
bool outputAll = (filter == null || !filter.Any ());
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.Count; i++)
|
||||||
|
{
|
||||||
|
if (outputAll)
|
||||||
|
{
|
||||||
|
indicesToOutput.Add (i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string label = labels [i];
|
||||||
|
foreach (string patternRaw in filter)
|
||||||
|
{
|
||||||
|
if (MatchPattern (label, patternRaw))
|
||||||
|
{
|
||||||
|
indicesToOutput.Add (i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indicesToOutput.Count == 0) return;
|
||||||
|
int maxLabelLen = 0;
|
||||||
|
foreach (int i in indicesToOutput)
|
||||||
|
if (labels [i].Length > maxLabelLen) maxLabelLen = labels [i].Length;
|
||||||
|
string format = "{0,-" + maxLabelLen + "} = {1}";
|
||||||
|
foreach (int i in indicesToOutput)
|
||||||
|
Console.WriteLine (format, labels [i], values [i]);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配模式(忽略大小写,首尾空格,支持通配符 *)
|
||||||
|
/// </summary>
|
||||||
|
private static bool MatchPattern (string text, string pattern)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty (pattern)) return true;
|
||||||
|
pattern = pattern.Trim ();
|
||||||
|
text = text ?? "";
|
||||||
|
|
||||||
|
if (pattern == "*") return true;
|
||||||
|
// 忽略大小写
|
||||||
|
var comparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
|
||||||
|
// 检查通配符位置
|
||||||
|
bool startsWithStar = pattern.StartsWith ("*");
|
||||||
|
bool endsWithStar = pattern.EndsWith ("*");
|
||||||
|
|
||||||
|
if (startsWithStar && endsWithStar)
|
||||||
|
{
|
||||||
|
// *middle* 包含子串
|
||||||
|
string middle = pattern.Substring (1, pattern.Length - 2);
|
||||||
|
return text.IndexOf (middle, comparison) >= 0;
|
||||||
|
}
|
||||||
|
else if (startsWithStar)
|
||||||
|
{
|
||||||
|
// *suffix 以 suffix 结尾
|
||||||
|
string suffix = pattern.Substring (1);
|
||||||
|
return text.EndsWith (suffix, comparison);
|
||||||
|
}
|
||||||
|
else if (endsWithStar)
|
||||||
|
{
|
||||||
|
// prefix* 以 prefix 开头
|
||||||
|
string prefix = pattern.Substring (0, pattern.Length - 1);
|
||||||
|
return text.StartsWith (prefix, comparison);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 精确匹配(忽略大小写)
|
||||||
|
return string.Equals (text, pattern, comparison);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 判断字符串是否为有效的包全名 (Package Full Name)
|
||||||
|
/// 格式: <IdentityName>_<Version>_<ProcessorArchitecture>_<ResourceId>_<PublisherId>
|
||||||
|
/// 其中 ResourceId 可以为空(表现为连续两个下划线)
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsPackageFullName (string fullName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (fullName)) return false;
|
||||||
|
string [] parts = fullName.Split (new char [] { '_' }, StringSplitOptions.None);
|
||||||
|
if (parts.Length != 5)
|
||||||
|
return false;
|
||||||
|
if (string.IsNullOrEmpty (parts [0])) // IdentityName
|
||||||
|
return false;
|
||||||
|
if (string.IsNullOrEmpty (parts [1])) // Version
|
||||||
|
return false;
|
||||||
|
if (string.IsNullOrEmpty (parts [2])) // ProcessorArchitecture
|
||||||
|
return false;
|
||||||
|
if (string.IsNullOrEmpty (parts [4])) // PublisherId
|
||||||
|
return false;
|
||||||
|
if (!parts [1].Contains ('.')) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 判断字符串是否为有效的包系列名 (Package Family Name)
|
||||||
|
/// 格式: <IdentityName>_<PublisherId>
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsPackageFamilyName (string familyName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (familyName)) return false;
|
||||||
|
string [] parts = familyName.Split ('_');
|
||||||
|
if (parts.Length != 2) return false;
|
||||||
|
if (string.IsNullOrEmpty (parts [0]) || string.IsNullOrEmpty (parts [1]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 从 args[startIndex..] 生成安全的命令行字符串
|
||||||
|
/// </summary>
|
||||||
|
private static string BuildCommandLine (string [] args, int startIndex)
|
||||||
|
{
|
||||||
|
if (args.Length <= startIndex) return null;
|
||||||
|
var sb = new StringBuilder ();
|
||||||
|
for (int i = startIndex; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
if (i > startIndex) sb.Append (' ');
|
||||||
|
sb.Append (EscapeArgument (args [i]));
|
||||||
|
}
|
||||||
|
return sb.ToString ();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 按 Win32 命令行规则转义单个参数
|
||||||
|
/// </summary>
|
||||||
|
private static string EscapeArgument (string arg)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty (arg)) return "\"\"";
|
||||||
|
bool needQuotes = false;
|
||||||
|
foreach (char c in arg)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace (c) || c == '"')
|
||||||
|
{
|
||||||
|
needQuotes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!needQuotes) return arg;
|
||||||
|
var sb = new StringBuilder ();
|
||||||
|
sb.Append ('"');
|
||||||
|
foreach (char c in arg)
|
||||||
|
{
|
||||||
|
if (c == '"') sb.Append ("\\\"");
|
||||||
|
else sb.Append (c);
|
||||||
|
}
|
||||||
|
sb.Append ('"');
|
||||||
|
return sb.ToString ();
|
||||||
|
}
|
||||||
|
public static readonly string [] configItems = new string [] {
|
||||||
|
"AppMetadataItems"
|
||||||
|
};
|
||||||
|
public static void RefreshConfig ()
|
||||||
|
{
|
||||||
|
var conf = new InitConfig (Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "config.ini"));
|
||||||
|
var sSettings = conf.GetSection ("Settings");
|
||||||
|
var kAppMetadatas = sSettings.GetKey ("PkgCLI:AppMetadataItems");
|
||||||
|
var appMd = kAppMetadatas.ReadString ("Id,BackgroundColor,DisplayName,ForegroundText,ShortName,SmallLogo,Square44x44Logo");
|
||||||
|
var appMdList = (appMd ?? "").Split (',', ';', '|');
|
||||||
|
for (var i = 0; i < appMdList.Length; i ++)
|
||||||
|
{
|
||||||
|
appMdList [i] = appMdList [i].Trim ();
|
||||||
|
}
|
||||||
|
PackageReader.UpdateApplicationItems (appMdList);
|
||||||
|
}
|
||||||
static void Main (string [] args)
|
static void Main (string [] args)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.InputEncoding = Encoding.UTF8;
|
||||||
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
|
if (args.Length <= 0 || args.Length >= 1 && IsHelpParam (args [0]))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
PrintTotalHelp ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var parser = new CliParsing ();
|
||||||
|
parser.Params = new HashSet<CmdParamName> (new CmdParamName [] {
|
||||||
|
new CmdParamName ("install", new string [] { "add" }),
|
||||||
|
new CmdParamName ("register", new string [] { "reg" }),
|
||||||
|
new CmdParamName ("update", new string [] { "up" }),
|
||||||
|
new CmdParamName ("stage"),
|
||||||
|
new CmdParamName ("remove", new string [] { "uninstall" }),
|
||||||
|
new CmdParamName ("read"),
|
||||||
|
new CmdParamName ("get"),
|
||||||
|
new CmdParamName ("find"),
|
||||||
|
new CmdParamName ("active", new string [] { "launch", "start", "activate" }),
|
||||||
|
new CmdParamName ("config", new string [] { "conf" }),
|
||||||
|
new CmdParamName ("version", new string [] { "ver" }),
|
||||||
|
new CmdParamName ("help", new string [] { "?", "h" }),
|
||||||
|
new CmdParamName ("developmode", new string [] { "develop" }),
|
||||||
|
new CmdParamName ("forceappshutdown", new string [] { "forceshutdown" , "appshutdown", "forcesd", "force"}),
|
||||||
|
new CmdParamName ("installallresources", new string [] {"allresources", "allres"}),
|
||||||
|
new CmdParamName ("savexml"),
|
||||||
|
new CmdParamName ("savejson"),
|
||||||
|
new CmdParamName ("fullname"),
|
||||||
|
new CmdParamName ("filter"),
|
||||||
|
new CmdParamName ("package", new string [] { "pkg" }),
|
||||||
|
new CmdParamName ("manifest", new string [] { "mani" }),
|
||||||
|
new CmdParamName ("usepri", new string [] { "pri" }),
|
||||||
|
new CmdParamName ("item"),
|
||||||
|
new CmdParamName ("set"),
|
||||||
|
new CmdParamName ("refresh"),
|
||||||
|
new CmdParamName ("show")
|
||||||
|
});
|
||||||
|
RefreshConfig ();
|
||||||
|
var cmds = parser.Parse (args);
|
||||||
|
if (CliPasingUtils.ParamsContainsOr (cmds, "install", "register", "update", "stage"))
|
||||||
|
{
|
||||||
|
#region help text: install register, update, stage
|
||||||
|
PrintVersion ();
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli <command> <file_path...> [/developmode] [/force] [/allres]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
/install Install a package. Supports .appx, .appxbundle, .msix, .msixbundle.
|
||||||
|
/register Register a package. Supports AppxManifest.xml or *.appxmanifest.
|
||||||
|
/update Update an installed app. Supports same formats as /install.
|
||||||
|
/stage Stage a package (pre-deploy). Supports same formats as /install.
|
||||||
|
|
||||||
|
Options (DeploymentOptions flags):
|
||||||
|
/developmode Install/register in development mode. Do not use with bundle packages.
|
||||||
|
/force Force application shutdown to allow registration when the package or its dependencies are in use.
|
||||||
|
/allres Skip resource applicability checks; stage/register all resource packages in a bundle.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<file_path...> One or more package or manifest file paths. Can be:
|
||||||
|
- Direct path to .appx, .msix, .appxbundle, .msixbundle, or .xml manifest.
|
||||||
|
- A .txt file containing a list of such paths (one per line).
|
||||||
|
- If no command is repeated, the command applies to all listed files.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /install MyApp.appx
|
||||||
|
pkgcli /register /manifest:AppxManifest.xml /developmode
|
||||||
|
pkgcli /update MyApp.msixbundle /force
|
||||||
|
pkgcli /stage MyApp.appx /allres
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var options = DeploymentOptions.None;
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "developmode")) options |= DeploymentOptions.DevelopmentMode;
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "forceappshutdown")) options |= DeploymentOptions.ForceAppShutdown;
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "installallresources")) options |= DeploymentOptions.InstallAllResources;
|
||||||
|
var totallist = new List<Tuple<int, string>> ();
|
||||||
|
var filelist = new List<string> ();
|
||||||
|
foreach (var f in cmds)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (f.Id))
|
||||||
|
{
|
||||||
|
var file = f.Value;
|
||||||
|
AddFiles (filelist, file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (f.Id)
|
||||||
|
{
|
||||||
|
case "install":
|
||||||
|
case "register":
|
||||||
|
case "update":
|
||||||
|
case "stage":
|
||||||
|
AddFiles (filelist, f.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var f in filelist) totallist.Add (new Tuple<int, string> (0, f));
|
||||||
|
if (cmds.ParamContains ("register") && cmds.ParamContains ("fullname"))
|
||||||
|
{
|
||||||
|
foreach (var f in cmds)
|
||||||
|
{
|
||||||
|
if (f.Id.NEquals ("fullname"))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (f.Value))
|
||||||
|
{
|
||||||
|
totallist.Add (new Tuple<int, string> (1, f.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
PackageOperation ope = null;
|
||||||
|
if (cmds.ParamContains ("install")) ope = AddPackage;
|
||||||
|
else if (cmds.ParamContains ("register")) ope = RegisterPackage;
|
||||||
|
else if (cmds.ParamContains ("update")) ope = UpdatePackage;
|
||||||
|
else if (cmds.ParamContains ("stage")) ope = StagePackage;
|
||||||
|
for (int i = 0; i < totallist.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine ();
|
||||||
|
var file = totallist [i];
|
||||||
|
Console.Write ($"\r({i + 1}/{totallist.Count}) Operation in progress...");
|
||||||
|
var hr = new _I_HResult (0);
|
||||||
|
var tempope = ope;
|
||||||
|
if (file.Item1 == 1)
|
||||||
|
{
|
||||||
|
if (cmds.ParamContains ("register")) tempope = RegisterPackageByFullName;
|
||||||
|
}
|
||||||
|
hr = tempope (file.Item2, null, options, prog => {
|
||||||
|
var str = $"\r({i + 1}/{filelist.Count}) Operation in progress... {prog}% of {(int)((i + prog * 0.01) / filelist.Count * 100)}%";
|
||||||
|
Console.Write (str);
|
||||||
|
});
|
||||||
|
if (hr.Failed)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write ($"\nPackage {i + 1} \"{file.Item2}\" Operation Error ({"0x" + hr.HResult.ToString ("X8")}): \n{hr.Message}");
|
||||||
|
Console.ResetColor ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine ("\nAll Done.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("remove"))
|
||||||
|
{
|
||||||
|
#region help text: remove
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /remove <package_full_name> [<package_full_name>...]
|
||||||
|
|
||||||
|
Operation:
|
||||||
|
Remove (uninstall) one or more packages.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<package_full_name> The full name of the installed package.
|
||||||
|
Format: <identity_name>_<version>_<architecture>_<resource_id>_<publisher_id>
|
||||||
|
Example: Microsoft.WinJS.1.0_1.0.9200.20789_neutral__8wekyb3d8bbwe
|
||||||
|
|
||||||
|
You can specify multiple full names separated by spaces.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /remove MyPackage_1.0.0.0_x64__abcd1234
|
||||||
|
pkgcli /remove PackageA_1.0.0.0_neutral__abcd1234 PackageB_2.0.0.0_x86__efgh5678
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var list = new List<string> ();
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (c.Id) || c.Id.NEquals ("remove"))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace (c.Value)) list.Add (c.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine ();
|
||||||
|
var file = list [i];
|
||||||
|
Console.Write ($"\r({i + 1}/{list.Count}) Operation in progress...");
|
||||||
|
var hr = RemovePackage (file, prog => {
|
||||||
|
var str = $"\r({i + 1}/{list.Count}) Operation in progress... {prog}% of {(int)((i + prog * 0.01) / list.Count * 100)}%";
|
||||||
|
Console.Write (str);
|
||||||
|
});
|
||||||
|
if (hr.Failed)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write ($"\nPackage {i + 1} \"{file}\" Operation Error ({"0x" + hr.HResult.ToString ("X8")}): \n{hr.Message}");
|
||||||
|
Console.ResetColor ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine ("\nAll Done.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("get"))
|
||||||
|
{
|
||||||
|
#region help text: get
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /get [/filter:<filter1,filter2,...>]
|
||||||
|
|
||||||
|
Operation:
|
||||||
|
List all installed packages with their properties.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
/filter:<filters> Show only the specified properties.
|
||||||
|
Filters are case-insensitive and support '*' wildcard.
|
||||||
|
Multiple filters separated by comma ',' or semicolon ';'.
|
||||||
|
Examples:
|
||||||
|
/filter:Identity:Name,Identity:Version
|
||||||
|
/filter:Identity:* (all Identity properties)
|
||||||
|
/filter:Properties:DisplayName
|
||||||
|
|
||||||
|
Output:
|
||||||
|
Each package is printed as a section [FullName] followed by key = value lines.
|
||||||
|
Redirect output to a file to save as INI-like format.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /get
|
||||||
|
pkgcli /get /filter:Identity:FullName,Properties:DisplayName
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var filter = new HashSet<string> (new NormalizeStringComparer ());
|
||||||
|
if (cmds.ParamContains ("filter"))
|
||||||
|
{
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (c.Id.NEmpty () || c.Id.NEquals ("filter"))
|
||||||
|
{
|
||||||
|
var items = c.Value.Split (',', ';');
|
||||||
|
foreach (var i in items) filter.Add (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var hr = GetPackages ();
|
||||||
|
if (hr.Item1.Failed)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine ($"Operation Error ({"0x" + hr.Item1.HResult.ToString ("X8")}): \n{hr.Item1.Message}");
|
||||||
|
Console.ResetColor ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var i in hr.Item2)
|
||||||
|
{
|
||||||
|
ToFormatString (i, filter);
|
||||||
|
Console.WriteLine ();
|
||||||
|
}
|
||||||
|
Console.WriteLine ("[Statistic]");
|
||||||
|
Console.WriteLine ($"Length = {hr.Item2.Count}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("find"))
|
||||||
|
{
|
||||||
|
#region help text: find
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine(@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /find <package_full_name|package_family_name> [/filter:<filters>]
|
||||||
|
pkgcli /find <identity_name> <identity_publisher> [/filter:<filters>]
|
||||||
|
|
||||||
|
Operation:
|
||||||
|
Find installed packages matching the given identifier.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<package_full_name> Full name of a package.
|
||||||
|
<package_family_name> Family name of a package.
|
||||||
|
<identity_name> Name part of the package identity (e.g., ""Microsoft.WindowsStore"").
|
||||||
|
<identity_publisher> Publisher part (e.g., ""CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"").
|
||||||
|
|
||||||
|
Options:
|
||||||
|
/filter:<filters> Same as in /get command.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /find Microsoft.WindowsStore_8wekyb3d8bbwe!App
|
||||||
|
pkgcli /find Microsoft.WindowsStore
|
||||||
|
pkgcli /find Microsoft.WindowsStore ""CN=Microsoft Corporation, ...""
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var names = new List<string> ();
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (c.Id.NEmpty ())
|
||||||
|
{
|
||||||
|
if (c.Value.NEmpty ()) continue;
|
||||||
|
names.Add (c.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var filter = new HashSet<string> (new NormalizeStringComparer ());
|
||||||
|
if (cmds.ParamContains ("filter"))
|
||||||
|
{
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (c.Id.NEquals ("filter"))
|
||||||
|
{
|
||||||
|
var items = c.Value.Split (',', ';');
|
||||||
|
foreach (var i in items) filter.Add (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var result = new List<Tuple<string, Tuple<_I_HResult, List<PMPackageInfo>>>> ();
|
||||||
|
if (names.Count == 2)
|
||||||
|
{
|
||||||
|
result.Add (new Tuple<string, Tuple<_I_HResult, List<PMPackageInfo>>> (names [0], FindPackage (names [0], names [1])));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var n in names)
|
||||||
|
{
|
||||||
|
if (IsPackageFullName (n))
|
||||||
|
{
|
||||||
|
result.Add (new Tuple<string, Tuple<_I_HResult, List<PMPackageInfo>>> (n, FindPackageByFullName (n)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add (new Tuple<string, Tuple<_I_HResult, List<PMPackageInfo>>> (n, FindPackage (n)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var r in result)
|
||||||
|
{
|
||||||
|
foreach (var l in r.Item2.Item2)
|
||||||
|
{
|
||||||
|
ToFormatString (l, filter);
|
||||||
|
Console.WriteLine ();
|
||||||
|
}
|
||||||
|
Console.WriteLine ($"[Statistic:{r.Item1}]");
|
||||||
|
Console.WriteLine ($"HResult = {"0x" + r.Item2.Item1.HResult.ToString ("X8")}");
|
||||||
|
Console.WriteLine ($"Message = {Escape.ToEscape (r.Item2.Item1.Message)}");
|
||||||
|
Console.WriteLine ($"Length = {r.Item2.Item2.Count}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("active"))
|
||||||
|
{
|
||||||
|
#region help text: active
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /active <app_user_model_id> [arguments]
|
||||||
|
|
||||||
|
Operation:
|
||||||
|
Launch (activate) a Universal Windows Platform (UWP) app.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<app_user_model_id> The Application User Model ID (AUMID).
|
||||||
|
Format: <package_family_name>!<app_id>
|
||||||
|
Example: Microsoft.WindowsStore_8wekyb3d8bbwe!App
|
||||||
|
|
||||||
|
[arguments] Optional command-line arguments passed to the app.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /active Microsoft.WindowsStore_8wekyb3d8bbwe!App
|
||||||
|
pkgcli /active MyAppFamily!App --fullscreen --debug
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (c.Id) || c.Id.NEquals ("active"))
|
||||||
|
{
|
||||||
|
if (c.Value.NEmpty ()) continue;
|
||||||
|
var i = 0;
|
||||||
|
for (i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
if (args [i].NNormalize ().IndexOf (c.Value.NNormalize ()) >= 0) break;
|
||||||
|
}
|
||||||
|
var hr = ActiveApp (c.Value, BuildCommandLine (args, i + 1));
|
||||||
|
if (hr.Succeeded) Console.WriteLine ("Done.");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write ($"Operation Error ({"0x" + hr.HResult.ToString ("X8")}): \n{hr.Message}");
|
||||||
|
Console.ResetColor ();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("read"))
|
||||||
|
{
|
||||||
|
#region help text: read
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /read [options] [<file>]
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Read an Appx/MSIX package (.appx, .appxbundle, .msix, .msixbundle) or its manifest file (.xml, .appxpackage, .msixpackage).
|
||||||
|
Extracts package/manifest information and outputs as JSON to the console, or saves as a ZIP archive containing either info.json or info.xml.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
/manifest:<file> Specify a manifest file to read.
|
||||||
|
/package:<file> Specify a package file to read.
|
||||||
|
<file> If no /manifest or /package is provided, the first unnamed parameter is used as the input file.
|
||||||
|
File type is auto-detected by extension:
|
||||||
|
.xml, .appxpackage, .msixpackage → ManifestReader
|
||||||
|
.appx, .appxbundle, .msix, .msixbundle → PackageReader
|
||||||
|
|
||||||
|
/item:<path> Instead of outputting the whole JSON object, output only the value at the given path.
|
||||||
|
Path syntax: use '.' or ':' as separators, case-insensitive.
|
||||||
|
Supports indexers (e.g., [0]), .length for collections, and automatic Base64 access (e.g., LogoBase64).
|
||||||
|
Examples:
|
||||||
|
/item:Identity.Name
|
||||||
|
/item:Properties.Publisher
|
||||||
|
/item:applications[0]
|
||||||
|
/item:applications.length
|
||||||
|
/item:applications[0].LogoBase64
|
||||||
|
|
||||||
|
/usepri Enable PRI resource resolution (localization and scaled images). Required for proper resource string and image resolution.
|
||||||
|
|
||||||
|
/savexml:<output> Save extracted data as an XML file wrapped in a ZIP archive.
|
||||||
|
The archive will contain an info.xml file with the structured data.
|
||||||
|
|
||||||
|
/savejson:<output> Save extracted data as a JSON file wrapped in a ZIP archive.
|
||||||
|
The archive will contain an info.json file with the structured data.
|
||||||
|
|
||||||
|
/help Display this help text.
|
||||||
|
|
||||||
|
Output Behavior:
|
||||||
|
- If neither /savexml nor /savejson is given, the information is printed directly to the console as JSON.
|
||||||
|
- If /item is used, only the value at the specified path is printed (as a string, version, or formatted JSON).
|
||||||
|
- If /savexml or /savejson is used, the data is saved to the specified file (ZIP archive) and a success/failure message is shown.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /read MyApp.appx
|
||||||
|
pkgcli /read /manifest:AppxManifest.xml /usepri
|
||||||
|
pkgcli /read /package:MyApp.msixbundle /item:Identity.Name
|
||||||
|
pkgcli /read /package:MyApp.appx /savejson:output.zip
|
||||||
|
pkgcli /read MyApp.appx /item:applications[0].DisplayName
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var filename = "";
|
||||||
|
var readtype = "";
|
||||||
|
var savename = "";
|
||||||
|
var savetype = "default";
|
||||||
|
CommandParam cmd = null;
|
||||||
|
cmd = cmds.GetFromId ("manifest");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
filename = cmd.Value;
|
||||||
|
readtype = "manifest";
|
||||||
|
}
|
||||||
|
cmd = cmds.GetFromId ("package");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
filename = cmd.Value;
|
||||||
|
readtype = "package";
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace (filename) || !File.Exists (filename))
|
||||||
|
{
|
||||||
|
cmd = cmds.GetFromId ("");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
if (File.Exists (cmd.Value))
|
||||||
|
{
|
||||||
|
filename = cmd.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace (readtype))
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension (cmd.Value);
|
||||||
|
if (ext.NEquals (".xml") || ext.NEquals (".appxpackage") || ext.NEquals (".msixpackage"))
|
||||||
|
readtype = "manifest";
|
||||||
|
else readtype = "package";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace (filename) || !File.Exists (filename))
|
||||||
|
{
|
||||||
|
cmd = cmds.GetFromId ("read");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
if (File.Exists (cmd.Value))
|
||||||
|
{
|
||||||
|
filename = cmd.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace (readtype))
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension (cmd.Value);
|
||||||
|
if (ext.NEquals (".xml") || ext.NEquals (".appxpackage") || ext.NEquals (".msixpackage"))
|
||||||
|
readtype = "manifest";
|
||||||
|
else readtype = "package";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace (filename) || !File.Exists (filename)) throw new FileNotFoundException ();
|
||||||
|
cmd = cmds.GetFromId ("savexml");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
savename = cmd.Value;
|
||||||
|
savetype = "xml";
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace (savename))
|
||||||
|
{
|
||||||
|
cmd = cmds.GetFromId ("savejson");
|
||||||
|
if (cmd != null)
|
||||||
|
{
|
||||||
|
savename = cmd.Value;
|
||||||
|
savetype = "json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace (savename))
|
||||||
|
{
|
||||||
|
savetype = "ini";
|
||||||
|
}
|
||||||
|
switch (readtype)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case "package":
|
||||||
|
{
|
||||||
|
var pr = new PackageReader (filename);
|
||||||
|
pr.UsePri = cmds.ParamContains ("usepri");
|
||||||
|
pr.EnablePri = true;
|
||||||
|
switch (savetype)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case "default":
|
||||||
|
{
|
||||||
|
if (cmds.ParamContains ("item"))
|
||||||
|
{
|
||||||
|
object value = pr.GetItem (cmds.GetFromId ("item").Value);
|
||||||
|
if (value is string) Console.WriteLine (value as string);
|
||||||
|
else if (value is DataUtils.Version) Console.WriteLine (value.ToString ());
|
||||||
|
else Console.WriteLine (JsonConvert.SerializeObject (value, Formatting.Indented));
|
||||||
|
}
|
||||||
|
else Console.WriteLine (JsonConvert.SerializeObject (pr.GetJsonObjectForCli (), Formatting.Indented));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "json":
|
||||||
|
{
|
||||||
|
var res = pr.SaveJsonFileCS (savename);
|
||||||
|
if (res) Console.WriteLine ("Succeeded!");
|
||||||
|
else Console.WriteLine ("Failed.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "xml":
|
||||||
|
{
|
||||||
|
var res = pr.SaveXmlFileCS (savename);
|
||||||
|
if (res) Console.WriteLine ("Succeeded!");
|
||||||
|
else Console.WriteLine ("Failed.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "manifest":
|
||||||
|
{
|
||||||
|
var mr = new ManifestReader (filename);
|
||||||
|
mr.UsePri = cmds.ParamContains ("usepri");
|
||||||
|
mr.EnablePri = true;
|
||||||
|
if (cmds.ParamContains ("item"))
|
||||||
|
{
|
||||||
|
object value = mr.GetItem (cmds.GetFromId ("item").Value);
|
||||||
|
if (value is string) Console.WriteLine (value as string);
|
||||||
|
else if (value is DataUtils.Version) Console.WriteLine (value.ToString ());
|
||||||
|
else Console.WriteLine (JsonConvert.SerializeObject (value, Formatting.Indented));
|
||||||
|
}
|
||||||
|
else Console.WriteLine (JsonConvert.SerializeObject (mr.GetJsonObjectForCli (), Formatting.Indented));
|
||||||
|
//Console.WriteLine (mr.BuildJsonText ());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("config"))
|
||||||
|
{
|
||||||
|
#region help text: config
|
||||||
|
if (CliPasingUtils.ParamContains (cmds, "help"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
Console.WriteLine (@"
|
||||||
|
Usage:
|
||||||
|
pkgcli /config Show current configuration (all keys).
|
||||||
|
pkgcli /config /show:<key> Show value of a specific configuration key.
|
||||||
|
pkgcli /config /set:<key> <value> Set configuration key to the given value.
|
||||||
|
pkgcli /config /refresh Reload configuration from config.ini.
|
||||||
|
|
||||||
|
Configuration keys:
|
||||||
|
AppMetadataItems Comma-separated list of application metadata fields to read from manifests.
|
||||||
|
Default: ""Id,BackgroundColor,DisplayName,ForegroundText,ShortName,SmallLogo,Square44x44Logo""
|
||||||
|
|
||||||
|
Configuration file:
|
||||||
|
config.ini in the same directory as pkgcli.exe.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
pkgcli /config
|
||||||
|
pkgcli /config /show:AppMetadataItems
|
||||||
|
pkgcli /config /set:AppMetadataItems ""Id,DisplayName,Logo""
|
||||||
|
pkgcli /config /refresh
|
||||||
|
");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
var conf = new InitConfig (Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "config.ini"));
|
||||||
|
var sSettings = conf.GetSection ("Settings");
|
||||||
|
if (cmds.ParamContains ("refresh")) RefreshConfig ();
|
||||||
|
else if (cmds.ParamContains ("set"))
|
||||||
|
{
|
||||||
|
var cmd = cmds.GetFromId ("set");
|
||||||
|
var key = cmd.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace (key)) throw new InvalidOperationException ($"key is empty");
|
||||||
|
var isfind = false;
|
||||||
|
foreach (var i in configItems)
|
||||||
|
{
|
||||||
|
if (i.NEquals (key))
|
||||||
|
{
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind) throw new KeyNotFoundException ($"Error: cannot find the key \"{key}\"");
|
||||||
|
var valuelist = new List<string> ();
|
||||||
|
foreach (var c in cmds)
|
||||||
|
{
|
||||||
|
if (c.Id.NEmpty ())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace (c.Value)) continue;
|
||||||
|
valuelist.Add (c.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var res = sSettings.GetKey ($"PkgCLI:{key.Trim ()}").Set (valuelist.Join (","));
|
||||||
|
if (res) Console.WriteLine ("Succeeded!");
|
||||||
|
else Console.WriteLine ("Failed.");
|
||||||
|
}
|
||||||
|
else if (cmds.ParamContains ("show"))
|
||||||
|
{
|
||||||
|
var cmd = cmds.GetFromId ("show");
|
||||||
|
var key = cmd.Value;
|
||||||
|
if (string.IsNullOrWhiteSpace (key))
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, string> ();
|
||||||
|
foreach (var k in configItems)
|
||||||
|
{
|
||||||
|
var cKey = sSettings.GetKey ($"PkgCLI:{k}");
|
||||||
|
dict [k] = cKey.ReadString ("(use default)");
|
||||||
|
}
|
||||||
|
Console.WriteLine (dict.FormatDictionaryAligned ("="));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var isfind = false;
|
||||||
|
foreach (var i in configItems)
|
||||||
|
{
|
||||||
|
if (i.NEquals (key))
|
||||||
|
{
|
||||||
|
isfind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isfind) throw new KeyNotFoundException ($"Error: cannot find the key \"{key}\"");
|
||||||
|
var value = sSettings.GetKey ($"PkgCLI:{key.Trim ()}").ReadString ("(use default)");
|
||||||
|
Console.WriteLine (value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmds.ParamsContainsOr ("version"))
|
||||||
|
{
|
||||||
|
PrintVersion ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine ("Invalid args. Please use \"/help\" to get help.");
|
||||||
|
Console.ResetColor ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine ($"Exception {ex.GetType ()}, \nMessage: \n {ex.Message}\nStack: \n {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.ResetColor ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
112
PkgCLI/Text.cs
Normal file
112
PkgCLI/Text.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace System
|
||||||
|
{
|
||||||
|
namespace Text
|
||||||
|
{
|
||||||
|
public static class Escape
|
||||||
|
{
|
||||||
|
/// <summary>按 JSON 规范对字符串进行转义。</summary>
|
||||||
|
/// <param name="input">原始字符串。</param>
|
||||||
|
/// <returns>转义后的 JSON 字符串字面量内容(不含外围双引号)。</returns>
|
||||||
|
public static string ToEscape (string input)
|
||||||
|
{
|
||||||
|
if (input == null) throw new ArgumentNullException (nameof (input));
|
||||||
|
if (input.Length == 0) return string.Empty;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder (input.Length);
|
||||||
|
foreach (char c in input)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '"': sb.Append ("\\\""); break;
|
||||||
|
case '\\': sb.Append ("\\\\"); break;
|
||||||
|
case '\b': sb.Append ("\\b"); break;
|
||||||
|
case '\f': sb.Append ("\\f"); break;
|
||||||
|
case '\n': sb.Append ("\\n"); break;
|
||||||
|
case '\r': sb.Append ("\\r"); break;
|
||||||
|
case '\t': sb.Append ("\\t"); break;
|
||||||
|
default:
|
||||||
|
// 控制字符 (U+0000 - U+001F) 需转义为 \uXXXX
|
||||||
|
if (c <= 0x1F)
|
||||||
|
{
|
||||||
|
sb.Append ("\\u");
|
||||||
|
sb.Append (((int)c).ToString ("X4"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append (c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.ToString ();
|
||||||
|
}
|
||||||
|
/// <summary>按 JSON 规范反转义字符串。</summary>
|
||||||
|
/// <param name="input">转义后的 JSON 字符串内容(不含外围双引号)。</param>
|
||||||
|
/// <returns>原始字符串。</returns>
|
||||||
|
/// <exception cref="FormatException">遇到非法转义序列时抛出。</exception>
|
||||||
|
public static string Unescape (string input)
|
||||||
|
{
|
||||||
|
if (input == null) throw new ArgumentNullException (nameof (input));
|
||||||
|
if (input.Length == 0) return string.Empty;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder (input.Length);
|
||||||
|
int i = 0;
|
||||||
|
while (i < input.Length)
|
||||||
|
{
|
||||||
|
char c = input [i];
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (i >= input.Length)
|
||||||
|
throw new FormatException ("字符串末尾包含不完整的转义序列。");
|
||||||
|
|
||||||
|
char next = input [i];
|
||||||
|
switch (next)
|
||||||
|
{
|
||||||
|
case '"': sb.Append ('"'); break;
|
||||||
|
case '\\': sb.Append ('\\'); break;
|
||||||
|
case '/': sb.Append ('/'); break; // 允许转义斜杠
|
||||||
|
case 'b': sb.Append ('\b'); break;
|
||||||
|
case 'f': sb.Append ('\f'); break;
|
||||||
|
case 'n': sb.Append ('\n'); break;
|
||||||
|
case 'r': sb.Append ('\r'); break;
|
||||||
|
case 't': sb.Append ('\t'); break;
|
||||||
|
case 'u':
|
||||||
|
i++;
|
||||||
|
if (i + 4 > input.Length)
|
||||||
|
throw new FormatException ("\\u 转义后必须跟随 4 位十六进制数字。");
|
||||||
|
|
||||||
|
string hex = input.Substring (i, 4);
|
||||||
|
int codePoint; // 先声明变量,兼容 C# 5/6
|
||||||
|
if (!int.TryParse (hex,
|
||||||
|
NumberStyles.HexNumber,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
out codePoint))
|
||||||
|
{
|
||||||
|
throw new FormatException (string.Format ("无效的 Unicode 转义序列: \\u{0}", hex));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append ((char)codePoint);
|
||||||
|
i += 3; // 循环末尾会再加1,因此这里只增加3
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new FormatException (string.Format ("未识别的转义序列: \\{0}", next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append (c);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.ToString ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
PkgCLI/packages.config
Normal file
4
PkgCLI/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net40" />
|
||||||
|
</packages>
|
||||||
@@ -381,6 +381,7 @@ void DestroyPriFileInstance (PCSPRIFILE pFilePri)
|
|||||||
if (!pFilePri) return;
|
if (!pFilePri) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#ifdef ELDER_FUNC
|
||||||
CreateScopedLock (g_threadlock);
|
CreateScopedLock (g_threadlock);
|
||||||
auto it = g_tasklist.find (pFilePri);
|
auto it = g_tasklist.find (pFilePri);
|
||||||
if (it != g_tasklist.end ())
|
if (it != g_tasklist.end ())
|
||||||
@@ -388,6 +389,7 @@ void DestroyPriFileInstance (PCSPRIFILE pFilePri)
|
|||||||
it->second.bIsRunning = false;
|
it->second.bIsRunning = false;
|
||||||
g_tasklist.erase (it);
|
g_tasklist.erase (it);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
IntPtr handlePtr = IntPtr (pFilePri);
|
IntPtr handlePtr = IntPtr (pFilePri);
|
||||||
System::Runtime::InteropServices::GCHandle handle = System::Runtime::InteropServices::GCHandle::FromIntPtr (handlePtr);
|
System::Runtime::InteropServices::GCHandle handle = System::Runtime::InteropServices::GCHandle::FromIntPtr (handlePtr);
|
||||||
PriFileInst ^inst = safe_cast <PriFileInst ^> (handle.Target);
|
PriFileInst ^inst = safe_cast <PriFileInst ^> (handle.Target);
|
||||||
|
|||||||
Binary file not shown.
@@ -442,6 +442,8 @@
|
|||||||
createShortcutButton.setAttribute("data-app-user-model-id", item.AppUserModelID);
|
createShortcutButton.setAttribute("data-app-user-model-id", item.AppUserModelID);
|
||||||
createShortcutButton.textContent = strres.get("MANAGER_APP_SHORTCUTCREATE_TITLE");
|
createShortcutButton.textContent = strres.get("MANAGER_APP_SHORTCUTCREATE_TITLE");
|
||||||
createShortcutButton.style.marginRight = "10px";
|
createShortcutButton.style.marginRight = "10px";
|
||||||
|
appItem.setAttribute("title", item.Id);
|
||||||
|
appItem.setAttribute("aria-label", item.DisplayName || item.ShortName);
|
||||||
Windows.UI.Event.Util.addEvent(launchButton, "click", function(e) {
|
Windows.UI.Event.Util.addEvent(launchButton, "click", function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
Package.manager.active(this.getAttribute("data-app-user-model-id"));
|
Package.manager.active(this.getAttribute("data-app-user-model-id"));
|
||||||
|
|||||||
Reference in New Issue
Block a user