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++

Windows Vista Changes

Win32 No Comments »

The next version of Windows, Windows Vista, has a new security feature called User Account Control (UAC) (nee User Account Protection (UAP) nee Least-privileged User Account (LUA)), which basically means that users will no longer have full Administrator privileges by default. Microsoft has produced an overview of the new security features, a Developer Best Practices guide, and a blog to help inform developers about the upcoming changes.

If your application currently runs correctly as a non-Administrator, no development effort should be required for Vista. If you perform any privileged operations (such as writing to Program Files\ or opening a HKEY_LOCAL_MACHINE key in the registry with write privileges), your application will likely continue to work but the user experience may be suboptimal.

Here is how to perform some common development tasks while working in a least-privileged environment:

  • Need to read from a HKEY_LOCAL_MACHINE key in the registry? Use RegOpenKeyEx() (not RegOpenKey()) and request KEY_READ (not KEY_ALL_ACCESS) privileges.
  • Need to save per-user data which will roam with the user (i.e. may be replicated to the network)? Get the path to the Application Data\ folder by either retrieving the value of the environment variable %APPDATA% or calling SHGetFolderPath() with the parameter CSIDL_APPDATA. The recommended directory in which to store files appears to be %APPDATA%\<Vendor>\<Product>\, e.g. %APPDATA%\Microsoft\Office\.
  • Need to save per-user data which will not roam with the user (e.g. a web browser cache)? Call SHGetFolderPath() with the parameter CSIDL_LOCAL_APPDATA.
  • Need to save per-machine data (e.g. machine-wide game high scores)? Use %ALLUSERSPROFILE% or SHGetFolderPath() with the parameter CSIDL_COMMON_APPDATA.
  • Need a place to store a temporary file? Use %TEMP% or GetTempPath(). GetTempFileName() may also be useful.

To help with the transition, Microsoft’s Application Verifier may prove a useful tool.

Use RAII

C++, Win32 No Comments »

This is covered by any halfway-decent C++ book, but I believe it deserves reiteration: Use the RAII idiom. I don’t think I could explain RAII any better than HackCraft does in The RAII Programming Idiom.

Let me demonstrate how to use RAII with a semi-contrived example. Here’s the pre-RAII code:

HMODULE hm = LoadLibrary(_T("user32.dll"));
if (hm != NULL) {
    FARPROC proc = GetProcAddress(hm, "MessageBoxW");
    if (proc != NULL) {
        typedef int (WINAPI *FnMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);

        FnMessageBoxW fnMessageBoxW = (FnMessageBoxW) proc;
        fnMessageBoxW(NULL, L"Hello World!", L"Hello World", MB_OK);
    }
    FreeLibrary(hm);
}

In this case, the resource wrapped is HMODULE, the resource acquisition function is LoadLibrary(), and the resource release function is FreeLibrary(). Beware of resources which have multiple resource release functions, such as Win32’s HANDLE with FindClose() and CloseHandle(); for these cases you will typically have to write multiple RAII classes. I will call the wrapper class HModule. Here’s how its use will look:

HMODULE hm = LoadLibrary(_T("user32.dll"));
HModule hm(LoadLibrary(_T("user32.dll")));
if (hm != NULL) {
    FARPROC proc = GetProcAddress(hm, "MessageBoxW");
    if (proc != NULL) {
        typedef int (WINAPI *FnMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);

        FnMessageBoxW fnMessageBoxW = (FnMessageBoxW) proc;
        fnMessageBoxW(NULL, L"Hello World!", L"Hello World", MB_OK);
    }
    FreeLibrary(hm);
}

// FreeLibrary() will happen automatically when hm
// goes out of scope

Things to note:

  1. I replaced the assignment statement HMODULE hm = LoadLibrary(_T("user32.dll")); with the construction statement HModule hm(LoadLibrary(_T("user32.dll")));. On newer compilers this may not be necessary.
  2. HModule needs to handle the case where LoadLibrary() fails—it must not call FreeLibrary() on an invalid HMODULE. Be careful of types such as HANDLE as it uses both NULL and INVALID_HANDLE_VALUE to denote invalid values.
  3. HModule should be transparently convertable to a HMODULE. This implies either inheriting from HMODULE or, preferably, providing a conversion operator to HMODULE. (Some resource wrapping classes, like STL’s std::basic_string, prefer a method rather than a conversion operator (c_str() in this case), probably to minimize unexpected, implicit conversions. The choice is up to you.)
  4. If a user calls FreeLibrary(hm); that will result in a double-free, a potentially dangerous bug. I don’t think there’s a whole lot that can be done about this besides saying “Don’t do that!”
  5. We need to worry assignment and the copy constructor: the default implementations will lead to double-free bugs. A few solutions are: disabling the operators, but this makes the class less useful than it could be; turning assignment/copy construction into transfer of ownership like STL’s std::auto_ptr, but this causes some unexpected semantics; or using reference counting like Boost’s shared_ptr, but this adds often-unneeded complexity and, by itself, introduces the problem of circular references. For simplicity, I will simply choose to disable assignment and the copy constructor.

Given these observations, here’s my version of HModule:

class HModule
{
private:
    HMODULE m_hm;

public:
    explicit HModule(HMODULE hm) : m_hm(hm) {}
    ~HModule() { if (m_hm) { FreeLibrary(m_hm); }}
    operator HMODULE() const { return m_hm; }

private:
    // Disable copy construction and assignment
    HModule(const HModule& hm);
    HModule& operator=(const HModule& hm);
};

Obviously, there’s a lot more that can be done to HModule, such as supporting release of ownership, reassignment, etc. However, by using RAII classes like HModule exclusively, you will immediately reap the gains of less thought about resource management, fewer bugs, and you will be one step closer towards exception safety.

BTW, if you don’t want to go to the trouble of writing RAII classes, there’s also ScopeGuard.

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.

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