Porting Desktop Apps to Windows 8.1 Store Apps
Windows Store Applications bring a brand new look and user experience. It’s also a great challenge for the Windows developers who are familiar with the desktop applications development to adapt to the new interface and programing model. In this article, I try to provide the generic guideline on how to port desktop apps to Windows store apps more quickly and smoothly.
Consideration at the design stage
When you port your desktop apps to Windows store apps, the first thing you should do is to evaluate your desktop application to find out the reusable parts. Actually, not all of the code can be reused. Generally speaking, we can separate the application code into the UI part and the business logic part. If these two parts are coupled tightly in your application, you should put some efforts to decouple them. For windows store apps, we recommend to use MVVM design pattern to isolate the view and the behavior. Then we can discuss the UI porting and business logic code porting individually.
The traditional technologies used to rend the UI in Windows platform are GDI/ GDI+, Windows Form, DirectX or WPF/Silverlight. Let’s discuss them one by one:
If you are using GDI/GDI+ or Windows Form to develop the UI, you should consider to rewrite the code since they are not supported in Windows store apps. You can write XAML code, which is a declarative language, to render the UI. It’s much easy to implement the static pages. But if you want to generate the UI element in the fly, you should consider Direct2D/DirectWrite. Here is the link to demonstrate how to use Direct2D in a Windows store application.
If your previous application is written in WPF/Silverlight, in technical you can reuse most of the UI code. XAML for Windows Store apps is a very close match to XAML for Silverlight and WPF. The major change is that some controls for Windows Store apps are different. You can refer to UI controls to get a list of Windows Runtime controls. However, you should still involve your designer to redesign the application UI followed by the UX guidelines. Please understand that the Windows Store apps UI is designed for the touch screen. It’s important to make sure that your new UI is user-friendly for the touch screen. A post Migrate Silverlight applications to Windows Store apps also elaborates on the detail.
If you want to port a game which is written in DirectX, it would be easier because you don’t need to redesign the UI (Or just redesign the menu of the game, which is not a big task.). You can just upgrade your original DirectX interface to DirectX11. You can also involve XAML in your DirectX apps to implement the menu system flexibly. For more detail, please refer to this blog Combining XAML and DirectX.
Your desktop application may use variant technologies to implement the business logic. Though Windows store apps are built on Windows Runtime (WinRT) library, you can still involve other libraries and frameworks. Here is a list on the supportability of the common used libraries and frameworks:
Technology |
Supportability |
Remark |
MFC |
Not supported |
Consider to use STL/CRT |
ISO C++ |
Supported |
|
CRT |
Most of CRT APIs are supported |
Refer to this link for unsupported APIs |
Win32 |
A subset of Win32 APIs is supported |
Refer to this link for supported APIs |
COM |
Only registration-free COM objects are supported |
Load registration-free COM object with CoCreateInstanceFromApp |
ATL |
A subset of ATL APIs is supported |
WRL can be the replacement of ATL |
.NET |
.NET for Windows Store apps is supported |
Refer to this link for supported APIs
|
C++/CLI |
Not supported |
Consider to use C++/CX |
Besides it, you should also take into the account the restrictions and the new features of the Windows Store apps. Some desktop app features are not supported in Windows store apps:
- Console
- Inter process communication
- Registry Access
Some features are restricted:
- File access: Generally you can only access the application private storage. You can’t access arbitrary files or folders unless using File Picker to select this file or folder manually.
- Direct hardware access: Generally, only Windows store device apps can access hardware directly. And only IHV and OEM can develop Windows store device apps. In Windows 8.1, Windows Store apps can communicate with USB, Human Interface Devices (HID), Bluetooth GATT, Bluetooth RFCOMM, and Point of Service (POS) devices since we provided the relative Windows Runtime interface.
- Direct Thread creation: You can’t create threads directly. You should use Thread pool to handle multithread tasks or using PPL asynchronous programing model.
So if your application relies on these features, you should consider to redesign the application, rewrite the related code and look for the possible workaround. For example, you can communicate with another process using web service; you can store your application settings into Application data instead of registry.
Windows store apps also bring some new features which desktop apps don’t have. These features can great extend your application’s functionality with only a few code. Here I list the major features that Windows store apps can involve:
- Search: You can involve the global search pane to search your application content.
- Share: The share panel can help users share content from your app with another app or service.
- Settings: The settings panel provides quick, in-context access to settings of your application.
- Live Tile: This is the application entry but can show push notifications even if the application doesn’t launch.
Implementation at the porting stage:
After reviewed your desktop application, now you know which code is reusable and which isn’t. Porting a desktop application doesn’t mean that you can convert an existing desktop application to a Windows Store app line-by-line. Instead, you should create a new project and rewrite the code with following steps:
- Choose the best VS template to create your Windows store application.
Visual Studio 2013 provides many templates to speed up your app development. You should choose the most suitable one according to your requirement. Windows Store apps support C++, C#/VB.NET and Html5/JavaScript languages. You can choose any of them or mix them freely. A common practice is to write the application in C# and WinRT components in C++.
There are four types of project templates to generate a Windows store application:
Blank project: This template doesn’t provide any predefined controls and layout. If you want to start your UI design by yourself, it’s a good choice.
Grid project: This template enable users to view groups of items and item details in three pages: the home page shows the list of groups and the related items. The group page shows the items in the group when a group is clicked. The detail page shows the item detail when an item is clicked.
Hub project: This template use a Hub control to display sections and item details in three page: the hub page show the list of sections. Each section has individual layout of the items. The section page show the items in this section and the detail page show the item detail.
Split project: This template enables users to view groups and item details in two pages: the home page only shows the list of groups. The split-view page displays a master/detail view, where the details on the right side change when a user selects an item on the left side.
More detail on the project template please refer to C#, VB, and C++ project templates.
You can also add some new Windows 8 features by adding item templates. For example, it’s easy to add a Search or Share functionality with the help of Search and Share contract item template. Please also refer to C#, VB, and C++ item templates for more detail.
- Redesign your UI with the Microsoft design style.
I don’t want to talk too much about it because it’s totally a designer’s task. If you have a designer who understand the principle of Microsoft design style and is familiar with Blend for Visual Studio 2013, it would be easy to design your UI.
- Wrap code and convert existing code.
Generally the business logic code of your desktop application is packed into Win32, COM or .Net libraries. In Windows store apps, the corresponding mapping is Windows runtime components. We have two options to reuse your code. Either you load your existing libraries in your Windows Runtime (WinRT) components, or you convert your code to WinRT components. It depends which types of libraries you use:
Windows Store apps can load the custom Win32 dlls by static and dynamic ways. Let’s demonstrate it using a sample.
Suppose that we have a Win32 dll called testdll.dll which exposes a function Add:
extern "C" _declspec(dllexport) int Add(int x,int y)
{
return x+y;
}
Then you can create a Windows store application or WinRT component project and add testdll.dll and testdll.lib to this project. You should set Content property of testdll.dll to “True” so that the dll can be copied to the installation package.
After finished above steps, you can call Add function in static way:
extern "C" _declspec(dllimport) int Add(int x,int y);
void Test()
{
int c;
c=Add(1,2);
}
You can also load Win32 dlls in dynamic way. In this case you should use LoadPackagedLibrary instead of LoadLibrary:
void Test()
{
int c;
typedef int (*DLLPROC) (int a, int b);
DLLPROC Addfun;
HINSTANCE DLLHandler;
BOOL fFreeDLL;
DLLHandler = LoadPackagedLibrary(L"TestDll.dll", 0);
DWORD error = GetLastError();
Addfun=(DLLPROC)GetProcAddress(DLLHandler,"Add");
if(Addfun!=NULL)
{
c=(Addfun)(1,2);
}
fFreeDLL = FreeLibrary(DLLHandler);
}
Please note that it’s required to add the testdll.dll to the project and set the Content property to True. And please do not set a full path file name to the first parameter of LoadPackagedLibrary function because Windows Store apps can’t access an arbitrary file path.
If your application is written in .NET, you can also use P/Invoke to call the functions in your Win32 dlls, just like what you did in your .NET desktop application.
If your target dlls are COM components, you have two options: One is that you can convert the COM component to registration-free so that it can be called by Windows store application. Another way is that you can convert it to WinRT component.
You can use CoCreateInstanceFromApp function to load COM components. But generally it results in error code REGDB_E_CLASSNOTREG. To load COM components successfully, here I provide a sample to demonstrate the steps:
1. Create a COM component and generate the manifest and headers. The sample I demonstrate is from our MSDN link In-process C++ COM server (CppDllCOMServer) . Download and compile the sample project to generate tlb and dll files.
2. Copy the generated CppDllCOMServer.tlb and the header files SimpleObject.h and CppDllCOMServer_h.h into the same folder as the DLL. And run following commdn in the Developer Command Prompt to genereate Application.manifest file:
>mt.exe -tlb:CppDllCOMServer.tlb -dll:CppDllCOMServer.dll -identity:"CppCOMServerDLL, version=1.0.0.0, processorArchitecture=x86" -out:Application.manifest
3. Add the threadingModel attribute to the comClass element and set the value to Apartment.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="CppCOMServerDLL" version="1.0.0.0" processorArchitecture="x86"></assemblyIdentity>
<file name="CppDllCOMServer.dll" hashalg="SHA1">
<comClass threadingModel="Apartment" clsid="{3739576F-F27B-4857-9E3E-8BAAA2A030B9}" tlbid="{D180D63C-6728-42CE-B953-885CB6E57F01}"></comClass>
<typelib tlbid="{D180D63C-6728-42CE-B953-885CB6E57F01}" version="1.0" helpdir="" flags="HASDISKIMAGE"></typelib>
</file>
<comInterfaceExternalProxyStub name="ISimpleObject" iid="{0AE6650F-C9D2-46B2-80C8-7FE10654CC93}" tlbid="{D180D63C-6728-42CE-B953-885CB6E57F01}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"></comInterfaceExternalProxyStub>
</assembly>
4. Edit the SimpleObject.h and remove the Protected and Private entries of the SimpleObject class. It should end up like the following.
class SimpleObject : public ISimpleObject
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IDispatch
IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo);
IFACEMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo);
IFACEMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid);
IFACEMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispParams, VARIANT *pvarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
// ISimpleObject
IFACEMETHODIMP get_FloatProperty(FLOAT *pVal);
IFACEMETHODIMP put_FloatProperty(FLOAT newVal);
IFACEMETHODIMP HelloWorld(BSTR *pRet);
IFACEMETHODIMP GetProcessThreadID(LONG *pdwProcessId, LONG *pdwThreadId);
SimpleObject();
};
5. Create a C++ Windows store application and add following files to the project:
-
- CppDllCOMServer.dll
- SimpleObject.h
- CppDllCOMServer_h.h
- Application.manifest
Set the content to True and File Type to Document for CppDllCOMServer.dll and Application.manifest.
6. Add a button in the Window store application with the name tbnTest. In a Button Click event handler, call the COM component using following code:
- //Create an instance of the component
//Interface for ISimpleObject: {0AE6650F-C9D2-46b2-80C8-7FE10654CC93}
//CLSID for SimpleObject is {3739576F-F27B-4857-9E3E-8BAAA2A030B9}
const GUID CLSID_SimpleObj = { 0x3739576f, 0xF27b, 0x4857, { 0x9e, 0x3e, 0x8b, 0xaa, 0xa2, 0xa0, 0x30, 0xb9 } };
const IID ISimpleIID = { 0x0AE6650F, 0xC9D2, 0x46b2, { 0x80, 0xC8, 0x7F, 0xE1, 0x06, 0x54, 0xCC, 0x93 } };
HRESULT hr;
MULTI_QI arrMultiQI[1] = { &ISimpleIID, NULL, 0 };
//Use the new CoCreateInstance API
hr = CoCreateInstanceFromApp(CLSID_SimpleObj, NULL, CLSCTX_ALL, NULL, 1, arrMultiQI);
ISimpleObject *pSimpleObj = reinterpret_cast<ISimpleObject*>(arrMultiQI[0].pItf);
BSTR myBSTR = nullptr;
//Call the COM component to get a BSTR
hr = pSimpleObj->HelloWorld(&myBSTR);
size_t nlen;
nlen = (wcslen(myBSTR)) + 1;
wchar_t* sMyString = new wchar_t[nlen];
wcscpy_s(sMyString, nlen, myBSTR);
String^ s = ref new String(myBSTR, nlen);
::SysFreeString(myBSTR);
//display the string
btnTest->Content = s;
To pass the compile, you should add two includes And add oleaut32.lib to the link dependencies:
#include <SimpleObject.h>
#include <OleAuto.h>
If you cannot make the COM object registry free, you should write a WinRT component. Actually, writing a WinRT component is just like defining a C++ class (though internally it’s still COM based.) I believe that you would love to write WinRT components instead of COM components after you try it. To implement Add function in your WinRT component, you just need to add following fragment in your WinRT component project:
public ref class TestClass sealed
{
public:
double Add(double a, double b)
{
return a+b;
}
}
It’s very neat, isn’t it? The different with the standard C++ class is that it’s a “sealed” “ref” class which are the keywords of CX extension. Another thing you should take care is that only the WinRT types can cross the Application Binary Interface (ABI). But under the WinRT surface, you can still use any C++ types freely.
.NET components also can’t be referenced by Windows store apps. You should create a WinRT component in C#/VB and then copy/past your .NET code to the component. The managed component code is much similar as the native WinRT component. Here I demonstrate the code which implements Add function in C# language for the comparison:
public sealed class TestDLL
{
public double Add(double a, double b)
{
return a+b;
}
}
- Refactor the code which is not supported by Windows Store apps.
When you perform the second step, you may encounter a lot of errors in compile time. Most of them is caused by the incompatible APIs. Since Windows Store apps just support a subset of Win32 APIs and .Net library, the incompatible APIs can be found by the compiler if they are called directly in the Windows Store apps or WinRT components. Alternatives to Windows APIs and Converting your existing .NET Framework code provide the alternative interfaces to replace these incompatible APIs.
But it’s not enough. The compile doesn’t check the APIs which is called in the linked or loaded dlls. You may encounter unexpected error at the runtime. Windows App Certification Kit(WACK) is a great tool to find out all the incompatible APIs in your application. Besides it, it can also help to find out possible performance issue and crash issue. So please make sure that you have passed the test of Windows App Certification Kit before submitting your applications to Windows Store. Otherwise your application will not get into the Store.
Conclusion
In this article we discussed how to port desktop applications to Windows store applications. The task is not trivial. You should resign your UI and wrap your existing business logic code to WinRT components. You should also replace a lot of incompatible APIs which can be detected by WACK tool. But it is all worth it. After the porting, your application can be viewable and download to millions of devices through Windows Store.
Comments
- Anonymous
March 13, 2014
The comment has been removed - Anonymous
March 13, 2014
You should make sure that your COM compoents is registration-free and it doesn't call any unsupported API.