Custom-Drawn Tooltips

C, Win32 1 Comment »

Like many common controls, the tooltip control supports custom drawing for maximum flexibility. This is a quick tutorial on how to use the tooltip custom draw facility.

Read the rest of this entry »

Generating PDFs

C No Comments »

I was looking for a free, open source software library for generating PDFs, and Sean O’Connor pointed me to HARU. I’ll have to check it out.

Beware Mixing Libc Versions In Windows

C, C++, Win32 No Comments »

When developing C/C++ applications on Windows, there are multiple versions of the C run-time library to choose from:

  • Single-Threaded (statically linked)
  • Multi-Threaded (statically linked)
  • Multi-Threaded DLL
  • Debug Single-Threaded (statically linked)
  • Debug Multi-Threaded (statically linked)
  • Debug Multi-Threaded DLL

Most of the time you don’t need to concern yourself with this setting. However, different versions of the C run-time library may not play well together.

For example, I recently discovered that if you try to link a piece of code which uses a static version of the C run-time library to a static library which uses a DLL version of the C run-time library, you will get errors of the form:

Linking...
MSVCRTD.lib(MSVCRTD.dll) : error LNK2005: _printf already defined in LIBCD.lib(printf.obj)
LINK : warning LNK4098: defaultlib "MSVCRTD" conflicts with use of other libs; use /NODEFAULTLIB:library
Debug/Exe.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.

Therefore, if you run across errors like this, check that all your projects use the same C run-time library. In Microsoft Visual C++ 6.0, the choice of C run-time library is provided under Project Settings > C/C++ > Code Generation > Use run-time library. Here is a picture of the dialog:

Project Settings Dialog for Visual C++

What’s Wrong With This Code?

C No Comments »

This code sample is adapted from a bug in a third-party library which I just fixed:

int getNumFields(char* buf, int bufLen)
{
    const int fieldHeaderSize = 32;
    int count;

    for (count = 0; buf[count] != 0xD; count += fieldHeaderSize)
    {
        // No 0xD found in buffer, return failure
        if (count >= bufLen)
            return -1;
    }

    return (count / fieldHeaderSize);
}

What’s wrong with this code?

Read the rest of this entry »

Data-Driven Programming

C, C++ No Comments »

While this is mentioned in an offhand way in the Assert Yourself chapter of Writing Solid Code by Steve Maguire (an excellent, if dated, read), consider using data-driven programming to simplify unwieldly if and switch blocks. For example, have you ever written code like this?

const char* GetString(int indx)
{
    if (indx == 1) {
        return "foo";
    } else if (indx == 2) {
        return "bar";
    } else if (indx == 3) {
        return "baz";
    } ...
}

The problem with this code is that it is repetitive, makes adding or changing entries difficult, and it can quickly get out of hand. For example, imagine trying to manage over 100 entries in this if block. Now imagine trying to change the indx search method, or the type of indx, or adding many other data points also searchable by indx.

To simplify, we can separate the action from the data by using data-driven programming — put the data into a data structure (I chose a static array, but you could use a binary tree, a hash table, or even an external file) and have the code search this data structure to determine the action to perform. For example:

#define ARRAYSIZE(x) ( sizeof(x) / sizeof(x[0]) )

struct GetStringEntry
{
    int indx;
    const char* str;
};

// These values should be set at compile-time, so no run-time
// costs of setting up the array (which may not be true for other
// data structures).
static GetStringEntry[] g_entries =
{
    { 1, “foo” },
    { 2, “bar” },
    { 3, “baz” },
    …
};

const char* GetString(int indx)
{
    // Search for the entry with the correct index
    for (GetStringEntry* entry = g_entries;
         entry != g_entries + ARRAYSIZE(g_entries);
         ++entry)
    {
        if (entry->indx == indx)
            return entry->str;
    }

    // Handle error
}

Yes, there’s a lot of boilerplate code (much of which becomes unnecessary with other languages), but it allows great flexibility. Adding a record becomes trivial, and seeing the relationship between an individual indx and str is easy. Want to speed up searching g_entries? Make sure g_entries is sorted and use a binary search algorithm (such as STL’s lower_bound). Want to change indx to a string? Change the type of indx in struct GetStringEntry from an int to a const char*, the indx values in g_entries to strings, and the == comparison in GetString() to strcmp() — far less work than it would be for the original. Want to add another data point searchable by indx? Add a member to struct GetStringEntry, the new values to g_entries, and write another accessor function.

You can use this technique in even more complicated scenarios. For example, imagine a message dispatching system in which each struct contains the message identifier and a pointer to the function that will be executed when the message is received. Imagine searching for an entry by testing to see if the provided value has certain bits set (by using bitwise-and) rather than performing an equality comparison. Future maintenance programmers will thank you as they discover how easy it is to manage, add and modify records, and improve upon code which uses this technique.

Retrieving Compiler Predefined Macros for gcc

C, C++ No Comments »

To retrieve the list of macros that gcc predefines, execute the following command:

gcc -E -dM - < /dev/null

Avoid Atoi() And Similar Functions

C No Comments »

Avoid atoi() and related functions: they do not distinguish between invalid input and a valid “0″ string. Use functions which properly report errors, such as strtod().

Beware File Open/Save Dialogs: They May Change The Current Directory

C, MFC, Win32 No Comments »

On some Windows operating systems (primarily Windows 95, 98, and ME), GetOpenFileName() and GetSaveFileName() (and wrappers of these functions such as MFC’s CFileDialog) will permanently change the process’s current working directory unless the OFN_NOCHANGEDIR option is specified. As you can imagine, this can easily break your application if you ever rely on the current working directory being set to a particular value (such as if you open files using relative paths).

Of course, it is best to eliminate any such current working directory assumptions from your application completely.

Be Careful With Bitfields

C, Win32 No Comments »

Consider the following piece of code (adapted from a real-world bug):

BOOL IsDirectory(LPCTSTR strPathName)
{
    DWORD dwAtts = ::GetFileAttributes(strPathName);
    if (dwAtts == INVALID_FILE_ATTRIBUTES)
        return FALSE;
    return (dwAtts == FILE_ATTRIBUTE_DIRECTORY);
}

This function will work most of the time, but every now and again it will run across a directory which it will claim isn’t one. Why?

The problem is caused because dwAtts is a bitfield (see the GetFileAttributes() documentation). Simple equality comparison isn’t appropriate, as if the directory has any other attributes (such as compressed, hidden, offline, etc.) the comparison will fail.

To test if a bit is set in a bitfield, use code of the form (dwAtts & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY or (dwAtts & FILE_ATTRIBUTE_DIRECTORY) != 0.

Memory Leak Tracing in Linux Using mtrace

C, C++, Unix No Comments »

Today I ran across an article on DevX.com entitled Identifying Memory Leaks in Linux for C++ Programs. This article describes a utility called mtrace which, in concert with the mtrace() function in the GNU libc, allows one to easily identify memory leaks in C programs.

To use mtrace, follow these steps:

  • Set the environment variable MALLOC_TRACE to point to a file where mtrace will log memory allocations.
  • Insert a call to mtrace() within your code before any memory is allocated.
  • Compile the program with debugging options set (GCC’s -g flag)
  • Execute the program.
  • Use mtrace(1) on the trace log file to view the memory leaks.

I shall have to look at the GNU libc source code to see how mtrace is implemented. I have a few educated guesses, but I can’t seem to piece together the whole picture.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in