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.
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:
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.
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:
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 »