Writing shared code for Windows Store and Win32 desktop apps

(continued from part 1 and part 2)

Windows Runtime (WinRT) APIs

There are a number of areas of the system where you must use WinRT APIs to access the required functionality for Windows Store apps, and there is no Win32 equivalent included in the Windows Store apps API family. This code is not a good candidate for dual-use scenarios, but there are times when it makes sense to house both the WinRT and Win32 implementation in the same module. Generally you should prefer to have the client application handle this platform-specific functionality and provide the information needed to your dual-use shared code as parameters, but this is not always convenient or practical.

This scenario is one where you have to make use of the WINAPI_FAMILY macro to determine if you are building for Windows Store apps or Win32 desktop apps. There are a number of ways to do this, and most of them are subtly incorrect. The system headers make extensive use of the WINAPI_FAMILY_PARTITION macro available in <winapifamily.h>, however, as the exact make-up of partitions is subject to change with the introduction of new families over time, the recommendation is to only take dependencies on the FAMILY macros.

 #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
// This code is for Win32 desktop apps
#else
// This code is for Windows Store or Windows phone apps
#endif

In some cases when writing code for Windows phone apps, you may need to handle a difference from Windows Store apps. In this case, you can use this guard.

 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
// This code is for Windows phone apps only
#endif

Alternatively, you may want to support contexts without the Windows 8.0 SDK such as using the Windows 7.1 SDK for Windows XP support. In this case requiring an explicit build configuration (such as /DBUILDING_FOR_DESKTOP in the project settings for Win32 desktop usage) is the easiest and cleanest solution.

 #ifdef BUILDING_FOR_DESKTOP
// This code is for Win32 desktop apps
#else
// This code is for Windows Store apps
#endif

The __cplusplus_winrt control define can be a useful way to isolate C++/CX language extensions as well, and this define is active whenever building with /ZW (the default for Windows Store app projects). It is, however, possible to be building for a Windows Store app without the /ZW switch (such as in a static library), so the #ifndef __cplusplus_winrt case can still be for a Windows Store app. Thus it is not a substitute for the logic above with the WINAPI_FAMILY control define for determining when building for the Windows Store vs. Win32 desktop.

 #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
// This code is for Win32 desktop apps
#elif !defined (__cplusplus_winrt)
#error This module requires WinRT C++/CX language support (/ZW)
// This code is for WinRT Windows Store apps
#endif

For example, here is some utility code for getting access to the proper path for a temporary file folder. This code builds for Windows Store apps using /ZW and for Win32 desktop apps.

 void GetTemporaryDirectory( wchar_t* dir, size_t maxsize )
{
if ( !maxsize ) return;
*dir = 0;
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
DWORD nChars = GetTempPath( maxsize, dir );
if ( nChars > 0 )
dir[nChars-1] = '\0'; // Trim trialing '\'
else
*dir = 0;
#else // Windows Store WinRT app
auto folder = Windows::Storage::ApplicationData::Current
->TemporaryFolder;
wcscpy_s( dir, maxsize, folder->Path->Data() );
#endif // WINAPI_FAMILY_PARTITION
}

Here is a similar function that gets the application local data folder for the Windows Store app using /ZW or for Win32 desktop apps using the Windows Vista IKnownFolder API.

 void GetApplicationDataDirectory( wchar_t* dir, size_t maxsize )
{
if ( !maxsize ) return;
*dir = 0;
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
ScopedObject<IKnownFolderManager> mgr;
HRESULT hr = CoCreateInstance( CLSID_KnownFolderManager,
nullptr, CLSCTX_INPROC_SERVER, IID_IKnownFolderManager, (LPVOID*)&mgr );
if (SUCCEEDED(hr))
{
ScopedObject<IKnownFolder> folder;
hr = mgr->GetFolder( FOLDERID_LocalAppData, &folder );
if (SUCCEEDED(hr))
{
LPWSTR szPath = 0;
hr = folder->GetPath( 0, &szPath );
if (SUCCEEDED(hr) )
{
wcscpy_s( dir, maxsize, szPath );
wcscat_s( dir, maxsize, L”\\MyUniqueApplicationName”);
CreateDirectory( dir, nullptr );
CoTaskMemFree( szPath );
}
}
}
#else // Windows Store WinRT app
auto folder = Windows::Storage::ApplicationData::Current
->LocalFolder;
wcscpy_s( dir, maxsize, folder->Path->Data() );
#endif
}

Note: This code assumes that CoInitialize(Ex) was already called by the client application.

Remember that Windows Store apps have a very restricted set of security privileges and access to the hard disk is tightly controlled. You should assume you only have read access to the files included in the AppX package for the Windows Store or the install location in “Program Files” for Win32 desktop apps. You should assume you only have read/write access to a temporary folder, the application local data folder, the application roaming data folder, and only other folders in special permissions scenarios (which may be read-only instead of read-write).

Windows::Storage:: ApplicationData property IKnownFolder equivalent SHGetKnownFolderPath equivalent
LocalFolder FOLDERID_LocalAppData
+ unique folder name
CSIDL_LOCAL_APPDATA
+ unique folder name
RoamingFolder FOLDERID_RoamingAppData
+ unique folder name
CSIDL_APPDATA
+ unique folder name

Note: There’s no direct equivalent to LocalSettings or RoamingSettings for Win32 desktop apps.

See File access and permissions in Windows Store apps

Resources

Scott Meyers. More Effective C++. Addison-Wesley, 1996. Print.

C++: New Standard Concurrency Features in Visual C++ 11, MSDN Magazine (March 2012)

X64 Primer: Everything You Need To Know To Start Programming 64-Bit Windows Systems, MSDN Magazine (May 2006)