microsoft / WindowsAppSDK

The Windows App SDK empowers all Windows desktop apps with modern Windows UI, APIs, and platform features, including back-compat support, shipped via NuGet.
https://docs.microsoft.com/windows/apps/windows-app-sdk/
MIT License
3.84k stars 320 forks source link

Support Package.DisplayName for Win32 desktop apps? #81

Open harvinders opened 4 years ago

harvinders commented 4 years ago

@marb2000 Currently we are able to find the dependencies for the current WinUI 3 Desktop app, and iterate through the optional packages. However, for whatever reason, we are unable to access many properties of the Package. Properties like DisplayName, Description etc are returned as empty string.

Document here clearly says that it is not available for non UWP apps.

This property is only supported for UWP app. If you call this property on an instance of a package that was created by any mechanism other than Package.Current, this property returns an empty string ("").

This makes it difficult for us to display the plugin details that are installed through the Optional Package in the Main applications settings page.

JaiganeshKumaran commented 4 years ago

Windows.ApplicationModel.Package has nothing to do with WinUI. It's a WinRT API for Packaged apps.

DrusTheAxe commented 4 years ago

You're right, this is an appmodel issue not WinUI.

Package.PublisherDisplayName not always available is one of the pain points we've incrementally (though not always completely) improved over the years. Expanding the issue to the broader Package API...

What would you like to see in this space?

