Extreme Guide C++ Builder
Extreme Guide C++ Builder
Extreme Guide C++ Builder
www.bcbjournal.com
ISSN 1093-2097
Edited by Malcolm Smith, Malcolm Groves, Curtis Krauskopf, Remy Lebeau, Bob Swart, and Damon Chandler
BBuuiillddiinngg aa C
Cuussttoom
m
M
Miiggrraattiinngg ttoo MMuullttii--TToouucchh
U
Unniiccooddee,, PPaarrtt II SSyysstteemm
Josh Kelley Byeongcheol Nam and
Ki-Tae Bae
M
Miiggrraattiinngg ttoo
U
Unniiccooddee,, PPaarrtt IIII LLiibbrraarryy PPrroobblleem
mss
Josh Kelley Curtis Krauskopf
MJFAF
Special Offer
P a ge 2 8
IN THIS ISSUE: 4
12
4 DataSnap 2010
Bob Swart
Migrating to Unicode,
12 Part I
Josh Kelley
19
Migrating to Unicode,
19 Part II
Josh Kelley
Changing C++Builder 29
29 2010’s Default Save
Directory
Curtis Krauskopf
Building a Custom 32
32 Multi-Touch System
Byeongcheol Nam and Ki-Tae Bae
40 Library Problems
Curtis Krauskopf
40
Developer’s Poll ........ 45
Contributors ............. 46
W
elcome to this special issue of the
C++Builder Developer‘s Journal on
C++Builder 2010.
Next, ByeongCheol Nam and Ki-Tae Bae de-
C++Builder continues to be the tool of choice for
scribe how to develop a multi-touch system using
developers seeking a powerful, yet easy-to-use
C++Builder 2010. The authors present a system which
application-development solution for Windows.
uses an infrared sensor as a multi-touch sensing de-
Embarcadero C++Builder 2010 represents the latest
vice; however, for those without such a device, you
and greatest release of this award-winning tool.
can still test the code by using two mice.
This special issue discusses several important
Finally, Curtis Krauskopf closes the special issue
aspects of C++Builder development which are unique
with an article on his experiences in porting libraries
to version 2010. Even more, many of the techniques
from BCB 6 to C++Builder 2010. Curtis discusses how
presented in this issue are relevant to all developers
to overcome a significant po-
who upgrade to a newer
tential problem when linking
version of C++Builder.
This special issue discusses static libraries into a
Bob Swart opens the
several important aspects of C++Builder 2010 project.
issue with an article on how
Thanks to the authors and
to use C++Builder 2010 to C++Builder development which editors who have made this
build DataSnap 2010 server are unique to version 2010. special issue possible. A spe-
and client applications. Bob
cial thanks is particularly due
discusses several unique
to our loyal readers who have
aspects of DataSnap 2010, including server methods.
continued to support the C++Builder Developer‘s
If you need a robust way of creating a data bro-
Journal since its first publication over 13 years ago.
ker/client application, this is the article for you.
On behalf of the special issue editorial board, I‘d
Next, Josh Kelley provides an important two-part
like to welcome you to this special issue, and I sincere-
series on migrating a C++Builder application to Un-
ly hope you find the material useful. Enjoy!
icode—a daunting process for most developers. In his
first article, Josh provides an introduction to Unicode
and discusses various ways to work with Unicode text
in C, C++, the Windows API, and the VCL. Malcolm Smith, Special Issue Lead Editor
In his second article on Unicode, Josh provides MJ Freelancing and Comvision Pty Ltd
specific details and guidelines on how to migrate a
C++Builder application to Unicode. Josh presents sev- Other Special Issue Editors:
eral C/C++ techniques that can be used to help with a Guest Editor: Malcolm Groves
Unicode migration. This two-part series is a must- Embarcadero Technologies
read for all C++Builder developers. Guest Editor: Curtis Krauskopf
Next, Curtis Krauskopf provides three techniques The Database Managers
for changing the default directory where new Guest Editor: Remy Lebeau
C++Builder 2010 projects are saved. As we all know, Lebeau Software, TeamB, and Indy Project Team
the C++Builder IDE insists on saving new projects
Guest Editor: Bob Swart
into the folder ―My Documents\RAD Stu-
Bob Swart Training & Consultancy (eBob42.com)
dio\Projects,‖ whereas most developers prefer to save
their projects in custom locations. Curtis presents Publications Editor: Damon Chandler
three approaches for customizing this location. C++Builder Developer‘s Journal
DataSnap 2010
By Bob Swart
I
n a previous article, I demonstrated how to use
DataSnap in C++Builder 2007 on Windows Vista
to build multi-tier database applications [1]. In the
years that followed, DataSnap has undergone a signif-
icant overhaul. No longer based on COM, the new
DataSnap multi-tier architecture is more lightweight,
but also feels like it is ―not yet finished‖ in all places.
Unfortunately, C++Builder support is one of the the main form to DSForm, and the Caption to
areas which is a lacking compared to the Delphi sup- ―C++Builder DataSnap 2010 Server Container‖, so we
port for the new DataSnap. Delphi 2010 includes two know what‘s going on when we see this form run-
DataSnap Wizards to produce different kinds of ning.
projects, for example, with all components and prop- The ―DataSnap Server‖ category on the Tool Pa-
erties already connected and ready to go. With lette contains the new DataSnap components that we
C++Builder, we still have to do all that manually, and need to use to produce a DataSnap server application
some things will take more effort than others. (see Figure 1). Two components are always needed
In this article I will take you all the way, from for any and all DataSnap Server applications: the
start to deployment, and even to server methods TDSServer and TDSServerClass components.
which out-of-the-box are not possible in C++ for
C++Builder, but with a little help from Delphi (or a
C++Builder helper class) we can still get them as well. TDSServer
Anyway, more than enough to cover lots of time and First, place a TDSServer component on the main form.
pages, so let‘s get started. The TDSServer is the actual ―engine‖ of the DataSnap
Server, and we can start, pause, or stop this engine
using the corresponding commands. By default, the
C++Builder 2010 Enterprise TDSServer has the AutoStart property set to true, to
You will need the Enterprise (or greater) edition of automatically start the engine when the application
C++Builder to be able to participate. Since there are itself starts. There is also a property called HideDSAd-
no special DataSnap wizards in C++Builder, we need min which is set to false by default, but can be set to
to start with a new VCL Forms Application (you can true to hide some of the methods that the DataSnap
also start with a Windows Service Application, but the Server exposes that are not of use for the average Da-
normal VCL Forms Application is taSnap Server application. If you
the easiest one to demonstrate leave this property set to false,
and debug). When saving the then you‘ll see the methods later
project, I save the form (UnitX) in when we connect the DataSnap
the file ServerContainer.cpp. This client to the server.
will be the unit that holds the The TDSServer class has five
DataSnap server communication events that we can hook into, to
components. The project itself is trace what‘s going on with the
saved in DS2010BCBServer- server. The events are OnCon-
.cbproj in my case. nect, OnDisconnect, OnError,
I‘ve set the Name property of Figure 1: DataSnap server components. OnPrepare and OnTrace. The
OnTrace event gives us interesting details on the server class. This instance will handle all requests
commands and data being sent back and forth be- from that source, so it‘s possible to maintain state (i.e.
tween the clients and the server. Even if you do not to ask for ―the next 10 records‖, since the server will
want to trace the communications, you should at least remember the previous position). The default setting
write an event handler for the OnError event, to en- of Session results in the easiest way to build DataS-
sure that the server will be notified (or at least some- nap servers and clients, but not in the most scalable
one will be notified) of an error situation. The TDSEr- architecture. A few thousand concurrent requests in a
rorEventObject has a property, Error, with the ex- time span of only one minute will be tough to handle
ception that was causing the error, so the least we can by any DataSnap server if LifeCycle is set to Ses-
do is present that information: sion (but a lesser problem for LifeCycle set to Serv-
er or Invocation).
void __fastcall TDSForm::DSServer1Error(
We must use the OnGetClass event hander of the
TDSErrorEventObject *DSErrorEventObject)
{ TDSServerClass to specify which class to use as the
ShowMessage(DSErrorEventObject-> DataSnap server class. This should be a data module if
Error->Message); we want to expose TDataSetProvider components—
}
which usually is the case. So, let‘s add a data module
first, and then get back to the form and implement the
Apart from the Error property, the DSErrorEventOb-
OnGetClass event handler.
ject also has properties for the DbxContext, the
Transport, the Server and the DbxConnection to
inspect when needed to get more details on the error. DataSnap data module
To add a data module, do ―File | New | Other‖ (or
TDSServerClass right-click on the DS2010BCBServer project and select
―Add New | Other‖), and in the Object Repository go
Now, place a TDSServerClass component next to it.
to the C++Builder Files category and pick the Data
Assign the Server property of the TDSServerClass to
Module. I‘ve set the name of the new data module to
the TDSServer component.
DSDataModule, and saved it in the file DSData-
The TDSServerClass is the component that tells
Mod.cpp. In the Project Options panel, open the
the DataSnap Server which class type to ―service‖,
Forms panel and remove DSDataModule from the
and how to service this type. The ―how‖ is deter-
Auto-create Forms list. The reason for this is that an
mined by the LifeCycle property, which by default is
instance will be created dynamically at runtime, based
set to Session. The alternative values are Invocation
on the settings of the TDSServerClass‘ LifeCycle
and Server. This LifeCycle property defines how
value, remember?
long an instance of the designated class will ―live.‖
We can now add our data access components on
With LifeCycle set to Server, we will get a sin-
the data module, exposing them using TDataSetPro-
gle instance of the server class used to handle all in-
vider components. For our example, place a
coming requests. This means that the class should be
TSQLConnection, TSQLDataSet and TDataSetPro-
thread safe by not using class data members, but only
vider on the data module. We can use the TSQLCon-
local variables inside methods to avoid threading is-
nection component to connect to a database using
sues. We can use an object like this to count the num-
dbExpress, for example to a SQL Server database (I
ber of incoming requests (hits) for example.
leave it up to the reader to connect to a database here)
The other extreme is the LifeCycle value of In-
like the Northwind example database.
vocation, which means that each incoming request
Make sure to connect the SQLConnection proper-
will result in a new instance of the DataSnap server
ty of the TSQLDataSet to the TSQLConnection com-
class, which handles the request and then gets killed
ponent. Then, we can set the CommandType to
again. This is truly stateless, since the server class will
ctQuery, ctTable or ctStoredProc and specify a
never know anything from a previous request.
SQL command, a tablename or a stored procedure
The default setting of LifeCycle to Session
name in the CommandText property. Using the North-
means that all incoming requests from a single source
wind database, I‘ve used a ctQuery with Command-
(a Session) will get a single instance of the DataSnap
Text as follows:
Then, change the ancestor class of TDSDataModule This will make sure that the TDSDataModule class
from TDataModule to TDSServerModule: type is used as our persistent class.
ful AuthenticationManager property that we can nent on the form, set the Driver property to DataSnap
connect to the TDSHTTPServiceAuthenticationMa- and the driver subproperties should by default be set
nager component. This will enforce the use of HTTP to communicate using TCP/IP over port 211. Note
Authentication (which can be verified in the that we may need to change that port number if we
OnHttpAuthenticate event handler of the modified it at the server side. Apart from that, all we
TDSHTTPServiceAuthenticationManager compo- need is to set the LoginPrompt property to false and
nent). Warning: since the protocol itself is still HTTP, the Connected property to true to see if we can con-
the HTTP Authentication information (username and nect to the server.
password) are being sent in plain text, so anyone with When this is verified, we can add another compo-
a packet sniffer can read them. It would be better to nent, this time from the DataSnap Client category on
use the HTTPS protocol in combination with HTTP the Tool Palette (a category which mainly hosts older
Authentication, but unfortunately, DataSnap 2010 DataSnap Client components, by the way). We need
does not support HTTPS just yet (see QC #81335 for to place a TDSProviderConnection component on the
more details on this missing feature). form, and connect its SQLConnection property to the
In addition to HTTP, DataSnap also supports TSQLConnection component. Next we need to specify
TCP/IP, and this is implemented by the TDSTCPSer- the value for the ServerClassName. This was the type
verTransport component. This one does not come name of the data module at the server side—the type
with built-in authentication, but we can always im- that was used for our Persistent Class as assigned in
plement the OnConnect event handler of the TDSServ- the OnGetClass event of the TDSServerClass, re-
er component and check the values of the DSAuthen- member? The actual type is TDSDataModule, and we
ticationUser and DSAuthenticationPassword Con- need to enter that as the value for the ServerClass-
nectProperties values manually (the same property Name property. It would have been nice to show a
values are used for the automatic HTTP Authentica- drop-down list of all exported server class names, but
tion check), but that‘s a story for another day. alas that‘s not the case.
To continue our demo, place a TDSTCPServer- The next step involves placing a TClientDataSet,
Transport component on the form, and point the connecting its RemoteServer property to the TDSPro-
Server property to the TDSServer component. We can viderConnection component. Then, if all previous
configure this component, specifically we should connections were made correctly (and the Server-
probably change the default port by modifying the ClassName property of the TDSProviderConnection
Port property from the default 211 and use another component is also correct), we can open up the Pro-
value. Make sure to remember that value, since the viderName property of the TClientDataSet, and this
client needs to specify the new port as well, of course. will show a list of exported names of TDataSetPro-
Compile and run the DS2010BCBServer project vider components from the server module. In this
(communicating using TCP/IP over the default port case, we should see only dspEmployees. If you do not
211). If a Windows Firewall warning appears, allow see the choice dspEmployees, you need to verify the
the program to communicate on the private network. previous steps for the DataSnap Client, and also make
That‘s it for the server. Now you can build DataS- sure the DataSnap Server itself is running (and not
nap 2010 clients, connecting using TCP/IP over port blocked by the firewall).
211, using DSProviderConnection to get to the Data- Finally, we can add a TDataSource, TDBGrid,
SetProvider exported from the DSDataModule TDBNavigator and other data-aware controls you
want to use to display the data from the DataSnap
Server in the thin (or smart) client. If we set the Ac-
DataSnap client tive property of the TClientDataSet to true, we will
We can now add a DataSnap Client application, typi- see live data at designtime in the DataSnap Client as
cally by adding it to the same project group. Right- shown in Figure 3.
click on the project group and do ―Add New Project | Warning: Make sure to set the Active property of
VCL Forms Application.‖ I‘ve saved the form in the TClientDataSet back to false when you save and
MainForm.cpp and the project in DSClient. cbproj. close the project. And also ensure the Connected
In order to connect to the (running!) DataSnap property of the TSQLConnection component is set to
server, we should place a TSQLConnection compo-
function TBCBServerModule.EchoString(
Delphi server methods const S: String): String;
begin
Like I said, I will stick to showing you how to use Result := S;
C++Builder to compile Delphi code. For that, we need end;
a .pas and .dfm file, which you may not be able to
end.
create if you have C++Builder 2010 without the full
RAD Studio 2010 (which also includes the Delphi per- This is just an empty Server Module, but we can copy
sonality). Assuming you only have C++Builder, you the TSQLConnection, TSQLDataSet and TDataSet-
need to create a file ServerModule.dfm with the fol- Provider from our TDSDataMod to the TDSServerMo-
lowing contents: dule if you want.
Adding server methods can now be done in the
object BCBServerModule: TBCBServerModule
OldCreateOrder = False Delphi unit, using Delphi syntax. As an example, the
Height = 480 above unit already includes the EchoString function.
Width = 640 You can add more public server methods, and the
end
{$METHODINFO ON}—defined for the parent class
And a file ServerModule.pas with the following lines TDSServerModule—will ensure that these server me-
of Delphi code: thods will be exported from the DataSnap server
DataSnap deployment
The ―old‖ DataSnap servers, based on COM, would
automatically register themselves in the old days (us-
ing C++Builder 6 to 2006). This protocol was changed
with C++Builder 2007, since Windows Vista would
complain if an application tried to register itself while
not being run ―as administrator‖.
With DataSnap 2010, there is no more COM, and
no need (or way) to register the location of the DataS-
nap server. An incoming client connection can no
longer automatically cause the server to be started. In
other words: when deploying a DataSnap server ap-
plication, we should ensure that the server application
is always up-and-running to allow the client(s) to
connect to it.
A regular VCL Forms application may not be the
best server type, which is why you may want to turn
your DataSnap server into a Windows Service Appli-
cation, or into a WebBroker (ISAPI) project. If you do
the latter, then you can use the web module to place
your DataSnap server components (that are currently
residing on the main form). Both a Windows Service
and a Web Server application will be available with-
out the need for someone to logon to the server ma-
chine (and starting the DataSnap server application).
Summary
In this article, I‘ve shown how we can use C++Builder
2010 to build DataSnap 2010 server and client applica-
tions. Although some support is still missing—like the
DataSnap Wizards and direct C++Builder support for
server methods—we can get functional servers and
clients.
Hopefully, Embarcadero will add the missing
functionality of DataSnap 2010 in future releases or
updates of C++Builder.
References
1. B. Swart, ―Database Development with Current subscribers can download this
C++Builder, Part VI: DataSnap,‖ C++Builder Dev. (and all other) journal articles from
Journal, 9 (10), 2005. our website: http://bcbjournal.com
2. http://cc.embarcadero.com/item/27643
Dollar for dollar, the Journal is the most cost-effective way to get the
most from your investment in C++Builder.
Subscriptions start at only $49 per year. Just think, if an article and/or its source
code saves you even a few hours of coding, the savings in time will more than pay
for the cost of a subscription.
Migrating to
Unicode, Part I
By Josh Kelley
O
ne of the biggest changes in C++Builder 2009
and 2010 is the addition of full Unicode sup-
port throughout the VCL and RTL. Unicode
support is a major step forward for the VCL and is
critical for internationalization, but its implementation and time format, sorting orders, support for right-to-
as an absolute requirement within the VCL can seem left layout, culture-neutral icon design, and so on.
like a major obstacle in upgrading to C++Builder 2009 Such issues are beyond the scope of this article.
or 2010.
Fortunately, migrating to Unicode can be much
less daunting than it first appears. The first key reali-
An introduction to Unicode
zation in migrating to Unicode for C++Builder 2009 or This is a brief introduction to Unicode. For a more
2010 is this: You do not have to migrate to Unicode to thorough background, see [1] or [2].
use C++Builder 2009 or 2010. C++ is a diverse lan- Unicode is the international standard for
guage, permitting the use of many libraries and sev- representing text from almost any language in a com-
eral programming paradigms, and while your code puter. Unicode was designed to replace the older AN-
that uses the VCL needs to be Unicode-aware, your SI and ASCII standards and to address those stan-
code that uses the C runtime library, the STL, or the dards‘ disadvantages in dealing with international
Windows API (for example) can continue to use ANSI text.
and only convert to Unicode when passing data to or ASCII (such as was used on the original IBM PCs)
from the VCL. A complete migration to Unicode is was designed for plain English text only. It
obviously necessary to gain the full benefits of inter- represented each character as a number between 32
nationalization, but migrating only the VCL portion of and 127. Space was 32, ‗0‘ was 48, ‗A‘ was 65, and so
your code can drastically simplify the task of upgrad- on. ASCII characters were stored one per byte, but
ing to C++Builder 2009 or 2010 while letting you gain since ASCII only defined characters through 127, byte
the other significant benefits that those versions offer values 128-255 were assigned a number of different
(such as Boost, C++0x support, gestures, and a Ribbon meanings depending on where they were used. (The
control). original IBM PC assigned various accented characters
Whether you choose to make your entire applica- and line drawing characters to the 128-255 range; lat-
tion Unicode-aware or to upgrade only the portions er, various countries assigned letters from their own
that interact with the VCL, there are several C and languages‘ alphabets; and so on.)
C++ development techniques that can make the task The ANSI standard kept characters 32-127 the
much easier. Part I of this article offers an introduction same as ASCII but standardized the use of the 128-255
to Unicode and discusses working with Unicode in C, range into a series of code pages. Each language or re-
C++, and the VCL, while Part II examines some of gion could be assigned its own code page, and as long
these Unicode migration techniques in more detail. as 8-bit textual data was associated with a code page,
Remember, though, that supporting Unicode is it could faithfully represent non-plain-English text.
only part of internationalizing an application. Full (―ANSI‖ is actually a bit of a misnomer; Windows‘
internationalization also includes issues such as date code pages were never standardized by ANSI.)
The Unicode standard was introduced to cover look in development and testing. UTF-16 is the
two major shortcomings with the ANSI standard. preferred encoding for a number of platforms and
First, the ANSI standard made no provision for multi- libraries (including Windows, OS X, Java, and
lingual text that needed more than one code page. .NET).
Second, alphabets such as Chinese have thousands of UTF-8 uses 8-bit code units. Byte values between
characters and so cannot fit in a single code page. 32 and 127 are identical to ASCII, and UTF-8
Unicode provides a standard way of referring to strings can use a terminating NULL character just
any of over 100,000 characters ([2]). Each of these like standard C ASCII strings. Code points above
100,000 unique characters (called code points in Un- 127 are represented by sequences of up to 4 bytes.
icode terminology) is assigned a unique name and a UTF-8 has the advantage of being backwards
unique numeric identifier, which is written as ―U+‖ compatible with ASCII; it has the disadvantage
followed by a 4-digit hexadecimal value. For example, that code units frequently take varying amounts
the code point for the English capital A is named of space (and so operations like ―take the 100th
―LATIN CAPITAL LETTER A‖ and is written character from this string‖ can become much
U+0041. It is important to note that Unicode charac- harder). Because UTF-8 has lower space require-
ters are not guaranteed to be representable by a 16-bit val- ments, it is commonly used for web pages, email,
ue. The portion of Unicode characters that can be and similar stored or transmitted data. It is also
represented in 16 bits is called the Basic Multilingual the preferred encoding for a few platforms and li-
Plane. Characters outside of the Basic Multilingual braries (including the Linux kernel and the GTK
Plane are primarily used for ancient scripts (such as framework).
Egyptian hieroglyphics), musical notation, and rarely
used Han ideograms. An additional complication of Unicode is the exis-
Because code points are abstract entities, Unicode tence of composite (or composable) characters. A let-
provides several encodings to represent these abstract ter with accent marks or diacritical marks can be
code point values in a machine-readable form. An represented in Unicode either as a unique, precom-
encoding describes how to represent each code point as posed code point or as the code point for the plain
one or more code units. (A code unit is simply ―the letter followed by the code point for its diacritical
minimal bit combination that can represent a unit of mark (or possibly multiple code points for multiple
encoded text‖ [3].) The most common encodings are diacritical marks). For example, é can be represented
UTF-8, UTF-16, and UTF-32. in Unicode either as U+00E9 (―Latin small letter e
with acute‖) or as U+0065 (―Latin small letter e‖) fol-
UTF-32 uses 32-bit code units. UTF-32 has the ad- lowed by U+0301 (―combining acute‖).
vantage that each code point takes a consistent The existence of composable characters means
amount of space, but it tends to be wasteful of that a simple binary comparison is not sufficient for
space (since English characters can otherwise be checking two Unicode strings for equality. Because of
represented in 8 bits, and most characters in use composable characters, UTF-16‘s surrogate pairs, and
can otherwise be represented in 16 bits). Because UTF-8‘s variable length encodings, it is no longer va-
of its high storage requirements, UTF-32 is rarely lid to assume that accessing arbitrary characters out of
used. a string or splitting string at arbitrary indexes will
UTF-16 uses 16-bit code units. UTF-16 can work. Operating system APIs and third-party libraries
represent most of the code points in use as a single such as ICU [4] offer routines to help with these com-
code unit. Code points above U+FFFF must be plications. (Windows APIs in particular will be dis-
represented using surrogate pairs, a pair of code cussed below under ―Unicode in the Windows API.‖)
units that together represent a single code point.
UTF-16 has the advantage that most code points
can be represented as single code units and thus
Working with Unicode
take a consistent amount of space; however, the Unlike languages which offer a single built-in string
storage requirements are higher than UTF-8, and data type, C++Builder offers several choices: code can
the possibility of surrogate pairs is easy to over- use C-style characters and strings, or C++ string ob-
jects, or VCL String objects. Each of these has its own
set of Unicode variations. Similarly, the Windows API as \u followed by their 4-digit hexadecimal value.
provides both ANSI and Unicode variants. (For example, L"\u00E9" is a ―Latin small letter e
with acute,‖ and L"\u263A" is a smiley face.)
Unicode data in C Because the C++Builder IDE is fully Unicode-
aware, you can also directly enter Unicode characters
A C-style string is simply an array of char values,
into your code, without resorting to escape characters.
terminated by a NULL byte (also written as „\0‟).
There are a few ways to enter Unicode characters in
Each char takes one byte of storage. (This is guaran-
Windows: for example, you can use Windows‘ built-
teed by the C and C++ standards; as a pedantic note,
in Character Map utility, or you can hold the Alt key
however, one byte of storage is not guaranteed to be 8
while typing ‗+‘ followed by the Unicode character‘s
bits, and some rare platforms use 16 bits or other siz-
4-digit hexadecimal value. The fileformat.info web site
es.) The encoding of a char string is not specified; it
has a complete list of input methods for Windows [5]
could be straight ASCII, or any of the ANSI code pag-
as well as a searchable database of Unicode characters
es, or even UTF-8, although in Windows, it‘s generally
[6]. C++Builder automatically uses the UTF-8 encod-
assumed to be in the system default ANSI code page.
ing for source files containing Unicode characters.
Working with Unicode introduces several more C and
Sprinkling Unicode escape characters throughout
C++ data types for C-style strings:
your code hampers readability, and directly entering
wchar_t: C and C++ apps have traditionally used Unicode characters can sometimes be difficult to work
wchar_t as a replacement for char when working with. An alternative is to use preprocessor #defines
with Unicode strings. wchar_t strings are written to create macros for Unicode characters which your
as L"Hello, world! \u263A". The size of a application needs. Using preprocessor macros instead
wchar_t is compiler-dependent: on Windows, it‘s 16 of const values is often discouraged in modern C++
bits and assumed to contain UTF-16 data; but on development, but using #defines for Unicode and
Linux, it‘s 32-bits; and other platforms may use other string constants have the advantage that they
values as small as 8 bits. If you need truly cross- can be automatically concatenated, at compile time,
platform Unicode-aware code, you may need to without having to clutter your code with concatena-
avoid the built-in types altogether and instead use tion operators.
a third-party library such as ICU [4].
char16_t, char32_t: C++0x (the draft Listing 1: Unicode in C
of the new standard for the C++ lan-
guage) specifies these two new charac- // Various character and string types
ter types for holding UTF-16 and UTF- wchar_t *wchar_msg =
L"Platform-specific-sized text (UTF-16 on Windows)";
32 data, respectively. char16_t values char16_t *utf16_msg =
are written as u"Hello, world! u"Cross-platform UTF-16 text, new with C++0x";
\u263A". char32_t values are written char32_t *utf32_msg =
as U"Hello, world! \u263A". (C++0x U"Cross-platform UTF-32 text, new with C++0x";
#if 0
also allows using u8"Hello, world! char *utf8_msg =
\u263A" to represent UTF-8 data as a u8"UTF-8 text, unsupported by C++Builder";
char array, but C++Builder doesn‘t #endif
support this.) // A smiley face as a Unicode escape code
wchar_t *msg1 = L"Hello, world! \u263A\n";
Of course, having Unicode data types
does little good if you have no way to speci- // A smiley face using a preprocessor macro and string
fy some of the more esoteric Unicode cha- // concatenation.
#define SMILEY_FACE L"\u263A"
racters. Traditional C strings can represent wchar_t *msg2 =
nonprintable characters using predefined L"Hello, world! " SMILEY_FACE "\n";
escape characters like \n (newline) as well // In a real project, such #defines would probably go
// in their own project-wide header file.
as arbitrary hexadecimal values like \x7f.
Similarly, Unicode characters can be written
class name. See Listing 2 for sample char string code RawByteString contains 8-bit (char) data in an
in C++ and its corresponding wchar_t code. unspecified code page. The VCL will avoid apply-
Most text-related classes (including <iostream>) ing any code page conversions to RawByte-
in the C++ Standard Library and in Boost are actually Strings; it becomes the calling code‘s responsibil-
typedefs for template classes. For example, ity to correctly handle code pages issues. Using
std::string is actually a typedef for RawByteString can have several advantages:
std::basic_string<char>, and std::wstring is a since each code page is otherwise a separate com-
typedef for std::basic_string<wchar_t>. Because pile-time type, RawByteString lets you write a
std::basic_string is a template, it can be instan- single routine that can handle any code page; it
tiated on any char-like data type that you wish. This removes any VCL overhead of doing code page
means that, if you need to work with C++0x‘s conversions itself; and it prevents possible loss of
char16_t or char32_t data types, you can use data from automatically converting text data into
std::basic_string<char16_t> instead of std:: encodings that can‘t represent some characters.
string, use std::basic_fstream<char32_t> instead
of std::fstream, and so on. Most member functions of these new string classes
operate just the same as they did for the old pre-
C++Builder 2009 String class. The printf-type me-
Unicode in the VCL
thods (printf(), sprintf(), vprintf(),
This is where things get interesting. Starting with cat_printf(), cat_sprintf(), and cat_sprintf())
RAD Studio 2009, the VCL offers several string classes deserve special mention. Like C‘s wprintf() and
which support ANSI, UTF-8, and UTF-16 encodings: wscanf() functions, their treatment of the “%s” and
“%c” format specifiers depends on whether they‘re
AnsiString corresponds to the old String class. It
contains 8-bit (char) data in the system default called on an AnsiString or UnicodeString instance.
code page. Refer back to Table 1 for details.
(UTF-16) variant of the Windows API is used. 8. ―Linux Programmer‘s Manual: printf(3).‖
The UNICODE macro also affects the use of tchar.h http://www.kernel.org/doc/man-
in writing code that can compile as ANSI or Unicode. pages/online/pages/man3/printf.3.html. Re-
This will be discussed in Part II. trieved 4/12/2010.
The Windows API also includes functions such as 9. ―WG14/N1124 Committee Draft.‖
CharNext(), CharPrev(), and CompareString() that http://www.open-
are capable of dealing with complexities such as com- std.org/JTC1/SC22/wg14/www/docs/n1124.pdf.
posite characters and surrogate pairs. See MSDN [10] Retrieved 4/12/2010.
for details.
10. ―MSDN: String Reference: Functions.‖
http://msdn.microsoft.com/en-
Conclusion us/library/ff468910%28VS.85%29.aspx. Retrieved
In this article, I provided an introduction to Unicode 4/14/2010.
and I presented an overview of how to use Unicode in 11. ―Internationalization for Windows Applications
C, C++, the VCL, and the Windows API. For a more (Windows).‖ http://msdn.microsoft.com/en-
detailed introduction to Unicode, see [11]. us/library/dd318661%28VS.85%29.aspx.
In Part II of this series, I‘ll show you how to mi-
grate your existing C++Builder applications to use
Unicode.
References
1. Joel Spolsky, ―The Absolute Minimum Every Soft-
ware Developer Absolutely, Positively Must Know
About Unicode and Character Sets (No Excuses!).‖
http://www.joelonsoftware.com/articles/Unicod
e.html
2. Wikipedia, ―Unicode.‖
http://en.wikipedia.org/wiki/Unicode
3. The Unicode Consortium. ―Glossary.‖
http://unicode.org/glossary/
4. ―ICU – International Components for Unicode.‖
http://site.icu-project.org/
5. ―How to enter Unicode characters in Microsoft
Windows.‖
http://www.fileformat.info/tip/microsoft/enter_
unicode.htm
6. ―Unicode.‖
http://www.fileformat.info/info/unicode/index.
htm
7. ―MSDN: Size and Distance Specification.‖
http://msdn.microsoft.com/en-
us/library/tcxf1dw6%28v=VS.80%29.aspx.
Get over 12 years of the Journal on CD!
Version 4.0 of our popular Archive
CD contains over 12 years of the
Developer’s Journal
A Monthly Publication Offering Tips & Techniques
for C++Builder
C++Builder Developer’s Journal
(from June 1997 through December
Archive CD
Version 4.0 2008), all neatly presented through
Volumes 1-12
1997-2008 an easy‐to‐use HTML user interface.
You can view the CD contents with
any browser and PDF reader.
www.bcbjournal.com
ISSN 1093-2097
Includes all article source code too!
Order: To order online, visit http://bcbjournal.org/archive_cd.htm
Price: Each CD is
$39.00 plus shipping.
Package: For just
$79.00, get both a CD
and a one‐year
subscription.
Migrating to
Unicode, Part II
By Josh Kelley
P
art I introduced Unicode and covered the vari-
ous options for working with Unicode text in C,
C++, the Windows API, and the VCL. Now, in
Part II, I will specifically discuss how to migrate a
C++Builder application to Unicode. Unicode support. And, even a completely migrated
application may still need to deal with ANSI or UTF-8
when interfacing with legacy file formats or APIs, or
Migrating C++Builder when reading or writing data from or to the disk or
applications to Unicode network.
There are two basic approaches to handling Unicode Regardless of which approach you choose, there
issues when migrating a pre-2009 C++Builder applica- are a number of C and C++ techniques that can be
tion to C++Builder 2009 and above. used to help with a Unicode migration:
1. You can do a complete Unicode migration. Use The Windows API includes functions for convert-
the Windows Unicode APIs instead of ANSI APIs, ing between ANSI and Unicode, and the VCL
replace char with wchar_t, replace std::string provides conversion constructors to easily convert
with std::wstring, and use UnicodeString in- between AnsiString and UnicodeString values.
stead of AnsiString. Depending on how your C The standard Windows header tchar.h includes
and C++ code is written, this could be a major macros designed to let you write code that com-
undertaking. piles as either ANSI or Unicode. This can help
2. You can leave your application using ANSI and when converting code one portion at a time.
convert to Unicode only where it‘s absolutely ne- C++-specific typedefs, and the use of C++ fea-
cessary. Because only the VCL portion of tures such as function overloading, can fill in the
C++Builder requires the use of Unicode, your C gaps left by tchar.h in writing code that compiles
and C++ string manipulation and Windows API as either ANSI or Unicode.
interaction can continue to use char and
std::string. Even the portions of your code that
The C++ concept known as ―shims‖ (as described
interact with the VCL can often get away with in Matthew Wilson‘s Imperfect C++ [1] and as used
continuing to use ANSI strings, thanks to the im- in the STLsoft library [2]), combined with the use
plicit conversions between AnsiString and Un- of C++ templates, can make it simple to write ge-
icodeString that the C++ VCL provides (de-
neric code that works with both AnsiString and
UnicodeString (and C-style strings, and
scribed in more detail below).
std::string, and anything else you care to sup-
Of course, these two approaches aren‘t mutually ex- port).
clusive. You can do an initial, ―quick-and-dirty‖ mi- The use of variadic functions such as printf()
gration using the minimal approach, and then gradu- and sprint() presents a special challenge for mi-
ally implement the complete approach for the por- grating to Unicode, since the compiler is unable to
tions of your application that would most benefit from catch ANSI-versus-Unicode issues with these.
Standalone scripts can be used to transform these Listing 1: Converting to and from Unicode
variadic function calls into a format that the com-
// Sample C++ functions for doing
piler can check and then revert them to their nor-
// Unicode<->ANSI conversions using the
mal format after all issues are addressed. // Windows API. Note the use of
// boost::scoped_array to dynamically
// allocate memory and automatically clean
Converting text to and from Unicode // it up once we‟re done.
Before any migration can proceed, you need to know
std::wstring AnsiToUnicode(const char *s)
how to convert between the various Unicode encod- {
ings and the various ANSI encodings. The two easiest DWORD size = MultiByteToWideChar(CP_ACP,
ways are using the Windows API and using the VCL. 0, s, -1, NULL, 0);
The relevant Windows API functions are Wide- if (size == 0) {
return std::wstring();
CharToMultiByte() [3], which, despite its name, }
converts from UTF-16 to the encoding of your choice boost::scoped_array<wchar_t> buffer(
(UTF-8 or any of the various ANSI encodings); and new wchar_t[size]);
MultiByteToWideChar(CP_ACP, 0, s, -1,
MultiByteToWideChar() [4], which converts from the buffer.get(), size);
encoding of your choice to UTF-16. MSDN has full return std::wstring(buffer.get());
documentation on using these functions. }
Converting using the VCL is even easier. The VCL
std::string UnicodeToAnsi(const wchar_t *s)
provides C++ conversion constructors – constructors {
that can be called with only a single argument – so DWORD size = WideCharToMultiByte(CP_ACP,
that you can construct a UnicodeString from an An- 0, s, -1, NULL, 0, NULL, NULL);
if (size == 0) {
siString or UTF8String, or vice versa. Because C++ return std::string();
conversion constructors are implicitly invoked as }
needed, this also lets you provide an AnsiString boost::scoped_array<char> buffer(
wherever a UnicodeString is needed, or vice versa. new char[size]);
WideCharToMultiByte(CP_ACP, 0, s, -1,
(For example, this lets you assign a UnicodeString to buffer.get(), size, NULL, NULL);
an AnsiString.) See Listing 1 for example code. return std::string(buffer.get());
The ease with which conversions can be done in }
the VCL can have drawbacks. Because the assignment __fastcall TForm1::TForm1(TComponent* Owner)
operators look just like regular assignment and the : TForm(Owner)
conversion constructors can be implicitly invoked, {
your code may be converting between ANSI and UTF- // Implicit Unicode-to-ANSI conversion:
AnsiString s1 = L"Hello, world!";
16 without your even being aware of it. This can add // Implicit ANSI-to-Unicode conversion:
runtime overhead, but more importantly, it can result UnicodeString s2 = "Hello, world!";
in loss of data when converting from UTF-16 to an
// This works without modification in
ANSI encoding that cannot represent all of the Un- // C++Builder 2009, even though Caption
icode characters. Delphi includes a compiler warning // is Unicode and s1 is ANSI.
when this happens (―W1058: Implicit string cast with Label1->Caption = s1;
potential data loss from ‗string‘ to ‗AnsiString.‘‖), but // An implicit conversion from Unicode to
// ANSI. NOTE: This could lose data.
C++Builder will silently accept it. s1 = Label2->Caption;
Ideally there would be an option to have the // Identical to the above, but explicit.
C++Builder compiler emit a warning any time these s1 = AnsiString(Label2->Caption);
ANSI-Unicode conversions are implicitly invoked, but // We can also use a temporary AnsiString
as far as I can tell, no such option exists. If having // or UnicodeString to do Unicode<->ANSI
these functions implicitly invoked is a concern for // conversions.
you, then the only solution is to modify C++Builder‘s MessageBoxA(Handle,
AnsiString(Label1->Caption).c_str(),
header files. "Demo", MB_OK);
To do this, open the file ―include\vcl\dstring.h‖ }
and find the following lines:
takes two arguments (a message and a caption), inline const wchar_t *c_str_ptr_w(
and so a TApplication::MessageBox-style func- const UnicodeString& s)
{
tion would need four overloads (ANSI message return s.c_str();
and caption; Unicode message and caption; ANSI }
message and Unicode caption; Unicode message
inline const wchar_t *c_str_ptr_w(
and ANSI caption). const std::wstring& s)
{
And this is only for a bare bones TApplica- return s.c_str();
tion::MessageBox-style function. Most other VCL }
functions take Strings, not wchar_t*, as parameters;
it would be convenient if we had overloads to do the So far we‘re following the practice described by Mat-
same for our hypothetical MessageBox() replacement, thew Wilson in [6] and [1] and implemented in the
but that adds even more overloads. It would be even STLSoft library [2]. We have a replacement for TAp-
more convenient if we could also support C++ Stan- plication::MessageBox() that we can switch to
dard Library types like std::string or COM-related with a simple search-and-replace (just replace ―Appli-
types like WideString or BSTR. The number of over- cation->MessageBox‖ with ―AppMessageBox‖) and
loads to require all of these combinations of parame- that take any of several types of Unicode arguments
ters for even a single function quickly becomes prohi- without excessive overloads or extra function calls.
bitive. Clearly, a better approach is needed. For the purpose of quickly migrating to Unicode,
The concept of shims, as promoted by C++ author however, it‘s useful to have a TApplica-
and developer Matthew Wilson, offers a solution. tion::MessageBox() replacement that can also take
Shims ―are small, lightweight (in most all cases hav- ANSI arguments. In his article on shims and in his
ing zero runtime cost) components that help types ‗fit work on the STLSoft library, Matthew Wilson explicit-
or align‘ into algorithms and client code‖ [6]. For ex- ly avoids providing shims that convert between ANSI
ample, suppose you had a function that, if given any and Unicode, since those introduce (in his words)
string-like object, gave you a pointer to a C-style ―semi-implicit‖ conversion operations that introduce
string. (Since this function gets a pointer to a wide C- a performance penalty and violate the expectation
style string, and following the convention of Matthew that shims be lightweight. However, as part of a
Wilson‘s STLSoft library, we‘ll call this function C++Builder 2009 or 2010 Unicode migration, it‘s more
c_str_ptr_w().) Then you could write the following useful to accept a (possibly negligible) performance
TApplication::MessageBox() replacement: penalty in order to complete the initial migration as
soon as possible, then address performance and
template <typename T1, typename T2> ―proper‖ Unicode handling as needed.
int AppMessageBox(const T1& Text, Therefore, we need to provide c_str_ptr_w
const T2& Caption, int Flags = MB_OK)
{ shims that take ANSI arguments (const char *, An-
return Application->MessageBox( siString, and UnicodeString). This is harder than
c_str_ptr_w(Text), the previous cases. Our code will have to take the fol-
c_str_ptr_w(Caption), Flags); lowing approach:
}
We need to somehow provide a const wchar_t
Now we simply need to make sure that pointer. We can‘t simply return a const
c_str_ptr_w() results in a valid argument for every wchar_t* from c_str_ptr_w(), because we
parameter type that we use for AppMessageBox(). need to allocate memory to store the results of
The first few parameter types are easy: the ANSI-to-Unicode conversion, and returning
a raw pointer to that allocated memory would
inline const wchar_t *c_str_ptr_w(
const wchar_t *s) constitute a memory leak.
{ We can, however, define a class that contains the
return s;
}
allocated memory and return a copy of (not a ref-
erence to nor a pointer to) that class. The C++
language guarantees that it will properly clean
Reading and writing external data now requires to manipulate narrow character text.
attention both to in-memory storage (RawByteString, 6. Use string shims, C++ overloading, and similar
AnsiString, or UTF8String) and to encodings. (Using techniques as needed to handle remaining ANSI
the system encoding default ANSI encoding may lose versus Unicode issues.
data when transferring from UTF-16. UTF-8 is often
7. Run the type-safe printf() transformer on your
preferable.) Code that writes external data also needs
code to catch any issues with variadic macros.
to consider writing a Byte Order Mark (BOM), a spe-
cial sequence of bytes at the beginning of a file that 8. Review your code for places where you assume
indicates the file‘s endianness and Unicode encoding. that strings can be arbitrarily indexed or split; this
Finally, database tools and database interactions is no longer the case with Unicode.
may require additional attention, depending on your
If you‘re doing a minimal migration:
database‘s capabilities.
Some of these issues are discussed in more detail 1. Check your third-party libraries and make sure
in [9]. that they‘re compatible with C++Builder 2009 and
2010.
Putting it all together 2. Convert your project to C++Builder 2009 or 2010.
Under Project, Options, Directories and Condi-
Unicode is a very broad topic, and even the sub-topic
tionals, make sure that ―_TCHAR maps to‖ is set
of migrating to Unicode for C++Builder 2009 and 2010
to ―char.‖
touches upon many techniques. As a review, here‘s an
overview of one approach to migrating your applica- 3. Replace String with AnsiString.
tion to C++Builder 2009 or 2010: 4. Use string shims, C++ overloading, and similar
First, decide on whether you‘re going to do a techniques to handle interactions between Un-
complete migration (to gain the full benefits of Un- icode VCL code and your ANSI application code.
icode) or a minimal migration (to get up and running 5. Run the type-safe printf() transformer on your
in the new IDE as soon as possible). code to catch any issues with variadic macros.
If you‘re doing a complete migration:
Gradually switch to Unicode, as time and business
1. Check your third-party libraries and make sure cases permit, to gain the full benefits of Unicode.
that they‘re compatible with C++Builder 2009 and
2010.
2. Before switching to C++Builder 2009 or 2010: Contact Josh at [email protected].
a. Replace AnsiString with String.
b. Mark string literals (“Hello”) with tchar.h‘s _T
macro.
References
1. Matthew Wilson, Imperfect C++. Addison-Wesley,
c. Replace C library routines with their tchar.h
equivalents. 2004.
2. Matthew Wilson et. al. ―STLSoft – Robust,
3. Add C++ typedefs such as tstring so that C++
Lightweight, Cross-platform, Template Software.‖
string manipulation will work after the switch to
http://www.stlsoft.org/.
Unicode.
3. WideCharToMultiByte.
4. Convert your project to C++Builder 2009 or 2010.
http://msdn.microsoft.com/en-
Under Project, Options, Directories and Condi-
us/library/dd374130%28VS.85%29.aspx.
tionals, make sure that ―_TCHAR maps to‖ is set
to ―wchar_t.‖ 4. MultiByteToWideChar.
http://msdn.microsoft.com/en-
5. Introduce AnsiString, RawByteString, and
us/library/dd319072%28VS.85%29.aspx.
UTF8String in places where you need to continue
Changing C++Builder
2010’s Default Save
Directory
By Curtis Krauskopf
T
he first time I save a project, the C++Builder
2010 (CB2010) IDE always tries to put it in ―My brary‖ from the right-hand panel.
Documents\RAD Studio\Projects,‖ which I 3. In the Project Manager panel, right-click
never use. Project1.lib...
In this article, I‘ll present three solutions which
can be used to solve this problem, depending on how
badly you want to override the default.
and choose ―Add New | Unit.‖ You should now
see something similar to the following...
Create a small project
Here‘s a quick example so you can follow along in
your own IDE.
First tip
So far, all of the steps have been the same as when
you are saving a regular project. The first tip is that
you can use the ―My Recent Document‖ shortcut on
the save panel to quickly go back to the folder you
previously chose. To see it in action, do these steps:
Third tip
CB2010 has a way to permanently change the de-
fault folder that is used for saving projects.
Building a Custom
Multi-Touch System
By Byeongcheol Nam and Ki-Tae Bae
M
ulti-touch refers to the ability of a touch de-
vice to detect and distinguish three of more
simultaneous touch events. Multi-touch sys-
tems have the potential to provide a highly intuitive
and productive means of user input (e.g., the iPod).
communicates with any multi-touch-enabled applica-
Multi-touch software programming is a future
tion by using a standardized touch protocol (TUIO).
trend. Through a variety of input devices, many fu-
Although Windows 7 has touch support, and al-
ture applications will be required to support multi-
though there are a few multi-touch applications out
touch-based interfaces.
there, most people do not have a multi-touch device.
In this article, we describe a custom multi-touch
Luckily, however, we can develop our own multi-
system which uses RS-232 to communicate between a
touch software by using a system-level user-input
multi-touch device and a C++Builder program. In
layer called Multi-Touch Vista [1], which allows you
turn, the C++Builder program (which we‘ll create)
to use two mice to simulate multiple touches. Multi-
Touch Vista generates a windows touch message and
supports multi-touch programs, such as Microsoft
Paint.
This article is recommended for people:
Interested in multi-touch.
Who want to use multi-touch software
although they don‘t have a multi-touch device.
Who want to build their own multi-touch
system.
Who want to make art using multi-touch.
Overview
Figure 1 shows a diagram of the system that we‘ll
create. The multi-touch device communicates with
our C++Builder program (the multi-touch signal ana-
lyzer) over RS-232. The C++Builder program then
communicates with Multi-Touch Vista using the
TUIO protocol (via a software library called reacTIVi-
sion) [2].
For the purposes of this article, you can use Multi-
Touch Vista with two mice that can simulate multi-
touch. The following section describes how to install
Figure 1: Custom multi-touch system overview. and run Multi-Touch Vista.
Multi-Touch Vista
Multi-Touch Vista is a user input management layer
that handles input from various devices and norma-
lizes it against the scale and rotation of the target
window [1].
First download Multi-Touch Vista from [1]. You
can experience multi-touch to complete this setup
process and you need to prepare two mice too. Let‘s
have fun along the journey.
Run Multi-Touch Vista Figure 3: Several Universal Software HID devices are installed.
Service mode
If the console mode test is gracefully over then you
want to know how to setup service mode. With ser-
vice mode, there is no need to run many executable
files. (However, I recommend you to use console
mode while you are developing something.)
First, launch a command window using the ―Run
as administrator‖ option (see Figure 7).
At the command window, navigate to the Multi-
Touch Vista folder, and then register the service by
typing:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\
installutil.exe /i Multitouch.Driver.Service.exe
The program receives RS-232 data from the touch de- if( Prm.render_touch_point )
{
vice via the TComPort component. When data arrives, m_pTouchPoint->RenderPoint();
the TComPort component‘s OnRxChar event handler m_pTouchPoint->RenderGroup();
calls the following function (the main update func- m_pTouchPoint->RenderTempTouch();
m_pTouchPoint->RenderSelectedTouch();
tion):
}
http://forums.bcbjournal.org
Discuss C++Builder with other developers in our online forum
This is the perfect place to discuss articles/code, and to get quick answers to
your technical and non-technical questions. We also encourage you to browse
our archive of monthly poll questions and to participate in forthcoming polls.
Library Problems
By Curtis Krauskopf
M
y head hurts. I recently upgraded my devel-
opment environment from BCB 6 to
C++Builder 2010 (CB2010). All was going
well and I was becoming comfortable with my new
development environment. Therefore, I decided to database. There were some search hits in the MSDN
port some libraries I‘ve been using to CB2010. Online, Codezone Community and Questions sec-
That went fine too. There were no new compiler tions, but they were for Visual Studio.
warnings and I thought this whole upgrading process Searching the CB2010 include files showed they
would be simple. The regression tests even passed are in the YVALS.H file in the C++ library
without any problems. (~\include\dinkumware\yvals.h). The following
But, that‘s when Murphy came knocking at my code shows the relevant code from YVALS.H.
door [1]. The first sign of problems was when I com-
// Code snippet of yvals.h...
piled the main part of a large program that used sev- /* MULTITHREAD PROPERTIES */
eral newly recompiled libraries. ILINK32 reported #if _MULTI_THREAD
that four externals were unresolved (see Figure 1). _EXTERN_C
These link errors are saying that four functions _CRTIMP2 void _Locksyslock(int);
_CRTIMP2 void _Unlocksyslock(int);
(__InterlockedDecrement, __InterlockedIncrem- long _CRTIMP2
ent, __Unlocksyslock, and __Locksyslock) are used _InterlockedIncrement(long *);
by Library.lib but they are not defined in any of the long _CRTIMP2
_InterlockedDecrement(long *);
libraries or the application‘s modules. The two lead- _END_EXTERN_C
ing underscores on the names told me that these were #else /* _MULTI_THREAD */
probably symbols in the compiler‘s library. #define _Locksyslock(x) (void)0
#define _Unlocksyslock(x) (void)0
#define _InterlockedIncrement(x) \
Researching the problem (++(*x))
#define _InterlockedDecrement(x) \
CB2010 help wasn‘t very helpful because none of (--(*x))
these symbols were in the help‘s index or in the local #endif /* _MULTI_THREAD */
Default Multi-threaded libraries? Figure 2: On the New Console Application wizard, the Multi
Threaded checkbox determines if the application will be single-threaded
I checked all of the panels in the Project Options for
or multi-threaded.
the library. Nothing in there indicated that the library
would be compiled in a multi-threaded mode.
The application was single-threaded because I had project has an XML entry called Multithreaded in a
specified that in the console wizard when I created the PropertyGroup. The PropertyGroup is distinguished
application‘s project (see Figure 2). from the other PropertyGroups in the XML file by
I even double-checked the static library wizard by having this condition:
creating a test library just to see if there was a multi-
Condition="„$(Base)‟!=„„"
threaded setting I missed there. No—there was noth-
ing in the wizard letting me specify a single-threaded
I know it looks strange but it‘s not important to un-
or multi-threaded library.
derstand it right now.
That led me to my next question: how does a
According to the CB2010 help, ―At compile-time,
project know, at compile-time, if it‘s supposed to be
the compiler sets the predefined macro, __MT__, to 1
single-threaded or multi-threaded?
when the -tWM compiler option is set.‖
The code I used to test for __MT__ being defined
How does a project know? looked like this:
I created two console projects. In the console wizard, I
activated the multi-thread checkbox in one project and #include <stdio.h>
deactivated it in the other project.
int main(int, char**)
This time, I knew I had two projects side-by-side {
that had to have something different about them. printf("In " __FUNC__ "()\n");
Checking every panel in the Project Options #ifdef __MT__
printf("In " __FILE__
proved fruitless. It wasn‘t defined anywhere there. ", __MT__ = %d\n", __MT__);
The only place it could be defined, then, would be #else
in the project‘s .CBPROJ file. I opened both .CBPROJ printf("In " __FILE__
files in a separate editor (opening it CB2010 cleverly ", __MT__ not defined\n");
#endif
opens the project in the CB2010 IDE). The .CBPROJ return 0;
files are XML files. }
Figure 3 shows the result of using my favorite text
comparison tool. It shows that a multi-threaded If __MT__ is defined at compile-time, the code displays
its value; otherwise, the
code displays ―not de-
fined.‖
In my test programs,
__MT__ was undefined in
single-threaded project and
it was defined in multi-
Figure 3: A multi-threaded application has an XML property called Multithreaded set to true. A single-
threaded project. Just to
threaded application does not have the XML property at all.
test my theory, I used an
edit feature in my favorite text comparison tool to But wait, there’s more
move the Multithreaded XML line from the multi-
If that was all there was to the problem, then this ar-
threaded project into the single-threaded project.
ticle would be finished. We discovered:
Success! Recompiling the programs showed that
the single-threaded project was now compiling with static libraries default to multi-threaded
__MT__ defined and the multi-threaded project was a static library compiled with the _MULTI_-
compiling with __MT__ undefined. That‘s exactly the THREAD symbol defined causes a link-time error
kind of behavior I expected by moving the Multith- when the library is linked with a single-threaded
readed XML property from the multi-threaded project main module.
into the single-threaded project.
an undocumented XML property called Multith-
readed controls whether the IDE compiles the
Back to the library module in multithreaded mode
When I opened the library‘s .CBPROJ file in the text CB2010 compiles the module in multi-threaded
editor, lo and behold, there was a Multithreaded XML mode even if the XML Multithreaded property is
line in the PropertyGroup with the same condition as set to false
I found in the project‘s application.
CB2010 can compile static libraries in single-
It appears, in CB2010, that static libraries default
threaded mode when the undocumented Multith-
to being compiled in multi-threaded mode and there
readed property is removed from the .CBPROJ
is no way to change it in the IDE or even detect it in
file.
the Project Options panel.
That answered the first question, but what about The previous code snippet from YVALS.H used
the second one? Why did the regression test pass? _MULTI_THREAD instead of __MT__. Even though the
The regression test was compiled using a .MAK symbol‘s name gave us the right clue, it was for the
file, not from a .CBPROJ. Opening the .MAK file ex- wrong reason. Here‘s why: The real dependency is
plained why there was no compile-time problem with when the dynamic runtime library (RTL) is linked into
the regression test: the -tWM compiler flag was the executable.
enabled. The regression test‘s application was com- The dynamic RTL is specified in the base, debug
piled in multi-thread mode. and release configurations on the C++ Linker | Dy-
namic RTL property. When the dynamic RTL is true,
Exploring undocumented that signals the linker to link the runtime library calls
to an external cc3290.dll file. When the dynamic RTL
behavior is false, the RTL is compiled into the executable so
Static libraries default to being compiled with the that it becomes stand-alone -- you only need to distri-
multi-threaded mode enabled. If there isn‘t a way to bute the executable without distributing any Embar-
turn that feature off in the IDE, can I disable it in the cadero (Borland) runtime library files.
.CBPROJ file? It‘s actually much worse than that. CB2010 and
To find that out, I opened the .CBPROJ file using CB2009 will compile a program that causes an access
an editor other than CB2010. I found the violation at runtime when the library calls std::endl
when the RTL is dynamically linked.
<Multithreaded>true</Multithreaded> Did I mention that my head hurts?
The following code shows an example of what I‘m
line and changed its property to false. talking about so you can follow along.
When I recompiled, I was surprised to find that
multi-threading was still enabled in the static library! // Shows how to cause an access violation
// in a perfectly legitimate C++ program
Using the example I had from the single-threaded ap-
plication in which the property wasn‘t even defined, I // LibrarySrc.hpp
then deleted the Multithreaded property from the #ifndef LIBRARYSRC
.CBPROJ file. After recompiling, the library was now #define LIBRARYSRC
void call_Library();
compiled without multi-threading enabled. #endif
ly (dynamic RTL was disabled). However, all new The solution is simple: whenever you link static
CB2010 console-mode projects default to having the libraries, always disable the dynamic RTL in the
dynamic RTL linked with the project. project‘s options. Because a #pragma can link in static
libraries without you specifying it in the project‘s op-
tions, the safest solution is to always disable the dy-
What to do about this? namic RTL in all of your projects.
Based on the permutations shown in Table 1, if your As a side-effect of the investigation, this article
project links to a static library that might have also shows how to determine if a module is compiled
std::cout, std::err, or std::endl, you can not also for single-thread or multi-thread operation. An undo-
link to a dynamic RTL. This is easy enough to change cumented feature in CB2010 is that libraries default to
by modifying the base build configuration for the being compiled in multi-threaded mode. By removing
project but you‘ll also need to verify that the debug the Multithread XML property from the .CBPROJ file,
and release builds do not override your new default. a library can be compiled in single-thread mode.
Conclusion
Contact Curtis at [email protected].
CB2009 and CB2010 have a significant potential prob-
lem when linking static libraries into a project. If the
static library calls std::endl and dynamic RTL is
enabled (by default) in the project, you will get a
Acknowledgement
compile-time error for four undefined symbols (Fig- I want to thank Malcolm Smith for verifying that this
ure 1). problem exists in CB2009.
However, if the static library doesn‘t use
std::endl but it does use std::cout or std::err,
you won‘t get a compile-time error but the application References
will throw an access violation the first time it tries to 1. http://en.wikipedia.org/wiki/Murphy‘s_law
execute that code.
Last month’s poll question was: This month’s poll question is:
How often do you upgrade C++Builder? If the next C++Builder supports cross-
platform development, will you upgrade?
a. Every new release
a. Definitely, yes!
b. Every other release
b. Maybe
c. Only on occasion
c. No, cross-platform development is a
d. Never non-issue for me
(a) (d)
18% 5%