Step Two: Link your Program's Components
After the compiler compiles your code, the linker links together the files that make up your
program.
The linker is responsible for combining your compiled code, which may be located in various
files after they are compiled, and connecting the files together, along with any libraries the
program requires. Linking makes sure each piece of code knows how to find the other
elements of your program. For example, the linker modifies the machine code so that your call
to printf() gets correctly routed to the MSL library function printf(), which does the all of the
magic necessary to implement console I/O.
When you write a program, you split your code into functions that are stored in different files
and are compiled to a single output file. In the case of the Hello World program I've been using
as an example, this output file is an application. In some cases, functions used in a program
may not even be stored in the same output file. When would this happen? When you write an
application, for example, that contains a lot of code that is reused in several parts of your
program, you might compile all of that reusable code into a single shared DLL. When you do
this, the application can still access the code, but it needs to know where and how to find it
when it needs it, hence the need for linking.
Types of Linking
In programming, a link is a pointer, either to an object, such as a compiled function, or to data,
such as a variable or an array. There are two ways to link: hard linking and soft linking.
Hard linking occurs when all of the code is accounted for at link time. That is, if a function is
used, the linker knows exactly where it is at link time and can create a direct link to it.
Soft linking (also known as weak linking) is when some of the code may not be accounted for
at link time, but you know that it is in a DLL that will be accessible at runtime. In this case, the
linker can tell your application to search for the DLL when it is needed.
Soft linking is the method used by many elements of your computer's operating system. Next,
we'll take a look at some of the options that the linker provides.
A Detailed Look at Linking
To further appreciate the functionality of the CodeWarrior linker for the Windows platform, you
need to understand how you can configure the CodeWarrior linker to accomplish what you
need.
Configure the Linker to Suit your Needs
The choices in this lesson appear in the x86 version of the CodeWarrior linker panel (Figure
4-1). But many of the terms used in the x86 linker are applicable to other linkers.
1
Figure. 4-1 The x86 linker settings window.
Open the Settings window by choosing "Project Name" Settings from the Edit menu.
(Project_Name is the currently active project -- in this case, Hello World x86.) Click on the x86
Linker (or similar) tab in the Settings window.
Let's take a look at some of the linker options:
Entry Point Usage: Under the Windows OS architecture, each code module has a
number of entry points that may or may not be optional, depending on the type of code
you are building. The nature of these entry points depends upon the type of output file
you're making (application, library, or DLL) and other factors. The host OS uses this
entry point information to properly call a DLL's initialization functions when an
application demands its services. Most often, you will leave this set to Default as
shown in Figure 4-1. This lets CodeWarrior automatically set up the proper entry
points for the output file, based upon your choice of target in the x86 Target panel.
Subsystem: There are several settings here. Of interest to you are the Native,
Windows CUI, and Windows GUI selections. The Native setting is for building drivers
and other exotic system modules. The Windows CUI setting is for those applications
sporting a console-style user interface (hence the name CUI). The Windows GUI
choice supports applications that rely on the Windows graphic user interface (GUI).
You'll notice that our simple, console-based Windows application uses the CUI
subsystem, as expected. Normally, CodeWarrior sets the subsystem for you
automatically when you choose a CodeWarrior project template to construct the
project file.
2
Subsystem ID/UserID: Like the subsystem item, this item is typically set for you
automatically. For the curious: Windows 3.1, Windows 95, and Windows NT 3.5 have
an ID of 3.10, while Windows NT 4 has an ID of 4.00.
Generate Link Map: This option allows you to generate a text file that contains
detailed information on the classes and functions used throughout your program. This
can be extremely helpful when debugging code by matching addresses to variable or
function names.
Generate SYM File: This option allows you to generate a symbol file during the link
process. Most debuggers require a symbol file in order to step through your source
code when debugging. This is discussed in more detail in Lesson 5. You can also
generate a symbol file for CodeView, a popular Windows debugging tool, by checking
the Generate CV Info item.
Generate CV Info: Generates a symbol file for CodeView, a popular Windows
debugging tool.
Command File: This item lets you pick the name of an optional text file that directs
sophisticated linking options. Called the linker command file, you use it to specify
which symbols to import and export, and other advanced linking operations. Unless
you're writing special-purpose code, you won't normally need a linker command file.
As you can see, the linker can be a relatively complex piece of software machinery. Luckily, in
many cases, the default settings will suffice. Very rarely will you have to tweak the linker
settings to get your programs to work. The folks at Metrowerks have done an excellent job of
managing this thankless job for you. Yay, CodeWarrior!
An Introduction to Debugging
Purging pesky pests proves productive for programmers!
Debugging is the process of tracking down a problem in your code and fixing it. The sad truth
is there is always the possibility that your code contains typing or logic errors. Maybe you
thought through a problem thoroughly but still missed a key step in solving it, or maybe you
made a tiny typo in your code or used the wrong variable. In many cases, you'll know when
you have a flaw in your logic or a programming error because your program will not function as
expected. Or, in a worst-case scenario, your computer will crash. When this happens, it's time
to begin debugging.
CodeWarrior includes an integrated debugger, which just means that you can view it while
you're looking at other windows of CodeWarrior. Begin using the debugger by selecting Enable
Debugger from the Project menu; then rebuild your target. Once that's done, your program will
run within the debugger window, allowing you to isolate and fix any problems you find.
Quite often you will generate a debug target of the program. The target's source files remain
the same, but many of the options required to produce a debuggable version of the program
(such as generating a symbol file) are preset. This lets you flip into debug mode by just picking
the debug target from the Project window's pop-up menu.
3
Figure 5-1: The debugger does its thing.
Figure 5-1 shows the Hello World debug program running within the CodeWarrior debugger.
Note that we've added a few lines of code to the program in the form of a loop using the
variables i, x, and y. We added this code in order to more easily demonstrate how the
debugger works. The debugger allows you to step through your code line by line. As you move
through the code, you can also note the values of all of your variables. By forcing your program
to run in this "slow motion" mode, you can see exactly how your code is working and very
easily track down bugs.
I lied. Not all bugs are easy to track down. I've personally worked on bugs that
have taken weeks to figure out and fix. Anyone who has programmed for any
length of time probably has similar horror stories. Don't let them get you down.
On top of everything else, you are a beginner, so you will inevitably be writing
buggy code. Remember: we learn more from our mistakes than our
successes. OK, I'll stop now.
Notice the buttons at the top left corner of the debugger window. These buttons assist you in
stepping through your code. The buttons are, from left to right: Run Program, Stop, Kill (Quit),
Step Over, Step Into, and Step Out. The last two step commands allow you to decide whether
you want to step into a particular function in your code, or simply step over it and let the entire
4
function execute in one fell swoop. If you know that a certain function does not contain the bug,
you can easily step over it and save time. If you're not sure, you can step into it and walk
through its code line by line. As a beginner, you should assume that nothing is bug-free -- and
you can use the practice.
The Debugging window (Figure 5-1) contains three main sections:
The Stack pane (top left) contains a display of the call stack, also known as call
history. As you step through source code and call function after function, this area
displays the history of functions you have called to get to the current location. This list
grows and shrinks as you step through different sections of a program and according
to whether you step into functions or not.
The Variables pane (top right) shows variable names and their values. Depending on
the type of variable, this display will vary to allow you to view (and edit) the data
accordingly. In the case of the Hello World x86 program, shown in Figure 5-1, there
are four variables: c, i, x, and y. As you step through your code, the values of the
variables will update in this window in real time. For example, notice how the latter
three variables have realistic values (since these values were set by the code loop),
while c has a nonsense value (you have yet to enter a character into the getchar()
function). Using this pane to spot odd or unexpected values is one of the keys to your
debugging success.
The Source pane (bottom) displays the C source code for Hello World x86 program. It
can also display the assembly language version of the code. Depending on the type of
bug you are tracking down, the C source display may not provide enough detail. In that
case, you may need to view the program at a finer level of detail -- in assembly
language. The pop-up menu at the bottom of this window lets you choose whether you
view the program as source, assembly language, or a mix of the two.
Note the red stop sign on the left edge of the Source pane. The stop sign indicates a
breakpoint. You set breakpoints in the debugger to tell it where to stop executing your code.
This feature can come in very handy when you want to quickly move to a certain point in your
program and begin working from there. When you set a breakpoint, the debugger will
automatically stop at that line of code and will let you take control. The small blue arrow is the
current program counter; it points to the line of code that is about to be executed. In the Figure
5-1, we have set the breakpoint so that the program executes the code loop before halting on
the second printf() statement. You can set and clear breakpoints by simply clicking to the left of
the line of code you're interested in.
Let's look at some of the preferences for the CodeWarrior debugger.
Global Debugger Preferences
There are many global preferences in the CodeWarrior debugger. Let's take a look. These
settings can be reached by selecting Preferences from the Edit menu.
Display Settings
5
Figure 5-2: Display settings.
The Display Settings panel (Figure 5-2) contains preferences that determine how the
debugger displays information in its windows. As you begin to use the debugger, you will find
that you want certain text to appear larger or smaller, or to be displayed in a specific color. You
can select these options here.
Color settings allow you to choose the text color used by watchpoints or variable names when
the code alters them. This allows you to see if a watchpoint or variable is being changed
without your knowledge as the code executes. Watchpoints are useful if you want to track a
variable at intervals throughout the program and want it to be conveniently displayed in a
window.
The other settings are self-explanatory. In most cases, the default settings will work. As you
learn more about the debugger, you may want to alter these settings.
Windowing Settings
6
Figure 5-3: Windowing Settings.
When debugging, you will quickly find that your desktop is cluttered with lots of open windows.
The Windowing settings (Figure 5-3) allow you to manage windows by choosing to hide or
show certain types of windows when you start debugging. The default setting, Hide
Non-Debugging Windows, will probably suffice in most cases.
Global SettingsThe Global Settings pane (Figure 5-4) contains options that control the way the
debugger operates.
7
Figure 5-4: Global settings.
Cache Edited Files Between Debug Sessions setting allows you to choose how long to cache
edited files between debug sessions. Caching files can increase the speed of the debugger but
will also take up precious disk space.
The remaining settings are self-explanatory. The default settings will serve you well as you
begin to use the debugger.
Remote Connections
Figure 5-5: Remote Connections.
Lastly, the Remote Connections Settings (Figure 5-5) allow you to remotely debug code
across a TCP/IP network. These settings are advanced, and the default values will work fine
as you learn to debug in CodeWarrior.
Note that the Java Settings and Java Debugging panels are for the Java Programming
Language and are not covered in this course. However, other courses are available for this
language!
Target and Project-Specific Debugger Preferences
The following settings are specific to each target and project that is debugged. You can reach
them by selecting Project Name Settings from the Edit menu, while a target is selected in the
Target tab in the CodeWarrior Project window.
8
Figure 5-6: Target and Project Debugger Settings.
Debugger Settings
Location of Relocated Libraries, etc: If the file you are debugging has been moved, or
if you're debugging remotely over a TCP/IP network, you must type the file's directory
path into this text box.
Stop at temp breakpoint on application launch: By default, this option breaks the
debugger (stops execution of the code) at the beginning of your program. However,
you can choose to break at any point.
Auto-target Libraries: Check this setting to automatically access libraries. This can
assist you when debugging code that is part of a library. The debugger will "do the
right thing" (such as loading the library and calling any initialization functions) as you
step into its code.
Log System Messages: This setting forces all system messages to be written to a log
file. This can be useful if your code is crashing your computer, and you can't see the
screen during the crash. The messages will be written to a log file that you can view
after you restart your computer. Log files can save you many headaches later. Learn
to read them!
Data Update Interval: By setting a value here and checking this box, you can choose
how often data views are updated. The more often you update data views, the more
up-to-date the displayed values will be in the debugger.
Cache symbolics between runs: Symbolic files (files generated during the compile
process) will be cached and referenced between debugger runs. This can speed up
9
the debugging process.
Stop at Watchpoints: To stop at watchpoints when they are equal to true, check this
option. Watchpoints can be very useful, similar to breakpoints. Whereas a breakpoint
will break into the debugger on a specific line of code every time, a watchpoint
monitors a variable or area of memory and breaks into the debugger only when the
contents change. There is also a feature called a conditional breakpoint that will halt
execution when the condition is true. Depending on your needs and the way you like
to work, these advanced features can be very helpful to you as a programmer.
x86 Exceptions
Figure 5-7: The x86 Exceptions panel.
As you debug questionable code, the CodeWarrior debugger intercepts certain oddball
operations (termed exceptions) brought about by a program bug. The IDE passes these
exceptions along to special code -- appropriately known as a handler -- that attempts to deal
with the problem gracefully.
However, for many exceptions, the debugger simply stays out of the way. Why is this?
If you're doing advanced programming such as writing a system library or a device driver, you
may want to test how its code handles some of these exceptions. By not intercepting many
exceptions, the debugger lets you do this. If for some reason you want the debugger to "seize
control of the moment" for specific exceptions, you can direct it to do so with this panel. The
exceptions shown here are ones that the CodeWarrior IDE is prepared to handle. Click on the
exception types you need the debugger to field for you, and then click Save. As mentioned
previously, this is heavy-duty programming material, so we won't go into any details here. The
10
debugger is a very advanced, yet very easy-to-use addition to the CodeWarrior environment. If
you learn to use it well, it will become one of the most important tools in your software
development arsenal.
Note that the Other Executables and Remote Debugging panels are for advanced users and
are not covered in this course. If you ever have a need to debug multiple-executables at a time
and/or debug over a TCP/IP network, you will know it!
Libraries
What's a library? This is a question you need to know the answer to! A library is a file that
contains compiled code that your programs can call upon when needed. Typical functions that
libraries can offer are file and screen I/O, memory management services, 3-D graphics display,
and scientific computations. Libraries can save you time in the development process and help
simplify your software applications. Chances are that you will use libraries quite often in
developing programs. There are several ways to use library code in your programs:
Write it yourself in a source code file, and optionally compile it as a library or DLL.
Use a library provided by someone else.
Use a shared library (a DLL) provided by someone else.
Writing Your Own Library or DLL
The first method is simple. When you include code within a C or C++ source file, you complete
the program by simply compiling, linking, and executing it. This is the way we've been working
thus far during this course. See, you were using them and you didn't even know it. You're
good.
Using Someone Else's Library
Using someone else's library changes things a bit. Libraries provided by third-party vendors
contain several files: the library file itself (usually with a name ending in .lib), a header file (.h),
and (hopefully) some documentation (.doc). The header file is required so that the compiler
can match function calls in your program to functions within the library. You include the header
file in your source file and then simply call the library functions as if they were part of the
operating system. In most cases, you place the .h and .lib files in your source path, as defined
by CodeWarrior. This path is usually in the directory where your project file is located, or inside
a subdirectory of this directory. Note that certain libraries CodeWarrior provides, such as its
MSL, reside in a different directory from the one your project lives in. In fact, CodeWarrior has
its own special access paths, called system paths, which point to the directories of its own
libraries and header files.
Using a Shared Library
Third-party vendors ship library files with their products so that they don't have to part with their
source code. If you're the vendor, it's much safer to include the library and a header file (as
opposed to a C or C++ source file and a header file), especially if you want to protect your
intellectual property. The problem with this approach is that if there's a bug in the library you've
gotten from a third-party vendor, you'll be unable to fix it yourself. You'll have to obtain a fix for
the library from that vendor, and who knows how easy that will be?
11
Sharing DLLs
A DLL is very similar to a library file. In fact, the only difference is that a library file usually gets
compiled and linked into your application, while a DLL is a separate file that either sits in your
system directory or in the same directory as your compiled application. When your application
runs, it locates the DLL and uses its services on the fly. DLLs are typically identified by the .dll
extension.
Benefits of Sharing DLLs
The benefit of a DLL is that it can be shared. OK, that was pretty obvious. What is the benefit
of sharing? First, more than one application can make use of a DLL. This can be especially
useful when several programs use a large DLL that takes up lots of disk space and/or memory.
Also, if a DLL contains a bug and is updated, all programs that share it will be automatically
updated. This saves you and your run-time processing large amounts of time. So what kind of
library would need to be shared by multiple applications? Oh, stuff like the very windows you
see in your operating system environment.
Note on Using Shared DLL files
The CodeWarrior IDE CD-ROM contains dozens of libraries, and hundreds more are available
on the Internet. Always, always, always run a virus check or disinfectant program before
incorporating third party code into your product. It's a good habit, like locking the door every
time you leave the house.
The libraries included with CodeWarrior fall into two categories. The first is the MSL, which
provides ANSI C/C++ standard functions. The MSL has been ported to many platforms, so its
functions are available in various flavors of Windows, the Mac OS, and Solaris. A second set
of libraries is intended for platform-specific uses. For example, on the Windows CD, you'll find
Windows 32 support libraries that interface to the Windows APIs and the MFC libraries, which
provide an object-oriented application framework for writing Windows applications. When you
gain more programming experience, you will find more uses for the specialized libraries.
What is MFC?
The Microsoft Foundation Classes (MFC) provides a set of functions that let you quickly write
Windows applications. While you're free to call the Win32 APIs directly, it's easier to use the
MFC because the classes provide basic services such as creating a window or writing a file
along with intelligent default settings and error-checking code. MFC is available on the
Windows CodeWarrior CD but may not be the very latest version of the classes. Check with
the Metrowerks help desk for that information, as Microsoft will send update patches between
CodeWarrior releases.
For the sake of platform parity, Macintosh developers can use Metrowerks' own PowerPlant
class libraries. PowerPlant is an object-oriented application framework designed for writing
Mac applications. Although we will not discuss PowerPlant here, it and MFC are similar in
many ways, and what you learn here will help you to understand PowerPlant as well. When I
discuss MFC, it is safe to assume that the same concepts apply to PowerPlant.
Note: There is also a library version of MFC on the CodeWarrior Macintosh
CD, but unlike the Windows CD, the source code is not included for the
Macintosh. Using MFC, you can write cross-platform code. Once you have
12
written the code, you can compile it for both the Macintosh (using PowerPlant)
and the PC (using MFC) at one time. Pretty cool, huh?
Application Framework
As mentioned earlier, MFC is an application framework. This means that MFC is a collection of
C++ classes in source code or library format. The framework can create a complete
application that supports advanced operating system features in a fraction of the time it would
take to do it all by hand. Note also that MFC contains tools to facilitate building your user
interface. These tools, specifically the Image Editor, Dialog Editor, and Resource Compiler,
help you build the graphic elements of the user interface (the last section of the lesson
explains how to install these useful components). An application framework concerns itself
primarily with the standard user interface of the application, as opposed to the specific content
of the application. Put another way, the framework helps you build a robust application
interface to interact with the user, giving you more time to write the other functions that
implement the application's purpose. So you don't have to worry about spending time and
energy coding a scroll bar or close box from scratch to appear like all other applications
running in the operating system environment. You can just use the libraries and get on with the
real creative stuff.
Figure 7-1 MFC's user interface builder.
Application frameworks also handle the dispatching of messages (such as user keystrokes,
mouse clicks, drawing messages, etc.) to the many buttons, lists, windows, and controls in
your application. This allows you to keep your application's code focused on its own features
13
rather than on addressing interface questions like "How do I tell when the user pressed the
Enter key?" or "How do I know when I need to redraw the contents of a window?" It's really the
little things that mean the most once you are actually using the application.
Application frameworks provide many benefits over the old-style roll-your-own approach. For
one thing, the code is mature and well tested. Hundreds of applications are being built by
hundreds of developers using MFC every single day. It is constantly being updated as new
features are added to the Windows operating system. In many cases, you can recompile your
application with a new version of MFC and take advantage of all the new features with few if
any changes to your source code.
Application frameworks also provide reusable code. Code reuse is one of the main reasons
object-oriented coding methodologies like C++ were developed. By writing code in reusable
classes, you can easily build upon existing classes to add necessary features. Code reuse
allows you to depend on well-tested code throughout your application and feel confident that
the code will work as expected. It is also very easy to share classes that you build with others
who may need the same features.
Application frameworks can sometimes make your programs a bit larger than they might be if
you were to create them completely from scratch. Since modern computers are usually
equipped with substantial amounts of memory, and since the framework can be used as a DLL,
it's fairly easy to justify the added bulk. PowerPlant and MFC are both very powerful tools that
you should consider using.
Your best bet now is to focus on learning more about MFC and how it can assist you -- and you
can make the decision on how much you want done for you, or how much you want to dig in
and do yourself.
How to Install and Use MFC Interface Tools
The MFC interface tools (including Dialog Editor and Image Editor) are located on the
CodeWarrior CD. Unfortunately, they're not part of the CodeWarrior Installation, so after the
initial CodeWarrior installation, you won't find them on your hard drive. In order to install the
tools, simply perform the following steps:
You need to locate the file named iTOOLS.Exe. This is the installation program you
will use to install the MFC Interface Tools. The iTools.Exe file should be located at
C:\Program Files\Metrowerks\CodeWarrior\BinSDK\bin\. If you can't find the file there,
then go to the CodeWarrior CD. The file iTOOLS.Exe can be located in the folder
Extras\SKDs\Win32\Microsoft Win32 SDK Tools\.
Note: The Installation program (iTOOLS.Exe) will ask you where you want to
save the MFC Interface Tools. Unfortunately, there is no Browse button to
locate a folder location, so you will have to manually type in a directory path. If
you are unsure of the exact path for where you want to save the tools, locate a
file that resides in the folder where you want to save the tools and retrieve the
complete path for this folder by examining the file's properties (right-click on
the file and select Properties). Here's a quick tip: you can actually copy the
path from this Properties box and then paste the path when prompted by the
installation program.
14
The tools will be located in the \bin\ folder of the target installation folder. The \bin\
folder is where you'll find the Dialog Editor (DlgEdit.Exe), Image Editor (ImagEdit.Exe),
and other useful tools.
If you're developing applications for Windows 95 or Windows NT, the necessary tools
are available in the folders \bin\win95 and \bin\winnt, respectively.
15