Recently, I developed a Windows C++ application, which is a mini game client framework that has the following features:
- Contains a launcher program that is very small ( ~ 2M ), with all other modules downloaded when needed.
- Support dynamic contents on web sites, especially flash content.
- Support Windows XP SP3 and above. As there are many XP users in China.
- Support downloading & launching applications as required, such as Unity Standalone games.
Although it's already online and downloaded by millions of users, there are some problems I have encountered when developing that I would like to share:
Before that, however, we have to install the "Windows XP support for C++" component first:
The one I choose is Pascal Casing.
- Contains a launcher program that is very small ( ~ 2M ), with all other modules downloaded when needed.
- Support dynamic contents on web sites, especially flash content.
- Support Windows XP SP3 and above. As there are many XP users in China.
- Support downloading & launching applications as required, such as Unity Standalone games.
Although it's already online and downloaded by millions of users, there are some problems I have encountered when developing that I would like to share:
Why C++ not C#
We use C++ as our developing language, which is far less efficient in productively and maintainability then .NET such as C#.
Why?
We can't choose .NET. The main reason is that we want the application to be as small as possible while as portable as possible. Using C# means we need to install .NET (~ 200M) first if we want users to run our app on Windows XP. That will surely mean we were going to lose a lot of XP users. And According to a survey made in Jan 2017, the install rate of Windows XP in China is still as high as 17.79%.
Targeting Windows XP
For our application to be able to run on Windows XP, we don't need to install older versions of Visual Studio such as VS 2008. We can instead use the latest Visual Studio and selecting a XP releted platform toolset.
Before that, however, we have to install the "Windows XP support for C++" component first:
Provided all our dependent libraries have been built with similar options, our result executable file can be run on Windows XP SP3 now. It can't be run on XP SP2 or earlier according to: Configuring Programs for Windows XP.
C++ runtime support
Along with the Windows XP platform toolset, the C Runtime Library (CRT), C++ Standard Library, Active Template Library (ATL), Concurrency Runtime Library (ConCRT), Parallel Patterns Library (PPL), Microsoft Foundation Class Library (MFC), and C++ AMP (C++ Accelerated Massive Programming) library include runtime support for Windows XP and Windows Server 2003. For these operating systems, the minimum supported versions are Windows XP Service Pack 3 (SP3) for x86, Windows XP Service Pack 2 (SP2) for x64, and Windows Server 2003 Service Pack 2 (SP2) for both x86 and x64.
Third Party Libraries
Life is painful if we can't utilize third party libraries to help us when writing code.
For Java developers, there are tons of publicly available libraries. And if we are using project management tools such as maven or gradle, we can include these libraries with a few lines of code, then they will be downloaded automatically.
For C++ programmers, life is not so easy. There are multiple dimensions that affect compatibility of a binary library:
- platform toolset
- runtime library: Multi-threaded/Multi-threaded DLL / Multi-threaded Debug/ Multi-threaded DLL Debug/
- release or debug
- 32 or 64 bits
- static of dynamic
- Linux or Windows...
And most libraries don't release binary distribution, rather they will ask us to build from source code.
So, including one extra libraries requires a lot of work as we have to:
- setup the building environment, which may require installing extra tools or libs recursively.
- tune the building variables to suit our requirements.
- build it
- copy includes headers and binaries to our project
- modify our project makefile to use these header files and binaries.
Sometimes we may just stuck at the first step. One of my colleagues once spend a whole week to build a old version CEF, but failed to do so. Finally, with lots of search on google, I found it is contained in one version of Unreal Engine (Uploaded here) . That totally saved his life.
So I would include as less libraries as possible. Here are some libraries I used in this project:
- Poco, which I depend a lot and has relatively good documents.
- Duilib, for building UI. It's buggy, poor documented but easy to setup and very light weighted.
- CEF, I use the very old version 3.2357.1291, which supports NPAPI.
I didn't use boost, as I was intimidated by both is library size and result exe size.
Character set
One decision we have to make before writing any code is choosing the character set of our project.
There are two options in Visual studio:
- Use Multi-Byte Character Set (or MBCS)
- Use Unicode Character Set
To make that decision, the following issues have to be considered:
Effects of character set option
- There are two versions of Windows API: xxxxA and xxxxW. E.g. MoveFileExA and MoveFileExW. The former requires a char *, while the later requires wchar_t *
- The character set option of visual studio only affect the default system API we use. For example if MBCS is chose, MoveFileEx is defined as MoveFileExA, so calling MoveFileEx will end up calling MoveFileExA. We can however call MoveFileExW directly even if we choose MBCS.
- On Windows, char in system API including standard lib means MBCS
- On Linux, char in system API including standard lib means utf8
- Different libraries have different means for char, e.g.:
- Poco interpret it as UTF-8 encoding by default.
- CEF as UTF-8 too
Meaning of wchar_t ( wchar_t * and std::wstring)
- By contract Windows and most libraries interpret wchar_t as UTF16.
There is an article discussing this, which suggest the following five rules:
- First rule: Use UTF-8 as the internal representation for text.
- Second rule: In Visual Studio, avoid any non-ASCII characters in source code.
- Third rule: Translate between UTF-8 and UTF-16 when calling Win32 functions.
- Fourth rule: Use wide-character versions of standard C and C++ functions that take file paths.
- Fifth rule: Be careful with third-party libraries.
Personally, I don't quite agree with it. For one thing, if we follow rule 1, rule 5 with be easily broken without notice. Because the stdlib treat char as MBCS and all libs depended on it, so it's very dangerous passing a UTF8 string to any third party library. No compiler warning is even given.
So for windows programs, I would prefer use wchar_t, even though it makes code more ugly with those L prefixes.
Build files
When you have to split your project into modules and make dependencies between them, or when you need to build 32/64, debug/release versions, or when you need easily and consistently add third party libraries, you can't rely on Visual Studio to maintain your project. It's simple too much work and too error prone.
However unlike Java community where there are many options for project management, there is little we can choose for c++ projects.
When the project begins, I use VS directly to manage my projects. Gradually, it becomes more and more painful. So I turned to CMake.
CMake is a powerfully but hard to learn build tool for C++. You will need to find examples to learn it and write product level scripts. The official document is affluent, detailed but hard to learn.
Naming Convention
There are many naming conventions in C++, which makes our code a mass if we introduce libraries with different naming conventions. This also happened to this project:
- Poco in Pascal Casing
- CEF in Pascal Casing while chromium it depends on in underscore case
- Duilib in Pascal Casing
Conclusion
So with all these problems, we can see it's complicated to develop in C++ application that are compatible in various Windows platforms and languages. if can have options I won't chose C++ in the first place.