Below is an initial idea to generate discussion (using C# syntax for discussion purposes). This is inspired by the current Windows.ApplicationModel.Package but improved. And of course would be fully functional on all releases supported by Project Reunion. There are many ideas and questions posed here. I've tried to call out most of them but all is open to debate. What would you like to see regarding functionliity, syntax and everything in between?

The W.AM.Package object provides read access to package-constant data (e.g. Name), runtime information (e.g. Status) and some manipulation methods (SetInUseAsync, StageContentGroups, etc). This is a rather sprawling collection which can be unwieldy given the broad but flat API surface. This can be factored in more logical functional groupings:

1. Construction

Instantiate a Package object. What forms interest you?

From a W.AM.Package object and the current process' package identity (if it has one) are obvious starting points.

Are there other forms desired? This topic overlaps with the 9.Enumeration topic (below).

2. Localizable Properties

Package properties that are localizable e.g. PublisherDisplayName.

Logo is an interesting one. Package.Logo returns a Uri e.g. file://... Is this useful as a URI? Annoying? Would a form returning a filename as a string be interesting?

GetLogoAsRandomAccessStreamReference() is equivalent to return new RandomAccessStreamReference(package.GetLogo()). This provides a way for a process that can enumerate packages but not access their content to still have access to their logo. Are there alternate forms that would be useful e.g. GetLogoBytes() equivalent to OpenFile(package.LogoFilename).ReadAllBytes()? Logo must be a gif, jpg or png. Would LoadImage() returning a Windows.UI.Xaml.Controls.Image be useful?

3. Location Properties

Packages are installed in a location and may have supplemental or alternate locations. These paths can be accessed as a string or StorageFolder.

There are 6 possible permutations for a package's location, but .EffectivePath/.EffectiveLocation is generally what people want. Is this confusing? Would a .Path (and .Location) property as simple aliases for .EFfectivePath/.EffectiveLocation be useful?

4. Identity Properties

Package identity has multiple parts and forms.

There's a set of identity conversion APIs only available via appmodel.h. We can make that functionality accessible here via static methods. These come in 3 flavors: Format, Parse and Verify.

Would TryParse be useful in addition to Parse (or instead of)? Or should Parse provide 'Try' functionality e.g. return null if parsing fails?

5. Package Graph

A process with package identity has a package graph. This is accessible today via Package.Current and Package.Dependencies, as the moral equivalents of PackageGraph[0] and PackageGraph[1+].

The .Dependencies property has no filtering, unlike GetPackageInfo() and GetCurrentPackageInfo() which let the caller ask for a subset of the package graph, e.g. "Only return Optional packages" via GetPackageInfo(...,PACKAGE_FILTER_OPTIONAL,...). It's also difficult to impossible to query certain variants, e.g. "Return all packages that depend on this Framework package". Overloading a Find method with all the variations would be unwieldly so we can make an options object supporting the various filter conditions.

FindRelatedPackages() is equivalent to W.AM.Package.Dependencies. It's also equivalent to FindRelatedPackages(new FindRelatedPackageOptions() { Dependencies = true; Framework = true; Resource= true; Optional = true; });

FindRelatedPackageOptions.Dependents would be the opposite direction of .Dependencies -- find all packages that depend on this one, vs find all packages that this one depends on.

6. Install Properties

W.AM.Package.InstalledDate returns time when a package was most recently registered for a user, for example:

Monday> pkgmgr->AddPackageAsync("file://q:/kittenz.msix")
Tuesday> pkgmgr->RegisterPackageByFamilyNameAsync("kittenz_1234567890abc")
Wednesday> pkgmgr->RegisterPackageByFamilyNameAsync("kittenz_1234567890abc")
Thursday> pkgmgr->RegisterPackageByFamilyNameAsync("kittenz_1234567890abc")
Friday> pkgmgr->FindPackageForUser(null, "kittenz_1.2.3.4_neutral__1234567890abc").InstalledDate == Thursday

.WhenFirstRegisteredForUser is the moral equivalent of a file's Created timestamp. In the above example it would return Monday.

.WhenLastRegisteredForUser is the moral equivalent of a file's LastModified timestamp. In the above example it would return Tuesday.

7. Application Properties

Main and Optional packages can contain 0-100 Applications, accessible via GetAppListEntries[Async]() returning AppListEntry, AppInfo and AppDisplayInfo objects. This issue is focused on packages, but if there's interest we can have similar issue to discuss those.

8. ContentGroup Properties

Do you use ContentGroups? Does the API fit your needs well? Are there pains or improvements you'd like to see?

9. Enumeration

Package enumeration is typically handled via Windows.Management.Deployment.PackageManager.FindPackage*() methods.

GetPackagesByPackageFamily() and FindPackagesByPackageFamily() in appmodel.h also provide a subset of package enumeration functionality.

FindPackageOptions acts as a filter. FindPackages(options) would only return packages that match the options specified (bool=true, string!=null, etc).

FindPackage(options) would fail if >1 package is matched. There are cases that can only possibly match 0-1 package, e.g. new FindPackageOptions(){ IsMain=true; User=someuser; } can only match 0-1 package because a user can only have 0-1 Main packages registered at a time. In these cases FindPackage(options) == FindPackages(options)[0] but could be implemented more efficiently, plus FindPackage failing if 2+ packages match would prevent accidental misuse e.g. FindPackages(new FindPackageOptions(){ .IsMain=true; }) or FindPackages(new FindPackageOptions(){ .User=someuser; .IsFramework=true; }) which can match 0-N packages.

Are there functional gaps in the current enumeration APIs?

Are there pain points we can solve with better APIs? Are these functional or syntactical pains?

PackageManager has a rich (and correspondingly complex) set of Find methods. Is this desirable?

Is the FindPackages(options) more appealing than the (currently) 12 overloaded Find*() methods? IS a mix of both desirable? Undesirable? What would you find most useful?

X. Miscellaneous

W.AM.Package has several members that don't neatly fall into the previous classifications. Should these live here? Do some fit better in one of the above groupings? In a new grouping?

IsMain/Framework/Optional*/Bundle are constants for a package.

IsDevelopmentMode, IsStub and Status are runtime values that can change over time.

CheckUpdateAvailabilityAsync, SetInUseAsync and VerifyContentIntegrityAsync are install-related but didn't feel like install info. Perhaps they're best in PackageInstallInfo if PackageInstallInfo had a better name?

Z. Other?

Is there additional functionality you'd like to see?

Packages have a Package SID for their AppContainer but W.AM.Package provides no access to this. Would this be useful? What form(s) should that take? String? byte[]? Other?

Packages have several footprint files (e.g. appxmanifest.xml). Thye're usually not needed, but when they are developers need must resort to games like hard-coded string concatenation etc e.g. Package.InstalledPath + "\\appxmanifest.xml". Would APIs be useful e,g,

string manifest = Package.FootprintFiles.AppxManifestFilename;
StorageFile manifestFile = Package.FootprintFiles.AppxManifestAsStorageFile;

string blockmap = Package.FootprintFiles.BlockmapFilename;
StorageFile blockmapFile = Package.FootprintFiles.BlockmapAsStorageFile;

string signature = Package.FootprintFiles.SignatureFilename;
StorageFile signatureFile = Package.FootprintFiles.SignatureAsStorageFile;

Microsoft-internal task 27242008

Microsoft.ApplicationModel.Package API v0 :-)

namespace Microsoft.ApplicationModel
{
class FindPackageOptions
{
    public Windows.System.User User;
    public string PackageFullName;
    public string PackageFamilyName;
    public string PackageName;
    public string PackagePublisher;
    public string PackagePublisherId;
    public bool IsMain;
    public bool IsFramework;
    public bool IsResource;
    public bool IsOptional;
    public bool IsOptionalInRelatedSet;
    public bool IsOptionalNotInRelatedSet;
    public bool IsBundle;
    public bool IsProvisioned;
}

class Package
{
    /*1: Construction*/
    public Package(Windows.ApplicationModel.Package package);   // Equivalent to a W.AM.Package object
    public Package(string packageFullName);                     // Equivalent to W.M.D.PackageManager.FindPackage(string)
    static public Package GetCurrent();                         // Equivalent to W.AM.Package.Current

    /*2:Localizable Properties*/
    public PackageDisplayInfo DisplayInfo { get { return new PackageDisplayInfo(this); }{ get; } }

    /*3:Location Properties*/
    public PackageLocation Location { get { return new PackageLocation(this); } }

    /*4:Identity Properties*/
    public PackageIdentity Identity { get { return new PackageIdentity(this); } }

    /*5:Package Graph*/
    public PackageGraph PackageGraph { get { return new PackageGraph(this); } }

    /*6:Install Properties*/
    public PackageInstallInfo Install { get { return new PackageInstallInfo(this); } }

    /*7:Application Properties*/
    public PackageApplications Applications { get { return new PackageApplicationInfo(this); } }

    /*8:ContentGroup Properties*/
    public PackageContentGroups ContentGroups { get { return new PackageContentGroups(this); } }

    /*9:Enumeration*/
    //
    // Equivalent to W.M.D.PackageManager.FindPackage
    public static Package FindPackage(string packageFullName);  // Find...(string)
    //
    // Equivalent to W.M.D.PackageManager.FindPackageForUser
    public static Package FindPackageForUser(User user, string packageFullName);   // Find...(string, string)
    //
    // Equivalent to W.M.D.PackageManager.FindPackages
    public static IList<Package> FindPackages();                                            // Find...()
    public static IList<Package> FindPackages(string packagFamilyName);                     // Find...(string)
    public static IList<Package> FindPackages(string packageName, string packagePublisher); // Find...(string, string)
    //
    // Equivalent to W.M.D.PackageManager.FindPackagesForUser
    public static IList<Package> FindPackagesForUser(User user);                                               // Find...(string)
    public static IList<Package> FindPackagesForUser(User user, string packagFamilyName);                      // Find...(string, string)
    public static IList<Package> FindPackagesForUser(User user, string packageName, string packagePublisher);  // Find...(string, string, string)
    //
    // Equivalent to W.M.D.PackageManager.FindPackagesForUserWithPackageTypes
    public static IList<Package> FindPackagesForUserWithPackageTypes(User user, PackageTypes types);                                               // Find...(string, PackageTypes)
    public static IList<Package> FindPackagesForUserWithPackageTypes(User user, string packagFamilyName, PackageTypes types);                      // Find...(string, string, PackageTypes)
    public static IList<Package> FindPackagesForUserWithPackageTypes(User user, string packageName, string packagePublisher, PackageTypes types);  // Find...(string, string, string, PackageTypes)
    //
    // Equivalent to W.M.D.PackageManager.FindProvisionedPackages()
    public static IList<Package> FindProvisionedPackages();
    //
    // Or....just 1 method
    public static Package FindPackage(FindPackageOptions options);
    public static IList<Package> FindPackages(FindPackageOptions options);

