• Articles about computing
• Articles about software development
Building native graphical applications in C for MS Windows, using GTK+ and open-source tools
This article describes how to use free, open-source tools to develop graphical
applications in C for Microsoft Windows, which run natively. By 'natively' I
mean that they don't need any supporting infrastructure (as Cygwin does), and
can be packaged and distributed just as any other Windows application, and
will look and behave like one.
I will be using the GTK+ graphical user interface (GUI) libraries to provide a
Windows-like user interface. GTK+ is widely used — almost ubiquitous, in fact — in Linux development, but has only recently started to get a substantial
foothold in the Windows world. A bonus of using GTK+ is that — with care — applications will be portable between Windows and Linux. For many people this
is a very big deal. Traditionally, to create a graphical application that ran
on both Windows and Linux we had to use a programming language, like Java, that
was specifically designed for that kind of compatibility. But you can't create
a native application for Windows using Java — it requires a complete
runtime system which has complicated redistribution requirements, both legal
and technical. Everything described in this article is open-source and can be
freely repackged and distributed.
In this article, I use only standard Windows utilities (like Notepad)
and command-line tools. I'm not suggesting for a moment that you'd want
to build a real application this way, but it does help to demonstrate
exactly what's going on when you compile and link code under Windows.
The application itself will be the simplest 'Hello, world' example I
could come up with using GTK+.
Please note that this article is written for experienced C developers. In
particular it assumes that you are able to make sense of compiler
error messages, and are willing to use the GNU
gcc compiler from
a command line. I also assume that
you have some knowledge of GTK+, or at least are willing to learn. There
are plenty of GTK+ tutorials, but they all assume that you have a working
build environment, which is where this article comes in. I'm tending to
assume that people who are likely to read this article are Linux developers,
looking to port their applications to Windows, or to start developing for
Windows. Consequently I will frequently refer to the differences between
Linux and Windows in this respect.
When time allows, a later article will explain how to set up a practical
build environment, which allows both Windows and Linux applications to be
build from exactly (well, almost exactly) the same sources.
It's very likely that, by the time you read this, version numbers of software components will have changed. Because I'm refering to DLLs and similar files explicitly by filename in this article, you should be prepared to have to adjust filenames to accomodate version changes. There are ways to avoid this problem in a pratical, automated build environment, but it isn't the purpose of this article to explain how to do so.
Getting and installing the tools
To follow the example in this article you will need to download and
set up the following software:
These days the recommended way to install MinGW is to use the automated
installer (download page). The automated
installer will strongly recommend installing in the directory
- MinGW (Minimal GNU for Windows). This is, essentially a native Windows port of the GCC C compiler toolchain and standard library, and some Windows extensions
- GTK+ build for MinGW
c:\MinGW. While this does not conform to Windows conventions
for installing software, I would strongly recommend using this
location — it's really more trouble than it's worth to change it, and
all the documentation and tutorials you're like to see assume this directory.
Rather awkwardly, the GTK+ build for MinGW is maintained as a bunch of
separate packages (presumably because this is how it's done for Linux).
All these packages are available from the
download page, in the form of archives. You'll need to download and unpack
The automated installer will probably offer to install MSYS as well. MSYS is a set of utilities like GNU
make. You won't need any of these for this example, but they are exceptionally useful for real build projects.
c:\MinGW) almost all the 'dev' and 'runtime' packages
on that page — you might
as well get everything. You won't need the
for this example, but it is important in any automated build system.
The 'dev' components are needed for building applications, but won't need
to be distributed with it. The 'runtime' components will need to be
distributed because, unlike Linux, Windows users won't normally have a
system-wide GTK+ runtime available. A mechanism for bundling and
distributing the runtime components will be discussed later.
Setting up the command prompt
In this article, all the building will be done from the standard Windows
command prompt (
cmd.exe). The Cygwin
is much nicer to work with, particularly if you're familiar with it from Linux,
but the MinGW tools are not built for Cygwin — They are native Windows applications. Consequently, if you do use the Cygwin prompt — or any build
system intended for Cygwin — you're going to have to be quite careful about
things like path separators.
The only thing it should be necessary to do to set up the command prompt
is to extend the PATH so the command line can find the C compiler and
libraries. It's also handy to create a directory to keep the application
code in — I'm using
f:\source\hello_world in this example.
gcc: no input files
If you can run
gcc at the prompt and get the message
'no input files' then most likely you're all set.
Compiling a simple console-mode program
Before launching into the complexities of GTK, we will build a simple
console-mode application, just to check that the C compiler is working
correctly and all the C runtime bits are available. Here's the
example I will use, saved as a file
int main ()
printf ("hello world\n");
Compile and run the example from the prompt as follows:
F:\source\hello_world>gcc -o hello hello.c
-o hello tells
gcc to write the output
as the file
hello.exe — the default is the
Note that this example does not create any graphical interface, or
need one. It only produces console output. You might wonder what would
happen if we ran
hello.exe in a context other than
a command prompt — for example, by double-clicking it in Windows
Explorer. We'll come back to this issue later, because it's surprisingly
vexing for those of us who are more familiar with the Linux way of
Compiling a simple graphical program
This section deals without compiling, but not linking, a graphical
GTK+ application. I'm distinguishing compiling from linking to
demonstrate better the technicalities, but also because in a
project of any scale you'd almost certainly want to do this. In addition,
for Windows development there are things we'll need to link that
are not executable code (specifically, Windows resource
objects — more later) so it's important to understand the separate
hello.c again, extended to create a GTK+ dialog
box which will show the message:
int main (int argc, char **argv)
printf ("hello world\n");
gtk_init (&argc, &argv);
GtkDialog *dialog = GTK_DIALOG (gtk_message_dialog_new
gtk_widget_destroy (GTK_WIDGET (dialog));
Note that conventionally the GTK+ main header file
Since we're going to have to tell
gcc exactly where this
header is (and a whole bunch of others), in principle we could use the
shorter formulation in the code and adjust the directory supplied to
gcc. In the longer term, this would be a bad idea, because
many automated build tools will assume that you've followed the convention,
gtk.h and all the other headers you're likely to need
in a full-scale project.
If you try to compile this application as the console example above, it
won't work, because of the missing headers. Irritatingly, you'll need
gcc not only where
gtk.h is, but all
the other headers that
gtk.h references. So to compile this example you're going to need the following command (sorry):
-I c:\MinGW\include\glib- 2.0\
-I c:\MinG W\include\cairo
-I c:\MinGW\i nclude\atk-1.0
-o hello.o -c hello.c
Of course, this should all be on one long line. We're getting to a point where
simple use of the command prompt is going to be problematic, and we'd do
better to put this command into a batch file (or Makefile, or whatever).
GLib (utility library), Pango (text renderer), Cairo (2D graphics library), and ATK (accessibility interface) are all software components on which GTK has depenencies, and which will need to be bundled with the final application — more
on this later. Note that they all have different versions, and the version
numbers might have changed by the time you read this.
If successful, the
gcc command above will produce the object
hello.o, ready for linking with the GTK libraries to
produce an application. The
-c switch tells
not to link, because linking will fail at this stage.
-mms-bitfields merits further explanation: this
gcc to pack C data structures into memory in a way that
is compatible with Microsoft compilers. This is a big deal if your C application
will call Windows APIs directly (rather than, for example, using the GTK+
wrappers). Even if your application won't call Windows APIs, it's still a
big deal, because the GTK+ libraries will have been compiled with this switch.
C code compiled with
--mms-bitfields is simply not compatible
with code compiled without it, even from the same C compiler.
Linking the simple graphical program
Linking presents the same sorts of problems as compiling: you need to
gcc where the libraries are. You also need to tell it
what libraries to link — it won't attempt to figure this out. Unlike
problems with not being able to find missing header files — which you can
probably fix by trial and error — it's hard to figure out what libraries
are lacking unless you're already familiar with GTK+ programming.
In any event, here's the command you need:
-o hello hello.o
-lgtk-win32-2.0 -lglib-2.0 -lgobject-2.0
Again, you'll need to put this on one long line, or in a batch file.
In this command, the
-L switch is specifying where the
libraries are, and the
-l switches what libraries must
be linked. GObject, which we haven't encountered before, contains the
implementation of the GTK object-oriented model.
There's a complication here that will bite us later — we've specified
the libraries that are required at link time, but that isn't by any
means all the libraries that will be required at run time. GTK will attempt
to load a whole bunch of other stuff dynamically, and it needs to know
where to find it — again, it's not something it can guess. More
on this point later.
Running the graphical program
If the link operation was successful it will have produced an executable
hello.exe as before, and you should be able to run it from
the prompt. All being well, you should see the application's window:
OK, so it's not the most impressive application in the world, but it is
a genuine, native windows GUI program. From the screenshot you'll note that
the application also produces console output, as before.
One thing that gives away the GTK+ origin of this application is the generic
GTK+ logo icon in the window's caption bar. This is the icon you'll see if you
create a shortcut to the application on the desktop as well, and you'll almost
certainly want to replace it with an application-specific one — details later.
Bundling the graphical program for distribution
Assuming you've got the application running from the command prompt, I
suggest trying the following experiment, which demonstrates why distributing
GTK+ applications can be somewhat problematic.
Start a new command prompt,
cd to the directory
hello.exe and try to run it. I suspect you'll
get a result something like this:
Why should it work from one command prompt and not the other? You'll
get the same (non-)result if you try to run
the Explorer. If you're
a Windows developer you'll probably find the answer more readily than
a Linux developer, because it turns on the way dynamic library loading
works in Windows. When we linked
hello.exe we had to tell
gcc where to find
(and all the other DLLs) so it could do the link. But
not store this information in the executable, because if it did it would
make the application depend on the absolute directory where the GTK+ components
were installed. So we need to tell Windows at run time where these bits
are. The reason this worked from the previous command prompt is because
we had already set PATH to include the MinGW
bin, which is
where all the DLLs are. Linux does not work this way — the library
loader will not automatically look for libraries in PATH. To create an
application you can distribute, you'll need to bundle the GTK+ libraries,
and find a way to tell Windows where to find them when running the application.
The problem of bundling GTK+ components rarely arises in the Linux world,
because there will typically be a single, system-wide installation of GTK+, and
the library loader will know where to find it. On Windows, however, where GTK+
is still relatively new, you'll have to give the operating system a bit of
There are essentially three ways to bundle GTK+ libraries with an application
Of these, the first is just too ugly to consider. The second has certain merits, but the installer ought to check that there is not already an installation
of GTK+ in whatever location you choose. If there is already an installation,
and it is different from the version you need, then you might run into
In practice, so far as I know all GTK+ developers working on Windows adopt
the third approach, which is to bundle the application and all the libraries
into a common directory. This works because Windows will always look for
libraries in the same directory as the executable that invokes them — you
don't have to tell it to do this, any more than you have to tell it
to look in PATH. Again, this is rather different from the Linux approach, but
whether it's better or worse I'm not sure. Anyway, it's easy to implement
To build a bundle for distribution, my approach is to have my build system
create a directory and copy all the necessary DLLs from the GTK+ distribution,
along with the application itself. There's really no obvious way to work
out the complete set of libraries an application needs — bundling is a
matter of observing the error messages as in the screenshot above, and
copying the missing DLL into the bundle directory.
If you do this, you should find that, not only can you run the application
from any prompt, you can also run it from the Explorer, or create a
desktop shortcut to it. The screenshot below shows
- Link all the GTK+ bits with the application into one huge, fat lump
- Create an installer that will put the GTK+ libraries into a system location
- Create an installer that will put the libraries into a new directory along with the application itself
being run from a directory containing the necessary DLLs — it also shows
the rather extensive list of DLLs that even this trivial application needs.
However, this list won't get much longer even with a full-scale
With care, the directory you build up here contains everything you need
for a deployable application, and all you need to do to keep users happy
is to bundle up an installer that will dump all this stuff into a
c:\program files. Most of the widely-used
Windows installer builders can do this in a couple of mouse clicks.
Console-mode and GUI-mode issues
If you run
hello.exe from the Explorer as shown above,
you'll probably be disagreeably surprised to find that you also get
a console prompt. With a Windows GUI application, you almost certainly
don't want this.
The reason this happens is that you've built a 'console mode' application,
and so Windows gives you a console, whether you want one or not. It
wouldn't make any difference if the application produced no console
output — you'd just get an empty black box instead. Worse, if you
close the console window, it kills the application as well.
The fact that Windows recognizes distinct console-mode and GUI-mode applications
is a source of vexation for Linux programmers, because Linux behaves much
more sensibly in this respect. Linux has no separate console mode: any
application can produce console output, and if you don't have a console
you can either arrange for that output to be captured some other way,
or just let it silently disappear. But Windows, irritatingly, forces
you to make a choice.
gcc to create a GUI-mode application is as simple as
adding the switch
-mwindows to the command line at link time.
This will get rid of the irritating console box. But if you do
this, you won't be able to get console output even if you run the application from a console or a debug framework. For basic application debugging during
development, this is a real pain.
If you're using an automated build process, the simple solution is to have it
build two completely separate binaries — one with the
switch and one without. Then you can use the console-mode version for testing and the GUI version for distribution. In practice, in a large scale project
you'll probably want to build slightly different 'debug' and 'production' releases anyway, so this is no great hardship.
Resources and icons
It's unusual for Linux executables to contain any binary elements except
executable code and any associated data. For better or worse, this is
not the case for Windows GUI applications, which conventionally embed
user interface specifications in the executable. Traditionally these
elements include icons, stringlists (for internationalization purposes),
menus, and user interface widget layouts. In the Linux world, if these
things are used they are typically stored in separate files.
In practice, if you're using GTK+ you will probably want to build user
interfaces the GTK+ way, rather than using native Windows API methods.
The only interface element that isn't easy to replicate using GTK+ is the
application icon, and that is because it's read by the Windows desktop
and not by the application itself. I don't know of any way to embed an
icon into an executable other than by treating it as a Windows resource file
element, and compiling it using the
windres resource compiler
which is part of MinGW. This is a bit of a kerfuffle just to install an
icon but, of course, it can be automated.
To install an icon we'll need to create a minimal resource file — it's
just a one line text file so we can use Notepad or any other editor.
Here is what the file should contain:
1 ICON "icons/app.ico"
I have called this file
app.rc. The icons themselves can
be in any directory and have any name. You can specify multiple icons
.rc file if you wish, or create a separate file for
each. You'll also need a way to create icons in the appropriate format — they're not something straightforward like
Happily, a number of free and/or open-source icons editors are available.
Once you've created your icon and installed it in the location specified
.rc, the procedure is to run
convert it to an object (
.o) file. This file can then be
incorporated into the executable at link time.
F:\source\hello_world>windres app.rc -o app.o
F:\source\hello_world>gcc -L c:\MinGW\bin
hello.o app.o -lgtk-win32-2.0
-lglib-2.0 -lgobject-2.0 -mwindows
For a Linux developer, it seems odd to be putting things into the executable
that the application does not need. In principle, the application can
locate resource (like icons) created using
native Windows API calls. However, if it's just a case of adding a program
icon, then that should not be necessary.
The procedure described in this article probably looks complicated, particular
compared to using Microsoft Visual Studio and just hitting the 'Run' button.
However, if you're familar with tools like
Make, or even
Windows batch files, then all
the complexity can be automated, and building the application becomes
a one-step operation.
All the same, if you're targeting the Windows platform specifically, and
no other, then using open-source tools as described here doesn't really
offer any advantage over MS Visual Studio, and has the disadvantage of
needing to learn how to use a whole new set of tools and APIs. Where the
open-source approach really wins is in supporting
Windows and Linux builds using the same code. With a little thought,
you can set up a build system that will create both Windows and Linux
installable packages using essentially the same code and configuration
files, and this could represent an enormous productivity bonus in the