Click here to Skip to main content
Click here to Skip to main content

Windows ATOM API Wrapper

By , 12 May 2007
 

Introduction

I had the need to use the GlobalAddAtom and GlobalDeleteAtom functions and read in the MSDN description that the caller must remember to match the calls to these routines.

Whenever I read comments like that, I immediately think RAII. So here are the wrappers for those routines and all the rest as a bonus.

Background

My usage of these wrappers required a specific breaking of these rules, but I won't bore you with the details of my project. These wrapper classes handled the special case easily.

Using the Code

To use any of the wrapper classes, you may either call the static functions directly, declare a typedef of the particular kind you require, or simply use the base types as is.

The following code demonstrates all of the above described use cases and gives a hint as to the reason I wanted to break the rules outline in the MSDN regarding matching up pairs of those calls.

// AtomTest.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include "win32Atom.hpp"

typedef win32::IntegerAtom<win32::GlobalAtom>    GlobalIntegerAtom;

int main(int argc, CHAR* argv[])
{
    static TCHAR const* atomName = _T("AtomTest");
    static unsigned short int intAtomNum = 254;
    win32::GlobalAtom ga;
    
    if (argc > 1) {
        GlobalIntegerAtom gia(intAtomNum);

        switch (toupper(argv[1][0]))
        {
        case _T('A'):
            ga.Increment(atomName);
            if (ga) {
                _tprintf(_T("Added %s\n"), atomName);
            }
            else {
                _tprintf(_T("Failed to add %s\n"), atomName);
            }
            break;
        case _T('D'):
            ga = win32::GlobalAtom::Find(atomName);
            if (ga) {
                ga.Decrement();
                if (!ga) {
                    _tprintf(_T("Deleted %s\n"), atomName);
                }
                else {
                    _tprintf(_T("Failed to delete %s\n"), atomName);
                }
            }
            else {
                _tprintf(_T("%s not found\n"), atomName);
            }
            break;
        case _T('F'):
            if (win32::GlobalAtom::Find(atomName)) {
                _tprintf(_T("Found %s\n"), atomName);
            }
            else {
                _tprintf(_T("%s not found\n"), atomName);
            }
            break;
        case _T('I'):
            if (gia.Find(intAtomNum)) {
                _tprintf(_T("Found integer atom\n"));
            }
            else {
                _tprintf(_T("Integer atom not found\n"));
            }
            if (++gia) {
                _tprintf(_T("Added integer atom\n"));
            }
            if (GlobalIntegerAtom::Find(intAtomNum)) {
                _tprintf(_T("Found integer atom\n"));
            }
            else {
                _tprintf(_T("Integer atom not found\n"));
            }
            if (--gia) {
                _tprintf(_T("Deleted integer atom\n"));
            }
            else {
                _tprintf(_T("Failed to delete integer atom\n"));
            }
            break;
        }
    }
    return 0;
}

Here is the win32Atom.hpp file in its entirety:

#if !defined(WIN32ATOM_HPP)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// win32Atom.hpp
//
// Windows ATOM API functions wrapper classes.
//
// LocalAtom    : Atoms local to the process
// GlobalAtom    : Atoms persist for the duration of the login session
// StringAtom<AtomType>        : String atoms
// IntegerAtom<AtomType>    : Integer atoms. Can neither be created nor destroyed
//    AtomType is either LocalAtom or GlobalAtom
//
// Author: David 'dex' Schwartz
// dex at ieee dot org
// 2007.05.12
//
// Free to copy, use, modify and distribute.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <windows.h>
#include <stdexcept>
#include <TCHAR.H>

namespace win32 {

class LocalAtom
{
private:
    ATOM mAtom;

public:
    LocalAtom(LocalAtom const& that)
        : mAtom(that.mAtom)
    {}

    LocalAtom(ATOM a = 0)
        : mAtom(a)
    {}

    ATOM Increment()
    {
        std::basic_string<TCHAR> atomName;
        UINT len = GetName(mAtom, atomName);
        return Increment(atomName.c_str());
    }

    ATOM Increment(LPCTSTR atomName)
    {
        ATOM a = Find(atomName);
        if (a != mAtom)
        {
            Decrement();
        }
        mAtom = Add(atomName);
        return mAtom;
    }

    ATOM Decrement()
    {
        if (mAtom)
        {
            mAtom = ::DeleteAtom(mAtom);
        }
        return mAtom;
    }

    operator bool() const
    {
        return mAtom != 0;
    }

    operator ATOM() const
    {
        return mAtom;
    }