    /*X:Miscellaneous*/
    public Windows.Management.Deployment.PackageTypes PackagTypes { get; }
    public bool IsMain { get; }
    public bool IsFramework { get; }
    public bool IsResource { get; }
    public bool IsOptional { get; }
    public bool IsOptionalInRelatedSet { get; }
    public bool IsBundle { get; }
    public bool IsDevelopmentMode { get; }
    public bool IsStub { get; }
    public Windows.ApplicationModel.PackageSignatureKind SignatureKind { get; }
    public Windows.ApplicationModel.PackageStatus Status { get; }
    public IAsyncOperation<PackageUpdateAvailabilityResult> CheckUpdateAvailabilityAsync()
    public IAsyncOperation<bool SetInUseAsync(bool inUse);
    public IAsyncOperation<bool> VerifyContentIntegrityAsync();
}

class PackageDisplayInfo
{
    public PackageDisplayInfo(Microsoft.ApplicationModel.Package package);

    public string DisplayName { get; }
    public string PublisherDisplayName { get; }
    public string Description { get; }

    public string LogoFilename { get; }
    public Uri LogoUri { get; }
    public RandomAccessStreamReference GetLogoStream(Size size);
    // LogoImage() returning the image as byte[]? Image? ???
}

class PackageLocation
{
    public PackageLocation(Microsoft.ApplicationModel.Package package);

    public string Path { get { return EffectivePath; } }                    // Alias for .EffectivePath
    public StorageFolder Location { get { return Effective Location; } }    // Alias for .EffectiveLocation

    public string InstalledPath
    public string MutablePath
    public string EffectivePath
    public string EffectiveExternalPath
    public string UserEffectiveExternalPath
    public string MachineEffectiveExternalPath

    public StorageFolder InstalledLocation
    public StorageFolder MutableLocation
    public StorageFolder EffectiveLocation
    public StorageFolder EffectiveExternalLocation
    public StorageFolder UserEffectiveExternalLocation
    public StorageFolder MachineEffectiveExternalLocation
}

class PackageFamilyNameTuple
{
    string Name;
    string PublisherId;
}

class PackageIdentity
{
    public PackageIdentity(Microsoft.ApplicationModel.Package package);

    public string Name { get; }
    public PackageVersion Version { get; }
    public ProcessorArchtiecture Architectue { get; }
    public string ResouceId { get; }
    public string Publisher { get; }
    public string PublisherId { get; }

    public string PackageFullName { get; }
    public string PackageFamilyName { get; }

    // Equivalent to PackageFullNameFromId() in appmodel.h
    public static string FormatPackageFullName(string packagename, PackageVersion version, ProcessorArchitecture architecture, string resourceId, string publisher);
    public static string FormatPackageFullNameGivenPublisherId(string packagename, PackageVersion version, ProcessorArchitecture architecture, string resourceId, string publisherId);

    // Equivalent to PackageIdFromFullName() in appmodel.h
    public static PackageIdentity ParsePackageFullName(string packageFullName);

    // Equivalent to PackageFamilyNameFromFullName() in appmodel.h
    public static string FormatPackageFamilyName(string packageFullName);

    // Equivalent to PackageFamilyNameFromId() in appmodel.h
    public static string FormatPackageFamilyName(string packagename, string publisher);
    public static string FormatPackageFamilyNameGivenPublisherId(string packagename, string publisherId);

    // Equivalent ot PackageNameAndPublisherIdFromFamilyName() in appmodel.h
    public static PackageFamilyNameTuple ParsePackageFamilyName(string packageFamilyName);

    // Equivalent to VerifyPackageId() in appmodel.h
    public static bool VerifyPackageId(string packagename, PackageVersion version, ProcessorArchitecture architecture, string resourceId, string publisher);
    public static bool VerifyPackageIdGivenPublisherId(string packagename, PackageVersion version, ProcessorArchitecture architecture, string resourceId, string publisherId);

    // Equivalent to VerifyPackageFullName() in appmodel.h
    public static bool VerifyPackageFullName(string packageFullName);

    // Equivalent to VerifyFamilyName() in appmodel.h
    public static bool VerifyPackageFamilyName(string packageFullName);

}

class FindRelatedPackageOptions
{
    public FindRelatedPackageOptions();

    public bool Dependencies;
    public bool Dependents;
    public bool Framework;
    public bool Resource;
    public bool Optional;
    public bool OptionalInRelatedSet;
    public bool OptionalNotInRelatedSet;
    public string PackageFamilyName;
}

class PackageGraph
{
    public PackageGraph(Microsoft.ApplicationModel.Package package);

    public IList<Package> FindRelatedPackages();    // Equivalent to W.AM.Package.Dependencies
    public IList<Package> FindRelatedPackages(FindRelatedPackageOptions options);
}

class PackageInstallInfo
{
    public PackageInstallInfo(Microsoft.ApplicationModel.Package package);

    public DateTime WhenFirstRegisteredForUser { get; }
    public DateTime WhenLastRegisteredForUser { get; }  // Eqvuivalent to W.AM.Package.InstalledDate

    public AppInstallerInfo GetAppInstallerInfo();      // Equivalent to W.AM.Package.AppInstallerInfo
}

class PackageApplications
{
    public PackageApplications(Microsoft.ApplicationModel.Package package);

    public IList<AppListEntry> GetAppListEntries();
    public IAsyncOperation<IList<AppListEntry>> GetAppListEntriesAsync();
}

class PackageContentGroups
{
    public PackageContentGroups(Microsoft.ApplicationModel.Package package);

    public IAsyncOperation<PackageContentGroup> GetContentGroupAsync(string name);
    public IAsyncOperation<IList<PackageContentGroup>> GetContentGroupsAsync();

    public IAsyncOperation<IList<PackageContentGroup>> StageContentGroups()
}
}
harvinders commented 4 years ago

@DrusTheAxe It would be great if we can support custom properties for optional packages or any package like it is currently support for app extensions.

App extension properties are a custom metadata field provided by the app extension developer in their app manifest. App extension hosts can read this metadata when examining app extensions without having to load any content from the app extension.

This would also allow optional packages to be queried before loading the code from within it.

DrusTheAxe commented 4 years ago

That's an interesting idea. What sorts of data are you thinking of? Do you have specific scenarios in mind?

AppxManifest.xml supports package-scope as well as application-scope extensions as a general structure, though specific extensions are usually only valid in one or other other. It's fair to say you're suggesting <Extensions Category="windows.packageExtension"> akin to <uap3:Extensions Category="windows.appExtension"> and an equivalent API?

harvinders commented 4 years ago

We are creating an application that is divided into a main app and lots and lots of plugins. We would like to load plugins on demand when we connect a hardware device, detected by the main app. Main application queries the meta data of the hardware and invokes/load the appropriate plugin assembly matching the meta data (things like make of hardware, year manufactured, product code, firmware id, etc).

