Enable In-App Product Purchases for Desktop Bridge Converted Applications


Desktop Bridge helps developers gradually migrate traditional apps to the Universal Windows Platform (UWP). The In-App Purchase (IAP) in Windows Store is an important scenario to monetize the converted apps. When working with developers on this scenario, I notice certain obstacles are in common, for example:

 

a. Porting old .Net or unmanaged applications to use the IAP UWP APIs.

b. Didn’t use async calls properly in UI apps and caused hang/deadlock

c. Default purchase windows failed to popup

d. Need accurate guide to follow IAP testing with current Windows Store APIs

 

Here I introduce the IAPWrapper solution to solve above issues for converted Win32 Desktop Applications, and also show how easily to use the the IAPWrapper in different scenarios:

 

a. WinForm in old .Net version

b. WPF app with latest .Net version

c. Win32 App in C++

d. Unity Win32 App in old .Net version

 

Note: The sample code is put on: https://github.com/appconsult/IAPWrapper

Many converted Win32 Desktop apps were developed in unmanaged code or old .NET version which cannot directly call current UWP Store APIs, in order to eliminate this gap, let's create an .Net IAP Wrapper library. This wrapper will finally leverage DLLEXPORT nuget package to export function entries, which can be used by different kinds of Apps easily. Below are detailed steps:

 

Create an IAP Wrapper Project:

1. Create a Win32 .NET class library project. The target frame work can be 4.6.1. I uses VS2017 as a quick start.

2. Add two references to allow this Win32 dll calls UWP APIs and async/await properly:

C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd

C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework

\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

3. Add statements to use necessary namespaces:

using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Services.Store;

 

4. Follow this guide to call a StoreContext object in class library project that will be uses the Desktop Bridge:

https://docs.microsoft.com/en-us/windows/uwp/monetize/in-app-purchases-and-trials#using-the-storecontext-class-with-the-desktop-bridge

In Converted Win32 Desktop App, it needs to configure the StoreContext object to specify which application window is the owner window for modal dialogs. The functional sample code is:

 public class IAPWrapper
    {
        //Declare the IInitializeWithWindow interface in your app's code with the ComImport attribute
        [ComImport]
        [Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IInitializeWithWindow
        {
            void Initialize(IntPtr hwnd);
        }

        //Get a StoreContext object by using the GetDefault method 
        private static StoreContext storeContext = StoreContext.GetDefault();

        public static string Purchase(string storeID)
        {
            try
            {

                PurchaseAsync(storeID);
                return "Done";
            }
            catch (Exception ex)
            {
                return "Exception:" + ex.ToString() + "exMessage:" + ex.Message;
            }
        }

        static async Task PurchaseAsync(string storeID)
        {
              IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)storeContext;
            var ptr = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;

            //Call the IInitializeWithWindow.Initialize method, and pass the handle of the window 
            //to be the owner for any modal dialogs that are shown by StoreContext methods.
            initWindow.Initialize(ptr);

            var result = await storeContext.RequestPurchaseAsync(storeID);

            if (result.ExtendedError != null)
            {
                MessageBox.Show(result.ExtendedError.Message, result.ExtendedError.HResult.ToString());
            }         

            MessageBox.Show("Purchase Finished with status " + result.Status);
        }
    }

 

5. Install DLLExport nuget package to the project:

clip_image009_thumb1

After installed the nuget package, you may see a setting dialog box pups up, can refer to this picture to configure (name space is still IAPWrapper here):

clip_image011_thumb1

For more details of DLLExport, please refer to: https://github.com/3f/dllexport

6. Add one statement to declare DLLExport before the Purchase (string storeID) function that we need to export from this dll:

[DllExport(CallingConvention.StdCall)]

public static string Purchase(string storeID)

7. Use X86 or X64 option to build the library.

Note: AnyCPU option doesn’t give us the expected exported function entries.

If I build it in X64 option, using “dumpbin iapwrapper.dll /exports” can see the “Purchase” is listed in export section

clip_image013_thumb1

This means we can load the library and function address to call it directly.

If build it in AnyCPU configuration option, the export function will not show up:

clip_image014_thumb1

8. After above steps, we will get the IAPWRAPPER.dll, now can use it easily in various scenarios.

 

To move forward, firstly we should avoid below known issues:

1. Don’t call Task.Wait directly, this will cause UI apps deadlock issue. For detailed info, refer to:

Async/Await - Best Practices in Asynchronous Programming

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

2. Don’t run app with admin privilege, otherwise may not see the purchase diaglog box popup.

3. Make sure the purchase option runs in the UI thread, refer to:

https://docs.microsoft.com/en-us/uwp/api/Windows.Services.Store.StoreProduct

clip_image016_thumb1

4. The store ID used as parameter is the 12 characters (9pxxxxxxxxxx) of add-on on your developer portal, not product display name 001.

image

 

Now we start go through each scenario to see how to call the IAP functions:

Scenario one: Use IAPWRAPPER in .Net 2.0 Winform app

1. Add DLLImport statement and call the Purchase function:

public partial class Form1 : Form
 {
        [DllImport("IAPWrapper")]
        extern static IntPtr Purchase(string storeID);
        public Form1()
        {
            InitializeComponent();
        }

        private void Demo_Click(object sender, EventArgs e)
        {
            Purchase(storeID.Text);
        }
}

2. Build the app, and put IAPWRAPPER.dll into the output folder.

Run the app, and trigger the purchase, we can see default purchase windows pops up (depends on your account setting, authentication window may occur as well,):

clip_image019_thumb1

clip_image021_thumb1

 

Scenario two: Use IAPWRAPPER in .Net 4.6 WPF app

1. Add DLLImport and call purchase function:

public partial class MainWindow : Window
    {
        [DllImport("IAPWrapper")]
        extern static IntPtr Purchase(string storeID);
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Demo_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Purchase(StoreID.Text);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.StackTrace);
            }

        }
    }

2. Build the app, and put IAPWRAPPER.dll into the output folder. Run the app, it will show up the same purchase window.

 

Scenario three: Use IAPWRAPPER in C++ Win32 app

1. Use LoadLibrary and GetProcAddress to call the Purchase function:

typedef char* (WINAPI *PURCHASE)(char*);
。。。
HINSTANCE hGetProcIDDLL = LoadLibrary(L"IAPWrapper.dll");
PURCHASE purchase = (PURCHASE)GetProcAddress(hGetProcIDDLL, "Purchase");
char *s = purchase("9pjp4testztd");
MessageBoxA(hDlg, s, "Return Info",0);

2. Run the app, it will show up the same purchase window.

 

Scenario four: Use IAPWRAPPER in UNITY Win32 app

1. Put IAPWrapper into the Assets\Plugins folder

2. Add DLLImport and Purchase function as below:

public class IAPCaller : MonoBehaviour
{
    [DllImport("IAPWrapper")]
    extern static IntPtr Purchase(string storeID);
    
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))
        TaskOnClick();
    }
     void TaskOnClick()
    {
        Purchase("9pjp4testztd");
        Debug.Log("You have clicked the mouse!");
    }
}

3. Build the app as Win32 app, and run it.

 

Summary

Here we discussed how to quickly integrate the essential Windows IAP function into different kinds of Win32 Apps. You may expand your IAPWrapper for more specific usage scenarios, checking licensing, trail status, enumerate add-ons, etc. Can refer to:

https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/StoreSample

After completing above tasks, please follow the 5 bullets exactly to ensure your applications are ready for IAP and Store function tests:

https://docs.microsoft.com/en-us/windows/uwp/monetize/in-app-purchases-and-trials#testing

 

Thank you!


Comments (0)

Skip to main content