    LocalAtom& operator=(LocalAtom const that)
    {
        if (this != &that)
        {
            mAtom = that.mAtom;
        }
        return *this;
    }

    LocalAtom& operator=(ATOM const a)
    {
        mAtom = a;
        return *this;
    }

    static UINT GetName(ATOM const a, std::basic_string<TCHAR>& atomName)
    {
        TCHAR name[256];
        UINT len = ::GetAtomName(a, name, sizeof(name)/sizeof(TCHAR));
        atomName.assign(name, len);
        return len;
    }

    static ATOM Find(LPCTSTR atomName)
    {
        return ::FindAtom(atomName);
    }

    static ATOM Add(LPCTSTR atomName)
    {
        ATOM a = 0;
        if (atomName)
        {
            a = ::AddAtom(atomName);
        }
        return a;
    }

    // Number of hash buckets to use in local atom table
    static bool Initialise(DWORD n)
    {
        BOOL ok = ::InitAtomTable(n);
        return FALSE != ok;
    }
};

class GlobalAtom
{
private:
    ATOM mAtom;

public:
    GlobalAtom(ATOM a = 0)
        : mAtom(a)
    {}

    GlobalAtom(GlobalAtom const& that)
        : mAtom(that.mAtom)
    {}

    ATOM Increment()
    {
        std::basic_string<TCHAR> atomName;
        UINT len = GetName(mAtom, atomName);
        return Increment(atomName.c_str());
    }

    ATOM Increment(LPCTSTR atomName)
    {
        ATOM a = Find(atomName);
        if (a != mAtom)
        {
            Decrement();
        }
        mAtom = Add(atomName);
        return mAtom;
    }

    ATOM Decrement()
    {
        if (mAtom)
        {
            mAtom = ::GlobalDeleteAtom(mAtom);
        }
        return mAtom;
    }

    operator bool() const
    {
        return mAtom != 0;
    }

    operator ATOM() const
    {
        return mAtom;
    }

    GlobalAtom& operator=(GlobalAtom const that)
    {
        if (this != &that)
        {
            mAtom = that.mAtom;
        }
        return *this;
    }
    
    GlobalAtom& operator=(ATOM const a)
    {
        mAtom = a;
        return *this;
    }

    static UINT GetName(ATOM const a, std::basic_string<TCHAR>& atomName)
    {
        TCHAR name[256];
        UINT len = ::GlobalGetAtomName(a, name, sizeof(name)/sizeof(TCHAR));
        atomName.assign(name, len);
        return len;
    }

    static ATOM Find(LPCTSTR atomName)
    {
        return ::GlobalFindAtom(atomName);
    }

    static ATOM Add(LPCTSTR atomName)
    {
        ATOM a = 0;
        if (atomName)
        {
            a = ::GlobalAddAtom(atomName);
        }
        return a;
    }
};

template <typename AtomType>
class StringAtom : public AtomType
{
private:
    std::basic_string<TCHAR> mAtomName;

public:
    StringAtom(LPCTSTR atomName = NULL)
        : mAtomName(atomName ? atomName : _T(""))
    {
        if (atomName && !mAtomName.empty())
        {
            Increment(mAtomName.c_str());
        }
    }

    ~StringAtom()
    {
        Decrement();
    }

    std::basic_string<TCHAR>& Name()
    {
        if (mAtom && mAtomName.empty())
        {
            AtomType::GetName(mAtom, mAtomName);
        }
        return mAtomName;
    }

    StringAtom& operator++()
    {
        if (!mAtomName.empty())
        {
            mAtomName = AtomType::Increment(mAtomName.c_str());
        }
        return *this;
    }

    StringAtom& operator--()
    {
        ATOM ret = 0;
        if (AtomType::operator bool())
        {
            ret = AtomType::Decrement();
        }
        return *this;
    }
};

template <typename AtomType>
class IntegerAtom : public AtomType
{
private:
    unsigned short int mAtomInt;

public:
    IntegerAtom(unsigned short int n)
        : mAtomInt(n)
    {
        if (mAtomInt < MAXINTATOM)
        {
            Increment(MAKEINTATOM(mAtomInt));
        }
        else
        {
            throw std::range_error(std::string("Integer atom greater than 0xBFFF"));
        }
    }

    ~IntegerAtom()
    {}

    IntegerAtom& operator++()
    {
        return *this;    // Has no meaning for integer atoms
    }

    IntegerAtom& operator--()
    {
        return *this;    // Has no meaning for integer atoms
    }

    UINT Name(std::basic_string<TCHAR>& atomName)
    {
        UINT len = 0;
        if (mAtom)
        {
            len = AtomType::GetName(mAtom, atomName);
        }
        return len;
    }