The plugins are currently optional packages (we can't use app extensions because plugins also have UI component and communication between main app and plugins in non-trivial). We would only like to load the plugin assembly when required, i.e. when the meta data matches. Currently we are using a json file to store the meta data per optional package, this require some boilerplate code and other things to ensure we always package the json file with the plugins.

It seems that the Extension provide the same functionality as we require, however within the manifest as you have described above. If we can get a similar option to define custom properties in the optional package, it would help everyone save writing some code and do testing.

DrusTheAxe commented 4 years ago

Yes, I can see how a 'packageExtension' would be very helpful for your scenario. This warrants its own issue. Would you be so kind as to post a new Issue/Proposal? We can continue the discussion there,

stevewri commented 4 years ago

@harvinders did you create the new issue/Proposal @DrusTheAxe asked above?

harvinders commented 4 years ago

@stevenbrix @DrusTheAxe Apologies, it took a while to get the time to create a dedicated issue. I have now raised #226.

MDendura commented 3 years ago

@harvinders Sorry to revive an old issue and I realise this isn't the ideal place, but I'm very interested in the exact scenario you talked about in your original comment and was hoping it wouldn't be bad form to ask you a couple of questions here.

I am also creating a Project Reunion/WinUI3 desktop app and am trying to load plug-in assemblies containing XAML controls etc. from MSIX Optional Packages. However, I've been a bit stuck trying to work around the fact that the VS project template for an MSIX Optional Package seems to be tied to UWP and the older csproj format. Are you using .NET Framework 4.x or .NET 5/Core?

If you went with .NET 5/Core, was there anything special you had to do in your csproj to have Visual Studio recognise your Optional Package manifest and give you the options to build the package, deploy etc.?

harvinders commented 3 years ago

No, we are still waiting for couple of technology options like reactiveui and winui to mature before jumping.

nextcodelab commented 7 months ago
using System.Globalization;
using Windows.Networking.Connectivity;
using Windows.Security.ExchangeActiveSyncProvisioning;
using Windows.Storage;
using Windows.System.Profile;

namespace Basic.WinUI.Helpers
{
    public class BasicAppInfo
    {

        public static string AppName
        {
            get
            {
                Package AppProperties = Package.Current;
                return AppProperties.DisplayName;
            }

        }
        public static string StorePublisherName
        {
            get
            {
                Package AppProperties = Package.Current;
                return AppProperties.PublisherDisplayName;
            }

        }

        public static string PackageFamilyName
        {
            get
            {
                return Package.Current.Id.FamilyName;
            }

        }

        //Get the version of the app
        /// <summary>
        /// This version has only 3 numbers example - 1.0.9
        /// </summary>
        public static string AppVersion
        {
            get
            {
                var packageVersion = Package.Current.Id.Version;
                return $"{packageVersion.Major}.{packageVersion.Minor}.{packageVersion.Build}.{packageVersion.Revision}";
            }

        }

        public static string GetCurrencySymbol()
        {
            var currencyInUse = new Windows.Globalization.GeographicRegion().CurrenciesInUse[0];
            var currencyFormatter = new Windows.Globalization.NumberFormatting.CurrencyFormatter(currencyInUse) { IsDecimalPointAlwaysDisplayed = false, FractionDigits = 0 };
            var currencySymbol = currencyFormatter.Format(0).Replace("0", "");
            return currencySymbol;
        }
        public static bool GetIsNetworkAvailable()
        {
            var isInternetConnected = false;
            var connectionProfile = NetworkInformation.GetInternetConnectionProfile();

            if (connectionProfile != null)
            {
                var connectivityLevel = connectionProfile.GetNetworkConnectivityLevel();
                isInternetConnected = connectivityLevel == NetworkConnectivityLevel.InternetAccess;
            }
            return isInternetConnected;
        }
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int GetSystemMetrics(int nIndex);

        public static bool IsTouchEnabled()
        {
            const int MAXTOUCHES_INDEX = 95;
            int maxTouches = GetSystemMetrics(MAXTOUCHES_INDEX);

            return maxTouches > 0;
        }
        public static string GetCurrentSystemLanguageTag()
        {
            return Windows.Globalization.Language.CurrentInputMethodLanguageTag;
        }
        public static bool GetIsOnWindows11()
        {
            var version = Environment.OSVersion.Version;
            return version.Major >= 10 && version.Build >= 22000;
        }
        public async static Task<string> GetDeviceInformationAsync(StorageFolder storageFolder = null)
        {
            if (storageFolder == null)
                storageFolder = StorageHelper.LocalFolder;
            var freeSpace = StorageHelper.CovertSize(await StorageHelper.GetFreeSpacebytesAsync(storageFolder));
            var Info = new DeviceInfo();
            Info.FreeSpace = freeSpace;

            var sb = new StringBuilder();
            sb.AppendLine("OS: " + (BasicAppInfo.GetIsOnWindows11() ? "Windows 11 or Latest" : "Windows 10"));
            sb.AppendLine("SystemFamily: " + Info.SystemFamily);
            sb.AppendLine("SystemVersion: " + Info.SystemVersion);
            sb.AppendLine("ApplicationName: " + Info.ApplicationName);
            sb.AppendLine("ApplicationVersion: " + Info.ApplicationVersion);
            sb.AppendLine("DeviceManufacturer: " + Info.DeviceManufacturer);
            sb.AppendLine("DeviceModel: " + Info.DeviceModel);
            sb.AppendLine("DeviceType: " + Info.DeviceType);
            sb.AppendLine("TouchScreen: " + BasicAppInfo.IsTouchEnabled());
            sb.AppendLine("Region: " + Info.Region);
            sb.AppendLine("Language: " + Info.Language);
            sb.AppendLine("LanguageNative: " + Info.LanguageNative);
            sb.AppendLine("LanguageCode: " + Info.LanguageCode);
            sb.AppendLine("FreeSpace: " + Info.FreeSpace);

            return sb.ToString();
        }

        public static bool IsOnWindows11
        {
            get
            {
                var version = Environment.OSVersion.Version;
                return version.Major >= 10 && version.Build >= 22000;
            }
        }
    }
    public class DeviceInfo
    {
        public string SystemFamily { get; }
        public string SystemVersion { get; }
        public string SystemArchitecture { get; }
        public string ApplicationName { get; }
        public string ApplicationVersion { get; }
        public string DeviceManufacturer { get; }
        public string DeviceModel { get; }

        public string DeviceType { get; }
        public string FreeSpace { get; set; }
        public string Region { get; set; }
        public string Language { get; set; }
        public string LanguageNative { get; set; }
        public string LanguageCode { get; set; }

        public DeviceInfo()
        {
            // get the system family name
            AnalyticsVersionInfo ai = AnalyticsInfo.VersionInfo;
            SystemFamily = ai.DeviceFamily;

            // get the system version number
            string sv = AnalyticsInfo.VersionInfo.DeviceFamilyVersion;
            ulong v = ulong.Parse(sv);
            ulong v1 = (v & 0xFFFF000000000000L) >> 48;
            ulong v2 = (v & 0x0000FFFF00000000L) >> 32;
            ulong v3 = (v & 0x00000000FFFF0000L) >> 16;
            ulong v4 = (v & 0x000000000000FFFFL);
            SystemVersion = $"{v1}.{v2}.{v3}.{v4}";

            // get the package architecure
            Package package = Package.Current;

            SystemArchitecture = package.Id.Architecture.ToString();

            // get the user friendly app name
            ApplicationName = package.DisplayName;

            // get the app version
            PackageVersion pv = package.Id.Version;
            ApplicationVersion = $"{pv.Major}.{pv.Minor}.{pv.Build}.{pv.Revision}";

            // get the device manufacturer and model name
            EasClientDeviceInformation eas = new EasClientDeviceInformation();
            DeviceManufacturer = eas.SystemManufacturer;
            DeviceModel = eas.SystemProductName;

            //custom
            switch (AnalyticsInfo.VersionInfo.DeviceFamily)
            {
                case "Windows.Mobile":
                    DeviceType = "Windows.Mobile";
                    break;
                case "Windows.Desktop":
                    DeviceType = BasicAppInfo.IsTouchEnabled()
                        ? "Windows.Tablet" : "Windows.Desktop";
                    break;
                case "Windows.Universal":
                    DeviceType = "Windows.Universal (IoT)";
                    break;
                case "Windows.Team":
                    DeviceType = "Windows.Team (SurfaceHub)";
                    break;
                default:
                    DeviceType = "Unknown";
                    break;
            }
            Region = Windows.System.UserProfile.GlobalizationPreferences.HomeGeographicRegion;
            Language = CultureInfo.CurrentCulture.EnglishName;
            LanguageCode = Windows.Globalization.Language.CurrentInputMethodLanguageTag;
            LanguageNative = CultureInfo.CurrentCulture.NativeName;
        }

    }
}
DrusTheAxe commented 7 months ago

The docs for Windows.ApplicationModel.Package.DisplayName incorrectly state

This property is supported only for UWP apps.

This is incorrect and should be deleted. Ignore that sentence. The rest of the paragraph is the important (and accurate) piece:

On operating systems earlier than 10.0.19041.0, you must call this property on the package returned by Package.Current, otherwise DisplayName will return an empty string.

Doesn't matter what kind of content you have in the package, UWP, DesktopBridge, PackagedService or even no apps. This works for packages, period, with the caveat on <10.0.19041.0 it only yields a value if you got the Package object via the .Current property.

For example on <10.0.19041.0 FOREACH p IN PackageManager.FindPackagesForUser(...) { p.DisplayName } returns an empty string whereas Package.Current would return the expected value.

Will pass along word to correct the docs