mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
1027 lines
30 KiB
C#
1027 lines
30 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using AppxPackage.Info;
|
||
using NativeWrappers;
|
||
using System.IO;
|
||
using System.Threading;
|
||
//using PriFormat;
|
||
namespace AppxPackage
|
||
{
|
||
public static class DataUrlHelper
|
||
{
|
||
[DllImport ("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||
private static extern int FindMimeFromData (
|
||
IntPtr pBC,
|
||
string pwzUrl,
|
||
byte [] pBuffer,
|
||
int cbSize,
|
||
string pwzMimeProposed,
|
||
int dwMimeFlags,
|
||
out IntPtr ppwzMimeOut,
|
||
int dwReserved);
|
||
private const int FMFD_RETURNUPDATEDIMGMIMES = 0x00000001;
|
||
private const int FMFD_IGNOREMIMETEXTPLAIN = 0x00000010;
|
||
private const int FMFD_URLASFILENAME = 0x00000020;
|
||
private static string GetMimeTypeFromStream (Stream stream)
|
||
{
|
||
if (stream == null) return string.Empty;
|
||
long originalPos = 0;
|
||
try
|
||
{
|
||
if (stream.CanSeek)
|
||
{
|
||
originalPos = stream.Position;
|
||
stream.Seek (0, SeekOrigin.Begin);
|
||
}
|
||
byte [] buffer = new byte [256];
|
||
int bytesRead = stream.Read (buffer, 0, buffer.Length);
|
||
if (stream.CanSeek) stream.Seek (originalPos, SeekOrigin.Begin);
|
||
if (bytesRead == 0) return string.Empty;
|
||
IntPtr mimePtr;
|
||
int hr = FindMimeFromData (
|
||
IntPtr.Zero,
|
||
null,
|
||
buffer,
|
||
bytesRead,
|
||
null,
|
||
FMFD_RETURNUPDATEDIMGMIMES | FMFD_IGNOREMIMETEXTPLAIN | FMFD_URLASFILENAME,
|
||
out mimePtr,
|
||
0);
|
||
string mime = string.Empty;
|
||
if (hr == 0 && mimePtr != IntPtr.Zero)
|
||
{
|
||
mime = Marshal.PtrToStringUni (mimePtr);
|
||
Marshal.FreeCoTaskMem (mimePtr);
|
||
}
|
||
if (string.IsNullOrEmpty (mime))
|
||
{
|
||
// fallback by magic bytes
|
||
if (bytesRead >= 8 && buffer [0] == 0x89 && buffer [1] == 0x50 && buffer [2] == 0x4E && buffer [3] == 0x47 &&
|
||
buffer [4] == 0x0D && buffer [5] == 0x0A && buffer [6] == 0x1A && buffer [7] == 0x0A)
|
||
mime = "image/png";
|
||
else if (bytesRead >= 3 && buffer [0] == 0xFF && buffer [1] == 0xD8)
|
||
mime = "image/jpeg";
|
||
else if (bytesRead >= 6 && Encoding.ASCII.GetString (buffer, 0, 6) == "GIF89a")
|
||
mime = "image/gif";
|
||
else if (bytesRead >= 2 && buffer [0] == 'B' && buffer [1] == 'M')
|
||
mime = "image/bmp";
|
||
else if (bytesRead >= 12 && Encoding.ASCII.GetString (buffer, 0, 4) == "RIFF" &&
|
||
Encoding.ASCII.GetString (buffer, 8, 4) == "WEBP")
|
||
mime = "image/webp";
|
||
else if (bytesRead >= 4 && buffer [0] == 0x00 && buffer [1] == 0x00 && buffer [2] == 0x01 && buffer [3] == 0x00)
|
||
mime = "image/x-icon";
|
||
else
|
||
mime = "application/octet-stream";
|
||
}
|
||
return mime;
|
||
}
|
||
catch
|
||
{
|
||
return string.Empty;
|
||
}
|
||
}
|
||
public static string FileToDataUrl (string filePath)
|
||
{
|
||
if (string.IsNullOrEmpty (filePath)) return string.Empty;
|
||
try
|
||
{
|
||
using (FileStream fs = new FileStream (filePath, FileMode.Open, FileAccess.Read))
|
||
{
|
||
if (fs.Length == 0) return string.Empty;
|
||
string mime = GetMimeTypeFromStream (fs);
|
||
if (string.IsNullOrEmpty (mime)) return string.Empty;
|
||
byte [] bytes = new byte [fs.Length];
|
||
fs.Seek (0, SeekOrigin.Begin);
|
||
int read = fs.Read (bytes, 0, bytes.Length);
|
||
if (read != bytes.Length) return string.Empty;
|
||
string base64 = Convert.ToBase64String (bytes);
|
||
return $"data:{mime};base64,{base64}";
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
return string.Empty;
|
||
}
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class BaseInfoSectWithPRISingle: BaseInfoSection
|
||
{
|
||
protected Ref<ManifestReader> m_reader = new Ref<ManifestReader> (null);
|
||
protected Ref<PriReader> m_pri = new Ref<PriReader> (null);
|
||
protected Ref<bool> m_usePri = new Ref<bool> (false);
|
||
protected Ref<bool> m_enablePri = new Ref<bool> (false);
|
||
public BaseInfoSectWithPRISingle (ref IntPtr hReader, ManifestReader reader, ref PriReader pri, ref bool usePri, ref bool enablePri) : base (ref hReader)
|
||
{
|
||
m_reader.Set (reader);
|
||
m_pri.Set (pri);
|
||
m_usePri.Set (usePri);
|
||
m_enablePri.Set (enablePri);
|
||
}
|
||
public new void Dispose ()
|
||
{
|
||
try { m_reader.Set (null); } catch (Exception) { }
|
||
try { m_pri.Set (null); } catch (Exception) { }
|
||
m_usePri = null;
|
||
m_enablePri = null;
|
||
m_hReader = null;
|
||
}
|
||
~BaseInfoSectWithPRISingle () { Dispose (); }
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRIdentity: BaseInfoSection, Info.IIdentity
|
||
{
|
||
public MRIdentity (ref IntPtr hReader) : base (ref hReader) { }
|
||
protected string StringValue (uint name)
|
||
{
|
||
var ptr = PackageReadHelper.GetManifestIdentityStringValue (m_hReader, name);
|
||
return PackageReadHelper.GetStringAndFreeFromPkgRead (ptr) ?? "";
|
||
}
|
||
public string FamilyName { get { return StringValue (2); } }
|
||
public string FullName { get { return StringValue (3); } }
|
||
public string Name { get { return StringValue (0); } }
|
||
public List<Architecture> ProcessArchitecture
|
||
{
|
||
get
|
||
{
|
||
var list = new List<Architecture> ();
|
||
uint uarch = 0;
|
||
PackageReadHelper.GetManifestIdentityArchitecture (m_hReader, out uarch);
|
||
var t = PackageReadHelper.GetManifestType (m_hReader);
|
||
switch (t)
|
||
{
|
||
case 1:
|
||
switch (uarch)
|
||
{
|
||
case 0x1: list.Add (Architecture.x86); break;
|
||
case 0x2: list.Add (Architecture.x64); break;
|
||
case 0x4: list.Add (Architecture.ARM); break;
|
||
case 0x8: list.Add (Architecture.ARM64); break;
|
||
case 0xE: list.Add (Architecture.Neutral); break;
|
||
}
|
||
break;
|
||
case 2:
|
||
if ((uarch & 0x1) != 0) list.Add (Architecture.x86);
|
||
if ((uarch & 0x2) != 0) list.Add (Architecture.x64);
|
||
if ((uarch & 0x4) != 0) list.Add (Architecture.ARM);
|
||
if ((uarch & 0x8) != 0) list.Add (Architecture.ARM64);
|
||
break;
|
||
}
|
||
return list;
|
||
}
|
||
}
|
||
public string Publisher { get { return StringValue (1); } }
|
||
public string ResourceId { get { return StringValue (4); } }
|
||
public DataUtils.Version Version
|
||
{
|
||
get
|
||
{
|
||
PackageReadHelper.VERSION ver = new PackageReadHelper.VERSION ();
|
||
PackageReadHelper.GetManifestIdentityVersion (m_hReader, out ver);
|
||
return new DataUtils.Version (ver.major, ver.minor, ver.build, ver.revision);
|
||
}
|
||
}
|
||
public override object BuildJSON ()
|
||
{
|
||
return new
|
||
{
|
||
name = Name,
|
||
package_full_name = FullName,
|
||
package_family_name = FamilyName,
|
||
publisher = Publisher,
|
||
resource_id = ResourceId,
|
||
architecture = ProcessArchitecture.Select (e => (int)e).ToList (),
|
||
version = Version.BuildJSON ()
|
||
};
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRProperties: BaseInfoSectWithPRISingle, Info.IProperties
|
||
{
|
||
public MRProperties (ref IntPtr hReader, ManifestReader reader, ref PriReader priBundle, ref bool usePri, ref bool enablePri) : base (ref hReader, reader, ref priBundle, ref usePri, ref enablePri) { }
|
||
protected string StringValue (string attr)
|
||
{
|
||
var ptr = PackageReadHelper.GetManifestPropertiesStringValue (m_hReader, attr);
|
||
return PackageReadHelper.GetStringAndFreeFromPkgRead (ptr) ?? "";
|
||
}
|
||
protected bool BoolValue (string attr, bool defaultValue = false)
|
||
{
|
||
int ret = 0;
|
||
HRESULT hr = PackageReadHelper.GetManifestPropertiesBoolValue (m_hReader, attr, out ret);
|
||
if (hr.Succeeded) return ret != 0;
|
||
else return defaultValue;
|
||
}
|
||
protected string StringResValue (string attr)
|
||
{
|
||
var res = StringValue (attr);
|
||
try
|
||
{
|
||
if (m_usePri && m_enablePri)
|
||
{
|
||
if (PriFileHelper.IsMsResourcePrefix (res))
|
||
return m_pri.Value.String (res) ?? res;
|
||
}
|
||
}
|
||
catch (Exception) { }
|
||
return res;
|
||
}
|
||
protected string PathResValue (string attr)
|
||
{
|
||
var res = StringValue (attr);
|
||
try
|
||
{
|
||
if (m_usePri && m_enablePri)
|
||
{
|
||
var resvalue = m_pri.Value.String (res);
|
||
if (!string.IsNullOrEmpty (resvalue)) return resvalue;
|
||
else return res;
|
||
}
|
||
}
|
||
catch (Exception) { }
|
||
return res;
|
||
}
|
||
public string Description { get { return StringResValue ("Description"); } }
|
||
public string DisplayName { get { return StringResValue ("DisplayName"); } }
|
||
public bool Framework { get { return BoolValue ("Framework"); } }
|
||
public string Logo { get { return PathResValue ("Logo"); } }
|
||
public string LogoBase64
|
||
{
|
||
get
|
||
{
|
||
var root = m_reader.Value.FileRoot;
|
||
var logopath = Path.Combine (root, Logo);
|
||
var ret = DataUrlHelper.FileToDataUrl (logopath);
|
||
if (!string.IsNullOrWhiteSpace (ret)) return ret;
|
||
logopath = Path.Combine (root, StringValue ("Logo"));
|
||
ret = DataUrlHelper.FileToDataUrl (logopath);
|
||
if (!string.IsNullOrWhiteSpace (ret)) return ret;
|
||
return String.Empty;
|
||
}
|
||
}
|
||
public string Publisher { get { return StringResValue ("PublisherDisplayName"); } }
|
||
public bool ResourcePackage { get { return BoolValue ("ResourcePackage"); } }
|
||
public override object BuildJSON ()
|
||
{
|
||
return new
|
||
{
|
||
display_name = DisplayName,
|
||
description = Description,
|
||
publisher_display_name = Publisher,
|
||
Framework = Framework,
|
||
resource_package = ResourcePackage,
|
||
logo = Logo,
|
||
logo_base64 = LogoBase64
|
||
};
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRApplication: Dictionary<string, string>
|
||
{
|
||
protected Ref<IntPtr> m_hReader = IntPtr.Zero;
|
||
protected Ref<PriReader> m_pri = null;
|
||
protected Ref<bool> m_usePri = false;
|
||
protected Ref<bool> m_enablePri = false;
|
||
protected string m_root = String.Empty;
|
||
public MRApplication (ref IntPtr hReader, ref PriReader priBundle, ref bool usePri, ref bool enablePri, string rootDir) : base (StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
m_hReader = hReader;
|
||
m_pri = priBundle;
|
||
m_usePri = usePri;
|
||
m_enablePri = enablePri;
|
||
m_root = rootDir;
|
||
}
|
||
public MRApplication (ref Ref<IntPtr> m_hReader, ref Ref<PriReader> m_priBundle, ref Ref<bool> m_usePri, ref Ref<bool> m_enablePri, string rootDir)
|
||
{
|
||
this.m_hReader = m_hReader;
|
||
this.m_pri = m_priBundle;
|
||
this.m_usePri = m_usePri;
|
||
this.m_enablePri = m_enablePri;
|
||
this.m_root = rootDir;
|
||
}
|
||
public string UserModelID { get { return this ["AppUserModelID"]; } }
|
||
protected bool EnablePri ()
|
||
{
|
||
if (m_pri == null || m_pri.Value == null) return false;
|
||
if (!m_usePri.Value) return false;
|
||
return m_enablePri.Value;
|
||
}
|
||
protected string StringResValue (string attr)
|
||
{
|
||
try
|
||
{
|
||
var res = this [attr];
|
||
try
|
||
{
|
||
if (m_usePri && m_enablePri)
|
||
{
|
||
if (PriFileHelper.IsMsResourcePrefix (res))
|
||
return m_pri.Value.String (res) ?? res;
|
||
}
|
||
}
|
||
catch (Exception) { }
|
||
return res;
|
||
}
|
||
catch (Exception) { return String.Empty; }
|
||
}
|
||
protected string PriGetRes (string resName)
|
||
{
|
||
if (string.IsNullOrEmpty (resName)) return string.Empty;
|
||
if (m_pri == null || m_pri.Value == null) return string.Empty;
|
||
return m_pri.Value.Resource (resName);
|
||
}
|
||
protected bool IsFilePathKey (string key)
|
||
{
|
||
foreach (var i in ConstData.FilePathItems)
|
||
if ((i?.Trim ()?.ToLower () ?? "") == (key?.Trim ()?.ToLower () ?? ""))
|
||
return true;
|
||
return false;
|
||
}
|
||
public new string this [string key]
|
||
{
|
||
get
|
||
{
|
||
string value = null;
|
||
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;
|
||
base [key] = value;
|
||
}
|
||
if (!EnablePri ()) return value;
|
||
if (PriFileHelper.IsMsResourcePrefix (value))
|
||
{
|
||
string pri = PriGetRes (value);
|
||
return string.IsNullOrEmpty (pri) ? value : pri;
|
||
}
|
||
if (IsFilePathKey (key) && !string.IsNullOrEmpty (value))
|
||
{
|
||
string pri = PriGetRes (value);
|
||
return string.IsNullOrEmpty (pri) ? value : pri;
|
||
}
|
||
return value;
|
||
}
|
||
}
|
||
public string At (string key)
|
||
{
|
||
string value = null;
|
||
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 (PriFileHelper.IsMsResourcePrefix (value))
|
||
{
|
||
string pri = PriGetRes (value);
|
||
if (!string.IsNullOrEmpty (pri))
|
||
return pri;
|
||
}
|
||
return value;
|
||
}
|
||
public string NewAt (string key, bool toPriString)
|
||
{
|
||
string value = null;
|
||
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;
|
||
base [key] = value;
|
||
}
|
||
if (!EnablePri () && toPriString) return value;
|
||
if (PriFileHelper.IsMsResourcePrefix (value))
|
||
{
|
||
string pri = PriGetRes (value);
|
||
return string.IsNullOrEmpty (pri) ? value : pri;
|
||
}
|
||
if (IsFilePathKey (key) && !string.IsNullOrEmpty (value))
|
||
{
|
||
string pri = PriGetRes (value);
|
||
return string.IsNullOrEmpty (pri) ? value : pri;
|
||
}
|
||
return value;
|
||
}
|
||
public string NewAtBase64 (string key)
|
||
{
|
||
string value = NewAt (key, true);
|
||
if (!IsFilePathKey (key) || string.IsNullOrEmpty (value)) return "";
|
||
switch (PackageReadHelper.GetPackageType (m_hReader))
|
||
{
|
||
case 1: // PKGTYPE_APPX
|
||
{
|
||
var root = m_root;
|
||
var logopath = Path.Combine (root, NewAt (key, true));
|
||
var ret = DataUrlHelper.FileToDataUrl (logopath);
|
||
if (!string.IsNullOrWhiteSpace (ret)) return ret;
|
||
logopath = Path.Combine (root, NewAt (key, false));
|
||
ret = DataUrlHelper.FileToDataUrl (logopath);
|
||
if (!string.IsNullOrWhiteSpace (ret)) return ret;
|
||
return String.Empty;
|
||
} break;
|
||
}
|
||
return "";
|
||
}
|
||
public static bool operator == (MRApplication a, MRApplication b)
|
||
{
|
||
if (ReferenceEquals (a, b)) return true;
|
||
if ((object)a == null || (object)b == null) return false;
|
||
return string.Equals (a.UserModelID, b.UserModelID, StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
public static bool operator != (MRApplication a, MRApplication b)
|
||
{
|
||
return !(a == b);
|
||
}
|
||
public override bool Equals (object obj)
|
||
{
|
||
var other = obj as MRApplication;
|
||
if (other == null) return false;
|
||
return this == other;
|
||
}
|
||
public override int GetHashCode ()
|
||
{
|
||
return (UserModelID ?? "").ToLowerInvariant ().GetHashCode ();
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRApplications: BaseInfoSectWithPRISingle, IEnumerable<MRApplication>
|
||
{
|
||
private IntPtr _hList = IntPtr.Zero;
|
||
private List<MRApplication> _apps;
|
||
public MRApplications (
|
||
ref IntPtr hReader,
|
||
ManifestReader reader,
|
||
ref PriReader priBundle,
|
||
ref bool usePri,
|
||
ref bool enablePri)
|
||
: base (ref hReader, reader, ref priBundle, ref usePri, ref enablePri)
|
||
{
|
||
if (IsValid)
|
||
{
|
||
_hList = PackageReadHelper.GetManifestApplications (m_hReader.Value);
|
||
}
|
||
}
|
||
#region Dispose
|
||
public new void Dispose ()
|
||
{
|
||
if (_hList != IntPtr.Zero)
|
||
{
|
||
PackageReadHelper.DestroyManifestApplications (_hList);
|
||
_hList = IntPtr.Zero;
|
||
}
|
||
base.Dispose ();
|
||
}
|
||
~MRApplications ()
|
||
{
|
||
Dispose ();
|
||
}
|
||
#endregion
|
||
#region 属性:Applications
|
||
public List<MRApplication> Applications
|
||
{
|
||
get
|
||
{
|
||
if (_apps == null)
|
||
_apps = ReadApplications ();
|
||
return _apps;
|
||
}
|
||
}
|
||
#endregion
|
||
#region 索引器
|
||
public MRApplication this [int index]
|
||
{
|
||
get { return Applications [index]; }
|
||
}
|
||
public MRApplication this [string key]
|
||
{
|
||
get
|
||
{
|
||
foreach (var app in Applications)
|
||
{
|
||
if (string.Equals (app.UserModelID, key, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return app;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
#endregion
|
||
#region IEnumerable
|
||
public IEnumerator<MRApplication> GetEnumerator ()
|
||
{
|
||
return Applications.GetEnumerator ();
|
||
}
|
||
IEnumerator IEnumerable.GetEnumerator ()
|
||
{
|
||
return GetEnumerator ();
|
||
}
|
||
#endregion
|
||
#region 内部解析逻辑(核心等价 C++ get)
|
||
private List<MRApplication> ReadApplications ()
|
||
{
|
||
var list = new List<MRApplication> ();
|
||
if (_hList == IntPtr.Zero) return list;
|
||
IntPtr hMapList = PackageReadHelper.ApplicationsToMap (_hList);
|
||
if (hMapList == IntPtr.Zero) return list;
|
||
try
|
||
{
|
||
uint count = (uint)Marshal.ReadInt32 (hMapList);
|
||
int baseOffset = Marshal.SizeOf (typeof (uint));
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
IntPtr hKeyValues = Marshal.ReadIntPtr (hMapList, baseOffset + i * IntPtr.Size);
|
||
if (hKeyValues == IntPtr.Zero) continue;
|
||
list.Add (ReadSingleApplication (hKeyValues));
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyApplicationsMap (hMapList);
|
||
}
|
||
return list;
|
||
}
|
||
private MRApplication ReadSingleApplication (IntPtr hKeyValues)
|
||
{
|
||
var app = new MRApplication (ref m_hReader, ref m_pri, ref m_usePri, ref m_enablePri, m_reader.Value.FileRoot);
|
||
int pairCount = Marshal.ReadInt32 (hKeyValues);
|
||
IntPtr arrayBase = IntPtr.Add (hKeyValues, sizeof (uint));
|
||
for (int i = 0; i < pairCount; i++)
|
||
{
|
||
IntPtr pPairPtr = Marshal.ReadIntPtr (arrayBase, i * IntPtr.Size);
|
||
if (pPairPtr == IntPtr.Zero) continue;
|
||
PackageReadHelper.PAIR_PVOID pair =
|
||
(PackageReadHelper.PAIR_PVOID)Marshal.PtrToStructure (pPairPtr, typeof (PackageReadHelper.PAIR_PVOID));
|
||
if (pair.lpKey == IntPtr.Zero) continue;
|
||
string key = Marshal.PtrToStringUni (pair.lpKey);
|
||
if (string.IsNullOrEmpty (key)) continue;
|
||
string value = pair.lpValue != IntPtr.Zero
|
||
? Marshal.PtrToStringUni (pair.lpValue)
|
||
: string.Empty;
|
||
app.Add (key, value);
|
||
}
|
||
return app;
|
||
}
|
||
#endregion
|
||
public override object BuildJSON ()
|
||
{
|
||
using (var apps = this)
|
||
{
|
||
return apps.Select (app => {
|
||
var dict = new Dictionary<string, object> (StringComparer.OrdinalIgnoreCase);
|
||
foreach (var kv in app)
|
||
{
|
||
dict [kv.Key] = kv.Value;
|
||
if (ConstData.IsFilePathKey (kv.Key))
|
||
{
|
||
dict [(kv.Key?.Trim () ?? "") + "_Base64"] = app.NewAtBase64 (kv.Key);
|
||
}
|
||
else
|
||
{
|
||
dict [kv.Key] = app.NewAt (kv.Key, m_usePri.Value && m_enablePri.Value) ?? kv.Value;
|
||
}
|
||
}
|
||
dict ["AppUserModelID"] = app.UserModelID;
|
||
return dict;
|
||
}).ToList ();
|
||
}
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRCapabilities: BaseInfoSection, ICapabilities
|
||
{
|
||
public MRCapabilities (ref IntPtr hReader) : base (ref hReader) { }
|
||
public List<string> Capabilities
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<string> ();
|
||
if (!IsValid) return ret;
|
||
IntPtr hList = PackageReadHelper.GetManifestCapabilitiesList (m_hReader.Value);
|
||
if (hList == IntPtr.Zero) return ret;
|
||
try
|
||
{
|
||
uint count = (uint)Marshal.ReadInt32 (hList);
|
||
int baseOffset = Marshal.SizeOf (typeof (uint)); // dwSize 后
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
IntPtr pStr = Marshal.ReadIntPtr (hList, baseOffset + i * IntPtr.Size);
|
||
if (pStr == IntPtr.Zero) continue;
|
||
string s = Marshal.PtrToStringUni (pStr);
|
||
if (!string.IsNullOrEmpty (s)) ret.Add (s);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyWStringList (hList);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public List<string> DeviceCapabilities
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<string> ();
|
||
if (!IsValid) return ret;
|
||
IntPtr hList = PackageReadHelper.GetManifestDeviceCapabilitiesList (m_hReader.Value);
|
||
if (hList == IntPtr.Zero) return ret;
|
||
try
|
||
{
|
||
uint count = (uint)Marshal.ReadInt32 (hList);
|
||
int baseOffset = Marshal.SizeOf (typeof (uint)); // dwSize 后
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
IntPtr pStr = Marshal.ReadIntPtr (hList, baseOffset + i * IntPtr.Size);
|
||
if (pStr == IntPtr.Zero) continue;
|
||
string s = Marshal.PtrToStringUni (pStr);
|
||
if (!string.IsNullOrEmpty (s)) ret.Add (s);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyWStringList (hList);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public List<string> CapabilityDisplayNames
|
||
{
|
||
get
|
||
{
|
||
var caps = Capabilities;
|
||
var dev = DeviceCapabilities;
|
||
var ret = new List<string> ();
|
||
foreach (var c in caps)
|
||
{
|
||
var capname = CacheData.GetPackageCapabilityDisplayName (c);
|
||
if (String.IsNullOrWhiteSpace (capname)) ret.Add (c);
|
||
else ret.Add (capname);
|
||
}
|
||
foreach (var d in dev)
|
||
{
|
||
var dcapname = CacheData.GetPackageCapabilityDisplayName (d);
|
||
if (!String.IsNullOrWhiteSpace (dcapname)) ret.Add (dcapname);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public override object BuildJSON ()
|
||
{
|
||
return new
|
||
{
|
||
capabilities_name = Capabilities,
|
||
device_capabilities = DeviceCapabilities,
|
||
scales = CapabilityDisplayNames
|
||
};
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRDependencies: BaseInfoSection, IEnumerable<DependencyInfo>
|
||
{
|
||
public MRDependencies (ref IntPtr hReader) : base (ref hReader) { }
|
||
public List<DependencyInfo> Dependencies
|
||
{
|
||
get
|
||
{
|
||
var output = new List<DependencyInfo> ();
|
||
if (!IsValid) return output;
|
||
IntPtr hList = PackageReadHelper.GetManifestDependencesInfoList (m_hReader);
|
||
if (hList == IntPtr.Zero) return output;
|
||
try
|
||
{
|
||
var deps = PackageReadHelper.ReadDependencyInfoList (hList);
|
||
foreach (var dep in deps)
|
||
{
|
||
// dep.lpName / dep.lpPublisher 是 IntPtr
|
||
string name = Marshal.PtrToStringUni (dep.lpName) ?? "";
|
||
string publisher = Marshal.PtrToStringUni (dep.lpPublisher) ?? "";
|
||
// VERSION 直接映射为 System.Version
|
||
var ver = new DataUtils.Version (dep.verMin.major, dep.verMin.minor, dep.verMin.build, dep.verMin.revision);
|
||
output.Add (new DependencyInfo (name, publisher, ver));
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyDependencesInfoList (hList);
|
||
}
|
||
return output;
|
||
}
|
||
}
|
||
public DependencyInfo this [int index] => Dependencies [index];
|
||
public IEnumerator<DependencyInfo> GetEnumerator ()
|
||
{
|
||
return Dependencies.GetEnumerator ();
|
||
}
|
||
IEnumerator IEnumerable.GetEnumerator ()
|
||
{
|
||
return GetEnumerator ();
|
||
}
|
||
public override object BuildJSON ()
|
||
{
|
||
return this.Select (d => new {
|
||
name = d.Name,
|
||
publisher = d.Publisher,
|
||
vermin = d.Version.BuildJSON ()
|
||
}).ToList ();
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class MRResources: BaseInfoSection, IResources
|
||
{
|
||
public MRResources (ref IntPtr hReader) : base (ref hReader) { }
|
||
public List<DXFeatureLevel> DXFeatures
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<DXFeatureLevel> ();
|
||
try
|
||
{
|
||
var dw = PackageReadHelper.GetManifestResourcesDxFeatureLevels (m_hReader);
|
||
if ((dw & 0x1) != 0) ret.Add (DXFeatureLevel.Level9);
|
||
if ((dw & 0x2) != 0) ret.Add (DXFeatureLevel.Level10);
|
||
if ((dw & 0x4) != 0) ret.Add (DXFeatureLevel.Level11);
|
||
if ((dw & 0x8) != 0) ret.Add (DXFeatureLevel.Level12);
|
||
}
|
||
catch (Exception) { }
|
||
return ret;
|
||
}
|
||
}
|
||
public List<string> Languages
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<string> ();
|
||
if (!IsValid) return ret;
|
||
IntPtr hList = PackageReadHelper.GetManifestResourcesLanguages (m_hReader.Value);
|
||
if (hList == IntPtr.Zero) return ret;
|
||
try
|
||
{
|
||
ret = PackageReadHelper.ReadWStringList (hList).ToList ();
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyWStringList (hList);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public List<int> Languages_LCID
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<int> ();
|
||
if (!IsValid) return ret;
|
||
IntPtr hList = PackageReadHelper.GetManifestResourcesLanguagesToLcid (m_hReader.Value);
|
||
if (hList == IntPtr.Zero) return ret;
|
||
try
|
||
{
|
||
ret = PackageReadHelper.ReadLcidList (hList).ToList ();
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyResourcesLanguagesLcidList (hList);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public List<int> Scales
|
||
{
|
||
get
|
||
{
|
||
var ret = new List<int> ();
|
||
if (!IsValid) return ret;
|
||
IntPtr hList = PackageReadHelper.GetManifestResourcesLanguagesToLcid (m_hReader.Value);
|
||
if (hList == IntPtr.Zero) return ret;
|
||
try
|
||
{
|
||
ret = PackageReadHelper.ReadUInt32List (hList).Select (e => (int)e).ToList ();
|
||
}
|
||
finally
|
||
{
|
||
PackageReadHelper.DestroyUInt32List (hList);
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
public override object BuildJSON ()
|
||
{
|
||
return new
|
||
{
|
||
dx_feature_levels = DXFeatures.Select (e => (int)e).ToList (),
|
||
languages = Languages,
|
||
scales = Scales
|
||
};
|
||
}
|
||
}
|
||
public class MRPrerequisites: BaseInfoSection, IPrerequisites
|
||
{
|
||
public MRPrerequisites (ref IntPtr hReader) : base (ref hReader) { }
|
||
protected DataUtils.Version GetVersion (string name)
|
||
{
|
||
PackageReadHelper.VERSION ver;
|
||
bool res = PackageReadHelper.GetManifestPrerequisite (m_hReader, name, out ver);
|
||
if (res) return new DataUtils.Version (ver.major, ver.minor, ver.build, ver.revision);
|
||
else return new DataUtils.Version ();
|
||
}
|
||
protected string GetVersionDescription (string name)
|
||
{
|
||
var ptr = PackageReadHelper.GetManifestPrerequistieSystemVersionName (m_hReader, name);
|
||
return PackageReadHelper.GetStringAndFreeFromPkgRead (ptr) ?? "";
|
||
}
|
||
public string OSMaxVersionDescription { get { return GetVersionDescription ("OSMaxVersionTested"); } }
|
||
public DataUtils.Version OSMaxVersionTested { get { return GetVersion ("OSMaxVersionTested"); } }
|
||
public DataUtils.Version OSMinVersion { get { return GetVersion ("OSMinVersion"); } }
|
||
public string OSMinVersionDescription { get { return GetVersionDescription ("OSMinVersion"); } }
|
||
public override object BuildJSON ()
|
||
{
|
||
return new
|
||
{
|
||
os_min_version = OSMinVersion.BuildJSON (),
|
||
os_min_version_description = OSMinVersionDescription,
|
||
os_max_version_tested = OSMaxVersionTested.BuildJSON (),
|
||
os_max_version_tested_description = OSMaxVersionDescription
|
||
};
|
||
}
|
||
}
|
||
[ComVisible (true)]
|
||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||
public class ManifestReader: IDisposable
|
||
{
|
||
private IntPtr m_hReader = PackageReadHelper.CreateManifestReader ();
|
||
private string m_filePath = string.Empty;
|
||
private bool m_usePRI = false;
|
||
private bool m_enablePRI = false;
|
||
private PriReader m_pri = null;
|
||
public PriReader PriFile => m_pri;
|
||
public IntPtr Instance => m_hReader;
|
||
public string FileRoot{ get { return Path.GetDirectoryName (m_filePath); } }
|
||
private void InitPri ()
|
||
{
|
||
m_pri?.Dispose ();
|
||
if (!m_usePRI) return;
|
||
#region Get PRI IStream
|
||
switch (Type)
|
||
{
|
||
case PackageType.Appx:
|
||
{
|
||
var pripath = Path.Combine (FileRoot, "resources.pri");
|
||
m_pri = new PriReader (pripath);
|
||
}
|
||
break;
|
||
}
|
||
#endregion
|
||
return;
|
||
}
|
||
public PackageType Type
|
||
{
|
||
get
|
||
{
|
||
var value = PackageReadHelper.GetManifestType (m_hReader);
|
||
switch (value)
|
||
{
|
||
case 0: return PackageType.Unknown;
|
||
case 1: return PackageType.Appx;
|
||
case 2: return PackageType.Bundle;
|
||
}
|
||
return PackageType.Unknown;
|
||
}
|
||
}
|
||
public PackageRole Role
|
||
{
|
||
get
|
||
{
|
||
var value = PackageReadHelper.GetManifestRole (m_hReader);
|
||
switch (value)
|
||
{
|
||
case 0: return PackageRole.Unknown;
|
||
case 1: return PackageRole.Application;
|
||
case 2: return PackageRole.Framework;
|
||
case 3: return PackageRole.Resource;
|
||
}
|
||
return PackageRole.Unknown;
|
||
}
|
||
}
|
||
public bool IsApplicationManifest { get { return Role == PackageRole.Application; } }
|
||
public bool IsValid { get { return m_hReader != IntPtr.Zero && Type != PackageType.Unknown; } }
|
||
/// <summary>使用 PRI,启用后会预先处理 PRI 文件。</summary>
|
||
public bool UsePri { get { return m_usePRI; } set { m_usePRI = value; InitPri (); } }
|
||
/// <summary>允许 PRI,启用后会返回读取的 PRI 文件结果,需保证 UsePri 开启。</summary>
|
||
public bool EnablePri { get { return m_enablePRI; } set { m_enablePRI = value; } }
|
||
public MRIdentity Identity { get { return new MRIdentity (ref m_hReader); } }
|
||
public MRProperties Properties { get { return new MRProperties (ref m_hReader, this, ref m_pri, ref m_usePRI, ref m_enablePRI); } }
|
||
public MRPrerequisites Prerequisites { get { return new MRPrerequisites (ref m_hReader); } }
|
||
public MRResources Resources { get { return new MRResources (ref m_hReader); } }
|
||
public MRApplications Applications { get { return new MRApplications (ref m_hReader, this, ref m_pri, ref m_usePRI, ref m_enablePRI); } }
|
||
public MRCapabilities Capabilities { get { return new MRCapabilities (ref m_hReader); } }
|
||
public MRDependencies Dependencies { get { return new MRDependencies (ref m_hReader); } }
|
||
public void Dispose ()
|
||
{
|
||
var lastvalue = m_usePRI;
|
||
m_usePRI = false;
|
||
InitPri ();
|
||
m_usePRI = lastvalue;
|
||
if (m_hReader != IntPtr.Zero)
|
||
{
|
||
PackageReadHelper.DestroyManifestReader (m_hReader);
|
||
m_hReader = IntPtr.Zero;
|
||
}
|
||
}
|
||
~ManifestReader () { Dispose (); }
|
||
public string FilePath
|
||
{
|
||
get
|
||
{
|
||
return m_filePath;
|
||
}
|
||
set
|
||
{
|
||
PackageReadHelper.LoadManifestFromFile (m_hReader, m_filePath = value);
|
||
}
|
||
}
|
||
public ManifestReader (string filePath) { FilePath = filePath; }
|
||
public ManifestReader () { }
|
||
public string JSONText { get { return BuildJsonText (); } }
|
||
public string BuildJsonText ()
|
||
{
|
||
var obj = BuildJsonObject ();
|
||
return Newtonsoft.Json.JsonConvert.SerializeObject (
|
||
obj,
|
||
Newtonsoft.Json.Formatting.Indented
|
||
);
|
||
}
|
||
public void BuildJsonTextAsync (object callback)
|
||
{
|
||
if (callback == null) return;
|
||
Thread thread = new Thread (() => {
|
||
string json = string.Empty;
|
||
try
|
||
{
|
||
json = BuildJsonText ();
|
||
}
|
||
catch
|
||
{
|
||
json = string.Empty;
|
||
}
|
||
JSHelper.CallJS (callback, json);
|
||
});
|
||
thread.SetApartmentState (ApartmentState.MTA);
|
||
thread.IsBackground = true;
|
||
thread.Start ();
|
||
}
|
||
private object BuildJsonObject ()
|
||
{
|
||
return new
|
||
{
|
||
valid = IsValid,
|
||
filepath = FilePath,
|
||
type = (int)Type,
|
||
role = (int)Role,
|
||
identity = Identity.BuildJSON (),
|
||
properties = Properties.BuildJSON (),
|
||
prerequisites = Prerequisites.BuildJSON (),
|
||
resources = Resources.BuildJSON (),
|
||
capabilities = Capabilities.BuildJSON (),
|
||
dependencies = Dependencies.BuildJSON (),
|
||
applications = Applications.BuildJSON ()
|
||
};
|
||
}
|
||
public static bool AddApplicationItem (string itemName) => PackageReadHelper.AddPackageApplicationItemGetName (itemName);
|
||
public static bool RemoveApplicationItem (string itemName) => PackageReadHelper.RemovePackageApplicationItemGetName (itemName);
|
||
}
|
||
}
|