In this blog post I'll show you how simple it is to detect from any regular 3rd party Windows 10 app if any target app is installed on the same device.

The motivation

Typical scenario, you are a publisher with 5 popular apps on Windows Store. User has just installed your App.A and you'd like to know if he has already installed App.B, App.C, etc, and if not, you would like to show Toast notification to promote the other apps you have published.

The solution

I won't beat around the bush, all you need is the package family name of all apps you want to discover and this method:

Launcher.QueryUriSupportAsync(Uri uri, LaunchQuerySupportType launchQuerySupportType, string packageFamilyName)

This method is part of the Launcher class and the primary goal of this method is to detect, whether target app X supports Uri scheme Y, but due to some oversight or maybe intentionally it can be used for detecting whether any target app is installed on the same device or not. You can use it even for detecting installed Windows 8.1 apps, Windows Phone 8.1 apps or even Edge extensions!

How it works?

private static readonly Uri dummyUri = new Uri ( "mailto:dummy@seznam.cz" ) ; /// <summary> /// Check if target <paramref name="packageName"/> is installed on this device. /// </summary> /// <param name="packageName">Package name in format: "949FFEAB.Email.cz_refxrrjvvv3cw"</param> /// <returns>True is app is installed on this device, false otherwise.</returns> public static async Task < bool > IsAppInstalledAsync ( string packageName ) { try { bool appInstalled ; LaunchQuerySupportStatus result = await Launcher . QueryUriSupportAsync ( dummyUri, LaunchQuerySupportType . Uri , packageName ) ; switch ( result ) { case LaunchQuerySupportStatus . Available : case LaunchQuerySupportStatus . NotSupported : appInstalled = true ; break ; //case LaunchQuerySupportStatus.AppNotInstalled: //case LaunchQuerySupportStatus.AppUnavailable: //case LaunchQuerySupportStatus.Unknown: default : appInstalled = false ; break ; } Debug . WriteLine ( $ "App {packageName}, query status: {result}, installed: {appInstalled}" ) ; return appInstalled ; } catch ( Exception ex ) { Debug . WriteLine ( $ "Error checking if app {packageName} is installed. Error: {ex}" ) ; return false ; } } private static readonly Uri dummyUri = new Uri("mailto:dummy@seznam.cz"); /// <summary> /// Check if target <paramref name="packageName"/> is installed on this device. /// </summary> /// <param name="packageName">Package name in format: "949FFEAB.Email.cz_refxrrjvvv3cw"</param> /// <returns>True is app is installed on this device, false otherwise.</returns> public static async Task<bool> IsAppInstalledAsync(string packageName) { try { bool appInstalled; LaunchQuerySupportStatus result = await Launcher.QueryUriSupportAsync(dummyUri, LaunchQuerySupportType.Uri, packageName); switch (result) { case LaunchQuerySupportStatus.Available: case LaunchQuerySupportStatus.NotSupported: appInstalled = true; break; //case LaunchQuerySupportStatus.AppNotInstalled: //case LaunchQuerySupportStatus.AppUnavailable: //case LaunchQuerySupportStatus.Unknown: default: appInstalled = false; break; } Debug.WriteLine($"App {packageName}, query status: {result}, installed: {appInstalled}"); return appInstalled; } catch (Exception ex) { Debug.WriteLine($"Error checking if app {packageName} is installed. Error: {ex}"); return false; } }

The method returns enum of possible outcomes, either the target app is installed and supports the target Uri scheme, or is installed but does not support it, or is not installed, or in other unsupported state.

Before checking this approach myself I would have expected, that for apps published by other developers it would just return generic Unknown response, if the Uri scheme is not supported or the app is not installed, but it works just fine as shown below.

Sample app

I've uploaded simple app on GitHub that checks for installation status of some popular apps and games. This is the result when I run this app on my Windows 10 Desktop machine with Windows 10 build 14393 (Anniversary Update).

F.A.Q.

Does it work on Windows 10 Mobile/Xbox/HoloLens?

Yes

How can I get the package family names to use?

It's quite easy, just install the app on your machine and open the folder:

C:\Users\<username>\AppData\Local\Packages

Here you can find app data for all your installed apps from Windows Store. The folders here use the package family names.

The only problem here is that you cannot access this folder on Windows 10 Mobile or Xbox, but Universal apps use the same package family name on all platforms.

Note there might be other ways how to detect the package family names for Windows Phone-only apps. If you find them, just let me know!

Do I need any special App Capability? Does it pass Windows App Certification Kit?

No special capability required. No problem with WACK.

Why is this a problem?

First of all Windows Store apps should run in a Sandbox and by design one app should not be able to detect anything about other installed apps.

Since we can detect if other apps are installed, this feature could be easily misused for nefarious purposes like Targeted Advertising, Device Fingerprinting or Behavior Detection.

For instance Bank app can gather information about number of installed games or social networks to profile the target user.

Game developer can gather intel what other popular games are installed on the same machine.

Malware app or library could detect what social network apps is the target user using, etc.

How this could be solved?

If the Windows OS team agrees that this needs to be fixed, this is the solution I can imagine. First there should be option in the App manifest to define Uri scheme detection as public for any app or private for only whitelisted apps. Then these rules should be applied:

if target app is not installed - return Unknown result.

result. if target app is installed and from the same publisher - no change in behavior . I should be able to ask if my apps are installed.

. I should be able to ask if my apps are installed. if target app is installed but does not support target Uri scheme - return Unknown result.

result. if target app is installed and has public Uri scheme - return Available result.

result. if target app is installed, has private Uri scheme and asking app is whitelisted, return Available result.

result. if target app is installed, has private Uri scheme and asking app is not whitelisted, return Unknown result.

Note the same fixes should be applied to File type support detection as well. I've not tested it, but I can imagine that the QueryFileSupportAsync method has the same behavior.

Summary

I've shown in this blog post that on Windows 10 device it's really easy to detect if user has Twitter or Facebook or any other app installed, possible issues that this behavior could create and possible solution to solve it in the future.

For more UWP tips - follow me on Twitter 🙂