Home » Blog
date 21.Feb.2017

■ Add Microsoft Store license check to your desktop (win32) application


ps. If you are a programmer just looking for the store licensing code, scroll to the end and skip the ranting :)

Do you remember the days of programming innocence? When I started learning C, everything was easy to grasp. You had the loops and you had the pointers (memory). And you built anything you wanted on top. Then C++ introduced objects, and vtables and smart pointers. Was it an improvement? I still occasionally have to double check that the way I am using CComPtr is not leaking references. And when does it throw exceptions? It isn't clear cut. Then things progressively got out of hand and modern C++ is unrecognizable in its constructs. I have nothing against "conveniences" but I prefer to understand what's happenning. Better the devil you know.

Then in 2012 (windows 8), microsoft to further annoy old dog programmers, decided that windows programming should move from cooperative window-message-based prototype to callback/async and ultimately castrated (in terms of capability) programming, the so called WinRT (Windows Runtime). Programmers are not to be trusted, and need to get user permission even to choose a file. Never mind interprocess communication and the clipboard, that is for dodgy hackers that write ransomware (!). With that kind of straitjacket you end up with "apps" that do next to nothing but look "cool", and neither programmers wrote nor users bought. Just take a look at the utilities section in the Store, lists 974 tools, and the equivalent desktop section in download.com shows 15913.

Project centennial to convert win32 desktop apps and list them in the Store


Desperate about the state of the Store, microsoft took the big step and allowed proper old school desktop tools to be added to the store. The conversion is mostly automatic; Desktop bridge converts your traditional installer into an APPX bundle, which after some approval can be uploaded to the Store. The sandboxing imposed on straight UWP apps does not apply to converted desktop apps; your program can do pretty much what it likes except for accessing and changing the registry (that ensures clean uninstallation).

Amazingly Microsoft put a lot of effort and resources to help desktop developers get on the store. You get a dedicated person to help you out with all things, from programming to store submission, and there is an active UWP support forum, all free. I suppose they expect to break even with all the 30% fees the Store takes from each app sale.

Getting listed in the Store is one thing, but making money is another. Desktop bridge will convert your program wholesale, including the old licensing and registration code, but the store won't allow you to use external payment processors — and forfeit the lucrative 30% sales commission. So you must strip away all the old licensing logic. Microsoft would have you believe that all you need to do is to let the Store handle the free trial and registration without adding any code to your program, but that's an illusion:

It is amazing that the store is out now for 5 years and such basic faults are still present. At any rate the solution is to get the store out of the way and do everything manually. I set the store submission to Trial never expires and manage the trial the "hard" way — of course if you have a desktop program you already have code to do just that without assistance.

Don't get caught out by UWP sandboxing. Don't store trial information in the "registry" or in the local appdata folder — these are all erased if user uninstalls then reinstalls. But as a fully (runFullTrust) trust-enabled desktop program you can use the filesystem to leave your first registration tracks.

Calling into winRT from win32 code


So how do you actually check for the store license? You need to call into winRT Windows.Services.Store namespace (the older Windows.ApplicationModel.Store is not open to converted desktop apps). And how do you do that from win32? There are code samples that demonstrate licensing but brace yourself for the shock. WinRT was not meant to be used by "low level" C++ so an extension called C++/CX was hacked in to provide access. You will see hat ^ pointers, HSTRING strings, await keywords and other weird things. It feels like programming in javascript. You have no idea what is happening behind the scenes and instead of error codes be prepared to catch exceptions.

Thankfully there is another interface to winRT called WRL. It has a superficial semblance to ATL COM but the concepts are quite different. At least it feels like C++ but as you can see yourself below, it is very spaghetti like. I found some crude WRL registration code on stack overflow and have converted it below in a standalone function.

So on to the code. I don't understand what most of these classes do exactly, but the license check works. You may need some painkillers for headache <g> Please refer to my older blog post that explains how to setup the development environment.

//
// requires win10 (latest) SDK
#include <Windows.Services.Store.h>
#include <wrl.h>

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Services::Store;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;

