Copilot for Microsoft 365 Tech Accelerator
Feb 28 2024 07:00 AM - Feb 29 2024 10:30 AM (PST)
Microsoft Tech Community
Desktop Bridge – Identify the application’s context
Published Jan 15 2019 01:57 PM 397 Views
Microsoft
First published on MSDN on Nov 03, 2016

In the previous post we’ve seen how the Desktop Bridge allows developer not just to turn a desktop application into an AppX package as it is and, eventually, nominate it so that we can publish it on the Store, but also to start integrating Windows 10 features without having to rewrite it from scratch as a full UWP app.

However, this approach could lead us to maintain two different code bases of the same application: a standard one, for all the users who don’t use the Store or who still don’t have Windows 10 on their computers, and a “modern” one, which instead is able to use the UWP APIs. As a developer, you’ll know for sure that keeping multiple branches of the same application has a cost: every time we need to add a new feature (especially if we are talking about a “generic” feature and not a specific Windows 10 one), we need to multiply our efforts.

In the previous post we’ve already seen a first approach to avoid keeping two different branches of our application: conditional compilation. Thanks to this feature, we can leverage the #if and #endif keywords or mark some methods with the Conditional attribute so that some methods or parts of the code are invoked only when the application is compiled to be packaged inside an AppX package.

We implemented a real example of this approach in the sample application we have created in the previous post , which is available on GitHub: https://github.com/qmatteoq/DesktopBridge/tree/master/4.%20Enhance The application includes a button to create a file on the user’s desktop by using the standard .NET Framework APIs but, thanks to conditional compilation, only when the app is launched inside the UWP container it calls also a method called ShowNotification() , which shows a toast notification to the user by using the standard UWP APIs.

However, there are some scenarios where conditional compilation isn’t enough to satisfy all our requirements. For example, in some cases you need to change the user interface of the application based on the context where the application is running. If, for example, we have implemented an auto-update feature in our application and we have added a button to check if there is new version of the application to download, we need to find a way to hide this button when the app is released through the Store: updates, in fact, will be published directly on the Dev Center, like a regular Store app, so we don’t need anymore to manually manage the update process.

In this case, conditional compilation can’t help: we need to determine, at runtime, which is the context where the application is running and choose if we need to display or not the update button.

To reach this goal, we can use a native API offered by Windows, which is included in the system library called kernel32.dll and offers a method called GetCurrentPackageFullName() that is able to retrieve the identity of the application. The identity’s concept belongs just to the UWP world (in Windows 10) and to the Windows Store apps world (in Windows 8.x). When a desktop application is running as native, outside the UWP container, it doesn’t have any identity and, as such, this API will return an error, which will help us to understand the current context. You can find an example on how to use this API in the official documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/hh446599(v=vs.85).aspx

#define _UNICODE 1
#define UNICODE 1

#include <Windows.h>
#include <appmodel.h>
#include <malloc.h>
#include <stdio.h>

int __cdecl wmain()
{
UINT32 length = 0;
LONG rc = GetCurrentPackageFullName(&length, NULL);
if (rc != ERROR_INSUFFICIENT_BUFFER)
{
if (rc == APPMODEL_ERROR_NO_PACKAGE)
wprintf(L"Process has no package identity\n");
else
wprintf(L"Error %d in GetCurrentPackageFullName\n", rc);
return 1;
}

PWSTR fullName = (PWSTR) malloc(length * sizeof(*fullName));
if (fullName == NULL)
{
wprintf(L"Error allocating memory\n");
return 2;
}

rc = GetCurrentPackageFullName(&length, fullName);
if (rc != ERROR_SUCCESS)
{
wprintf(L"Error %d retrieving PackageFullName\n", rc);
return 3;
}
wprintf(L"%s\n", fullName);

free(fullName);

return 0;
}


As you can notice, if you’re working on a C# application based on the .NET framework (like the sample one we used in the previous samples), there’s an additional complexity: the API isn’t directly exposed by the .NET framework, but it’s a native C++ method offered directly by the Windows core. Consequently, it isn’t so easy to use it in a managed application: you need to leverage interop approaches to allow these two worlds to talk together, like using the P/Invoke feature or C++ / CLI libraries. Additionally, the code we’ve just seen has been introduced in Windows 8, since the Windows Runtime and the concept of Windows Store apps didn’t exist in Windows 7. As such, using this code on Windows 7, Vista or even XP will always return an exception.


You can notice this requirement in the official documentation:



To make the developer’s life easier, I’ve decided to include the C++ code we’ve previously seen inside a .NET library which is supported by any .NET 4+ application, which leverages:



  • The P/Invoke approach to invoke the GetPackageFullName() native method

  • The native .NET Framework APIs to check if the app is running on an operating system where tis API isn’t supported, like Windows 7

The library is open source and it’s available on my GitHub repository: https://github.com/qmatteoq/DesktopBridgeHelpers/ . Additionally, it’s also available as a NuGet package, to make your life easier when it comes to include it in your applications. The id of the package is DeskopBridge.Helpers .


Once installed in your .NET project (no matter if it’s a console, Windows Forms or WPF application), it’s enough to create a new instance of the DesktopBridge.Helpers class and call the IsRunningAsUwp() method. It will return a boolean value: true if the application is running inside the UWP container or false if, instead, it’s running as a native desktop application.


You can find many samples (one for each .NET technology) inside the repository itself: https://github.com/qmatteoq/DesktopBridgeHelpers/tree/master/Samples


Also the original project used in the previous post has been updated to leverage this library. Inside the window of the Windows Forms application, in fact, I’ve added a Label control, where I display a different text based of the execution context, which is determined when the form is loaded with the following code:

private bool IsRunningAsUwp()
{
UwpHelpers helpers = new UwpHelpers();
return helpers.IsRunningAsUwp();
}

private void Form1_Load(object sender, EventArgs e)
{
if (IsRunningAsUwp())
{
txtUwp.Text = "I'm running inside a UWP container";
}
else
{
txtUwp.Text = "I'm running as a native desktop app";
}
}


If you now set as startup project the Windows Forms one (called Enhance ) you will notice that the message displayed inside the window will be I’m running as a native desktop app. If, however, you set as startup project the UWP deployment one (called Enhance.DesktopToUWP ) or you try to create an AppX package from it, then you’ll get the message I’m running inside a UWP container.






Avoiding deployment errors


This library can be useful also for another scenario: avoiding deployment errors when you leverage the conditional compilation approach. If you have read the previous post carefully , you’ll remember that, by directly launching the native desktop application using the DesktopUWP configuration, the file on the desktop was successfully created but then you got an exception: the reason was that, when you apply the DesktopUWP configuration , the application invokes the ShowNotification() method, which leverages the UWP APIs to show the toast notification to the user. However, since the app is running as native and not inside the UWP container, the execution will fail with an exception. It’s important to remember, in fact, that to leverage UWP APIs it isn’t enough to launch the desktop application on a PC with Windows 10 Anniversary Update, but it needs to be packaged as AppX and launched inside the UWP container.


Consequently, if you don’t pay enough attention, you may risk to create a traditional installer of your application (to release, for example, on your website for users who don’t have Windows 10 yet) which includes an executable compiled in the wrong way (in this case, using the DesktopUWP configuration): as such, your users will get a crash at runtime when they will press the button to create the file on the desktop.


You can avoid deployment issues like this one by leveraging the same library we’ve discussed before by adding, when the application is loaded, a code similar to the following one:

private void OnFormLoaded(object sender, EventArgs e)
{
DesktopBridge.Helpers helpers = new DesktopBridge.Helpers();
bool isUwp = helpers.IsRunningAsUwp();
#if DesktopUWP
if (!isUwp)
{
MessageBox.Show("You're compiling the app with the wrong configuration!");
this.Close();
}
#endif
}
}


By using the #if and #endif keywords, we use the library to check if the application is running inside the UWP container or not only when it’s compiled using the DesktopUWP configuration. This way, if you try  to launch the Windows Forms application as native but compiled with the wrong configuration, you’ll realize immediately your mistake, thanks to the warning message. Without this approach, you would risk to not notice the mistake until you leverage a feature which uses the UWP APIs. In the case of our sample app, for example, the app would always start without errors, because we don’t use UWP APIs at startup. Consequently, we won’t notice the issue until we press the button to create the file on the desktop. This way, it will be easier for us to make sure that:



  • When we want to release the application using a traditional installer, it will be compiled using the Debug or Release configuration.

  • When we want to release the application as an AppX package, it will be compiled using the DesktopUWP configuration.

Wrapping up


I hope that this library, even if it’s very simple, can make your life easier to optimize your applications and avoiding to keep two different code bases in order to support traditional installers and AppX distribution. Before wrapping up, I want to thank Raffaele Rialdi , one of the most known Italian MVPs, who helped me to better understand the P/Invoke mechanism and implement it in the proper way inside the library.


Happy coding!

Co-Authors
Version history
Last update:
‎Nov 22 2022 12:21 PM
Updated by: