Writing shared code for Windows Store and Win32 desktop apps
Introduction
Apps written for the Windows Store make use of the Windows Runtime (WinRT) and a restricted subset of Win32 APIs located in the core API family (indicated by WINAPI_FAMILY
set to WINAPI_PARTITION_APP
). Traditional Win32 desktop apps have access to a larger desktop API family (indicated by WINAPI_FAMILY
set to WINAPI_PARTITION_DESKTOP
), but this is subject to various levels of OS support required for each function. These two taken together can make it challenging to write shared code libraries and helper functions that can successfully compile for both Windows Store apps and Win32 desktop applications supporting Windows Vista, Windows 7, and Windows 8.
In general, applications should be written to target either the Windows Store or the Win32 desktop. Windows Store apps make use of a distinct UI, input, system-integration, and presentation model which is not supported for Win32 desktop applications even on the Windows 8 Desktop. Targeting the Windows RT (a.k.a. Windows on ARM) platform requires writing a Window Store app, while targeting down-level platforms such as Windows Vista and Windows 7 require writing Win32 desktop apps. Trying to address both of these with the same EXE is not possible, and each will have significant platform-specific code.
The purpose of this series of posts is to talk about the overlap, and how developers creating shared libraries and game middleware can write C++ code that will successfully compile for both platforms.
Note the majority of this article applies to Windows phone 8 using the Windows phone SDK 8.0. Windows phone 8 development makes use of a WINAPI_FAMILY
of WINAPI_FAMILY_PHONE_APP
.
Compiler Toolsets and SDK Selection
To author Windows Store apps, developers must use Visual Studio 2012 which includes the Windows 8.0 SDK. This same toolset can be used to target Win32 desktop apps for Windows 8 (Desktop), Windows 7, and Windows Vista. For this article, the focus is on using this compiler toolset.
Note that with careful coding, it is possible to also support Visual Studio 2010 with the Windows 8.0 SDK for building Win32 desktop apps. In some specific cases some extra functionality is needed that is otherwise handled by Visual Studio 2012’s C++11 Standard Library, and this means restricting language feature use to VS 2010’s C++0x support and avoiding the use of C++/CX language extensions.
C++11 Language Feature | VS 2010 | VS 2012 |
nullptr | ü | ü |
static_assert | ü | ü |
override / final * | ü | ü |
Lambda expressions | ü | ü |
Rvalue references | ü | ü |
decltype | ü | ü |
auto | ü | ü |
Strongly typed enumerations | ü | |
Forward declared enumerations | ü | |
Ranged-based for loops | ü | |
Initializer lists | û | û |
Variadic templates | û | û |
* = In VS 2010, final
was implemented as sealed
Note: A future update to Visual C++ will include support for additional C+11 features including initializer lists, variadic templates, uniform initialization, function template default arguments, delegating constructors, explicit conversion operators and raw strings.
Use of the older standalone DirectX SDK is not recommended or supported for Windows Store apps. It includes many legacy technologies that are not supported for this platform, and thus their use complicates the goal of ‘dual-use’ coding. See the blog posts “Where is the DirectX SDK?” and “DirectX SDKs of a certain age” for more information.
C++11 Standard Library
The majority of the C++11 Standard Library is supported for both Windows Store apps and Win32 desktop apps. This provides a large breadth of functionality that is common and safe to use for ‘dual-use’ scenarios.
C++11 header | VS 2010 | VS 2012 |
<array>, <memory>, <random>, <regex>, | ü | ü |
<stdint.h>, cstdint | ü | ü |
| ü | ü |
cbegin(), cend(), crbegin(), crend() | ü | ü |
<forward_list> | ü | ü |
<algorithm> and <exception> updates | ü | ü |
<allocators> | ü | ü |
<codecvt> | ü | ü |
<system_error> | ü | ü |
emplace(), emplace_front(), emplace_back(), etc. | ü | |
<chrono> | ü | |
<ratio> | ü | |
<scoped_allocator> | ü | |
<atomic>, <condition_variable>, <future>, <mutex>, | ü | |
<intializer_list> | û | û |
<cuchar>, <cfenv>, <ctgmath>, <cstdalign>, <cstdbool> | û | û |
The majority of Visual C++ functions in the C Runtime are available for Windows Store apps, but there are some specific headers which are not fully available.
Visual C++ header | Notes |
agents.h | The majority of the Concurrency Runtime (ConcRT) is available. There is, however, no support for the advanced scheduler (i.e. schedule groups, contexts) |
concrtrm.h | The Concurrent Runtime (ConcRT) resource manager is not available to Windows Store apps. |
conio.h | No functions in this header are available |
ctype.h, cctype |
|
direct.h | Only |
io.h |
|
locale.h, clocale | Obsolete locale functions are not available |
malloc.h |
|
mbctype.h, mbstring.h | All multi-byte ( |
process.h | Most process and DLL related functions are not available. |
stdio.h, cstdio |
|
stdlib.h, cstdlib | POSIX/DOS-style environment variables and related functions & types are not supported for Windows Store apps. There is also no equivalent for |
tchar.h | The |
time.h, ctime | System-time functions ( |
wchar.h, cwchar |
|
wctype.h, cwctype | Obsolete |
Machine Architectures
Windows Store apps should compile for Windows x86 (32-bit), Windows x64 (64-bit) native, and Windows RT (ARM). Win32 desktop apps should compile for Windows x86 and Windows x64 native. Most C/C++ code should work fine across all platforms if using platform-neutral types.
Use portable types. Use
size_t
,ptrdiff_t
, and the various<stdint.h>
(<cstdint>
) types (i.e.int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, intptr_t,
anduintptr_t
).Group pointers in structures and classes. Most data types do not change size when moving to x64 native, but pointers become 8 bytes (known as the “LLP64” data model). The default pack setting for x64 is 16 rather than 8 to ensure structures are padded to a natural alignment including pointers. Mixing pointers with other data types in structures results in more padding than would happen if the pointers were grouped together.
Prefer C++ style casts. Use of
const_cast<>
,static_cast<>
, andreinterpret_cast<>
rather than C-style casts can help highlight potential pointer-truncation issues more easily.Use maximum warnings (
/Wall
). A number of warnings that tend to highlight 64-bit portability issues include C4302 and C4826 are off by default. You can disable specific warnings to reduce ‘noise’ as they are identified by#pragma warning
or/wd
.Use
/analyze
. Static code analysis will highlight a number of issues, particularly using the incorrect printf format specifications.
Inline assembly is not supported for x64 native or ARM compilation, so it should be avoided generally. You can make use of intrinsics instead. Avoid using MMX™ intrinsics (i.e. those from the mmintrin.h header or that operate with the __m64
type) to ensure the same code works for both x86 and x64 native. For ARM, there is a full set of intrinsics available in armintr.h
and arm_neon.h
.
The ability to write standalone assembly for all machine architectures is not currently supported for Windows Store apps, and is therefore not recommended for ‘dual-use’ or Windows Store app scenarios.
When writing architecture-specific code, make use of the _M_IX86
(32-bit), _M_X64
(64-bit), and _M_ARM
machine architecture defines for conditional compilation. All three are “Little Endian” platforms (Windows RT included).
Note: The VS 2012 toolset fully supports x86, x64, and ARM. VS 2010 has no support for ARM targets.
Exception-Safe Coding
Windows Store apps make use of C++ exception handling and are compiled with /EHsc
. Many Win32 desktop applications use HRESULT
s and do not enable exception handling of any kind, although some do use it. Dual-use shared code can use HRESULT
s or other error codes and leave the decision to use exception handling to the client code. (See DirectXTex for an example of this approach.) Alternatively, dual-use shared code can throw either C++ standard exceptions or Windows Store app Platform
exceptions through specific compiler techniques. (See DirectXTK for an example of this approach.)
Since dual-use code can be used in the context of exception handling, it is strongly recommended that you make use of ‘exception-safe’ coding practices. C++ exception handling takes advantage of the language and ensures that objects are properly destructed when leaving scope normally or when processing an exception. When using the C++11 Standard Library, those containers are already written to be ‘exception-safe’.
The main area where this impacts ‘dual-use’ shared code and C++ code in general is when allocating resources. The guidance here is to never rely on calling delete, delete [], CloseHandle, Release,
etc. directly but have the destructor of a class instance handle it automatically. This technique is known as Resource Acquisition Is Initialization (RAII). This ensures that the code will behave well both in normal operation and in the cases where exception handling is used. The C++11 Standard Library provides a number of classes that make implementing this pattern fairly straight-forward.
Traditional C++ | Exception-safe C++ |
MyObject *obj = new MyObject; |
-or-
|
BYTE* buffer = new BYTE[ 2048 ]; |
-or-
|
float* buffer = _aligned_malloc( 2048, 16 ); | struct aligned_deleter |
HANDLE h = CreateFile(…); | struct handle_closer |
CRITICAL_SECTION cs; | std::mutex m; |
ID3D11InputLayout* inputLayout = NULL; |
-or- device->CreateInputLayout(…, inputLayout.ReleaseAndGetAddressOf() ) |
Note: When building with VS 2012 or VS 2010 with the Windows 8.0 SDK for both Win32 desktop applications and Windows Store apps you can use Windows Runtime Library’s ComPtr
. This is similar to ATL’s CComPtr
.
Note: When passing these objects to other functions, you can pass raw pointers and use .get()
on the memory control object on each call, or pass the smart pointer object. When using smart pointer objects as parameters, pass them by constant reference, similar to other STL containers, in order to avoid additional temporary copies and to avoid excessive reference count increment and decrement cycles.
The use of this ‘exception-safe’ pattern has the added benefit of ensuring you do not need to make use of explicit try / catch
blocks in your code to handle resource cleanup. This contributes to keeping ‘dual-use’ code agnostic to the use of Exception Handling while still being ‘exception-safe’ when it is used.
(continued in part 2)