// winRT thread to check store license
// WARNING: NO ERROR CHECKING FOR BREVITY BUT YOU SHOULD DO CHECKS!
BOOL StoreIsRegistered(HWND hwdlg)
{
   ATLASSERT(::IsWindow(hwdlg)); // e.g the window showing "checking license..."

   RoInitializeWrapper roinit(RO_INIT_MULTITHREADED);
   HRESULT hr = roinit;
   // if this blows, you have initialized COM in an incompatible appartment mode
   // so better create a separate temp thread just for the winRT stuff here
   ATLASSERT(SUCCEEDED(hr));

   BOOL bHaveLicence = FALSE;
   ComPtr<IStoreContextStatics> storeContextStatics;
   hr = GetActivationFactory(HStringReference(L"Windows.Services.Store.StoreContext").Get(), &storeContextStatics);
   ComPtr<IStoreContext> storeContext;
   hr = storeContextStatics->GetDefault(&storeContext);

   // this is a requirement for desktop converted apps, dunno why
   // https://msdn.microsoft.com/windows/uwp/monetize/in-app-purchases-and-trials#desktop
   ComPtr<IInitializeWithWindow> initWindow;
   hr = storeContext->QueryInterface(IID_PPV_ARGS(&initWindow));
   hr = initWindow->Initialize(hwdlg);
   
   ComPtr<IAsyncOperation<StoreAppLicense*>> getLicenseOperation;
   hr = storeContext->GetAppLicenseAsync(&getLicenseOperation);

   ComPtr<IStoreAppLicense> appLicense;
   // async programming: setup a callback then wait for the event to fire
   HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, 0);

   // i don't think we need fancy threaded callback -- we are already threaded
   //Callback<Implements<RuntimeClassFlags<ClassicCom>, IAsyncOperationCompletedHandler<StoreAppLicense*>, FtmBase>>
   auto onCompletedCallback = Callback<IAsyncOperationCompletedHandler<StoreAppLicense*>>(
      // lambda!
      [&appLicense, hEvent](IAsyncOperation<StoreAppLicense*>* operation, AsyncStatus status)
   {
      if (status != AsyncStatus::Completed) {
         // It failed for some reason. Find out why.
         // ComPtr<IAsyncInfo> asyncInfo;
         // operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
         // HRESULT errorCode; asyncInfo->get_ErrorCode(&errorCode);
      }
      else {
         auto hr = operation->GetResults(&appLicense); // implicit return
         ATLASSERT(SUCCEEDED(hr));
      }

      SetEvent(hEvent);
      return S_OK;
   });

   hr = getLicenseOperation->put_Completed(onCompletedCallback.Get());
   // wait for the operation to complete
   WaitForSingleObject(hEvent, INFINITE);

   if(appLicense) {
      boolean isActive = false, isTrial = false;
      hr = appLicense->get_IsActive(&isActive);
      if (isActive) {
         hr = appLicense->get_IsTrial(&isTrial);
         bHaveLicence = !isTrial;
      }
      // not sure what inactive license means, revoked?
   }

   CloseHandle(hEvent);
   return bHaveLicence;
}

Make sure you read the code comments (and add your own error checking :). Here are some important points:

And the morale of the story is...


I successfully submitted deskrule lite to the store late last year (it's listed for 2 months now). The sure thing is that I don't need to buy a bigger safe deposit box <g> I can't say I am disillusioned, it was an experiment with low expectations. The downloads are just not there. Even as a free trial, $20 is huge in Store terms, where most apps are free or just cost a dollar or two. They say that one has better chances to enrichment distributing a free basic app and sell "in-app" functional upgrades (IAP). This is an experiment that I will leave for someone else :)

I also converted and submitted xplorer² lite to the store. That was simpler because it has no registration code to worry about. The downloads are much better compared to deskrule, but you will not get rich giving away the family silver. Also I see that it crashes a bit too often compared to the pure desktop version (the Store has a good crash health analytics section) — I suspect it is something to do with the UWP wrapper.

Post a comment on this topic »

Share |

©2002-2017 ZABKAT LTD, all rights reserved | Privacy policy | Sitemap