    operator bool() const
    {
        return AtomType::operator bool();
    }

    static ATOM Find(unsigned short int n)
    {
        return AtomType::Find(MAKEINTATOM(n));
    }
};

} // namespace win32

//typedef win32::StringAtom<win32::GlobalAtom>    GlobalStringAtom;
//typedef win32::IntegerAtom<win32::GlobalAtom>    GlobalIntegerAtom;

#endif // WIN32ATOM_HPP

Points of Interest

The code throws a std::range_error for integer atoms higher than the maximum value allowed.

I have provided increment and decrement operators for both string and integer atom types. Even though the integer ones don't do anything, they act as a form of documentation.

History

  • 12th May, 2007: Version 1.0

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

David 'dex' Schwartz
Team Leader
Australia Australia
Member
Developing various kinds of software using C/C++ since 1984 or so. Started out writing 8086 asm for direct screen i/o and mouse handling etc.
Used several other languages eg. Java, Python, Clipper/dBase, FORTRAN 77, Natural ADABAS, Unix scripting, etc.
My current work involves Enterprise Content Management on Win32.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionWrong value after assignment?memberehaerim10 Mar '09 - 9:27 
I modified the finding part a bit like:
 
case _T('F'):
{
ATOM TempAtom;
ga = win32::GlobalAtom::Find(atomName);
if (ga) {
_tprintf(_T("Found %s, Atom=0x%04X\n"), atomName, ga); // Before TempAtom=ga;
TempAtom = ga;
_tprintf(_T("Found %s, Atom=0x%04X => 0x%04X\n"), atomName, TempAtom, ga); // Found AtomTest, Atom=0xC068 => 0x12C068 => ga prints wrong value. Why?
}
else {
_tprintf(_T("%s not found\n"), atomName);
}
break;
}
 
After the second print, the value of ga was different. I don't understand why.
 
Can someone explain why?
AnswerRe: Wrong value after assignment?memberDavid 'dex' Schwartz17 Mar '09 - 2:06 
Perhaps it's because ga isn't an integer value, it is a class.
_tprintf doesn't cast it's parameters to match the expected type in the format string.
 
In other words ga didn't change value _tprintf didn't receive an integer.
 
Try passing (ATOM)ga instead.
 
Keep it simple
dex

GeneralRe: Wrong value after assignment?memberehaerim17 Mar '09 - 3:40 
But then how TempAtom print 0xC068 without casting it to (ATOM) at all?
GeneralRe: Wrong value after assignment?memberDavid 'dex' Schwartz17 Mar '09 - 23:46 
ATOM is WORD which is (unsigned short).
So TempAtom is ALREADY an integer type.
 
Keep it simple
dex

GeneralRe: Wrong value after assignment?memberDavid 'dex' Schwartz18 Mar '09 - 0:27 
What you need to ask yourself are two questions.
 
1. Is sizeof(ga)==sizeof(int) answer NO
2. What is sizeof(ga) ? answer 2
 
so %X format expects an integer argument sizeof(int) == 4
 

Final answer. Write correct code and use correct casts where necessary.
e.g. static_cast<int>(TempAtom), static_cast<int>((ATOM)ga)
 
It is only a fluke of the variable argument passing implementation that the TempAtom expression prints correctly at all in the second _tprintf statement.
 
Keep it simple
dex

GeneralAn oldiemvpHans Dietrich12 May '07 - 10:19 
I haven't seen this much atom code for a long while. Just out of curiosity, what are you using them for?
 
Suggestion: combine and/or derive the local and global atom classes - they're very similar.
 

AnswerRe: An oldie [modified]memberDavid 'dex' Schwartz12 May '07 - 18:41 
Hans.
The only things I could find similar were the mAtom member the default and copy constructors, bool conversion and the assignment operator. The rest use routines specific to the local or global atom tables.
Introducing another layer of inheritance just doesn't seem justified, especially when doing so would tend to imply the two types of ATOM are somehow interchangeable, which they most definitely are not.
You are free to argue the case though, if you feel the need Smile | :)
 

 

 

-- modified at 0:48 Sunday 13th May, 2007
 
Keep it simple
dex

AnswerRe: An oldiememberDavid 'dex' Schwartz12 May '07 - 18:46 
What am I using them for?
As a cheap alternative to a semaphore to work around a problem with folder view drag and drop in a shell namespace extension for Explorer.
 

 
Keep it simple
dex

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 12 May 2007
Article Copyright 2007 by David 'dex' Schwartz
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid