Is the atom returned by RegisterClass(Ex) a "real" atom?


A customer was debugging some code that calls Register­Class on a class that's already been registered. In this case, it was registered by another DLL in the same process. Normally, this wouldn't be a problem, because each DLL passes its own instance handle to Register­Class so that there are no name collisions. However, in this case, both DLLs are passing the CS_GLOBAL­CLASS flag, which means that collisions can occur after all.

The customer found that the call to ERROR_CLASS_EXISTS, which makes sense in the case of a collision in the global class table. The code then calls Find­Atom to look up the class name, but Find­Atom fails, saying, "No such atom."

Does this mean that the class atom created by Register­Class isn't a real atom? How can you get the atom for a window class that somebody else has registered?

Okay, let's look at these questions in order.

Is the atom a real atom? Well, if you ask the atom, it'll say, "I feel real to me." But remember that there are many atom tables in the system.

  • The global atom table.
  • One local atom table for each process.
  • The registered clipboard format atom table.
  • The registered window class atom table.
  • Possibly others, too.

The Find­Atom function searches the process's local atom table, so if you go looking for a registered window class atom there, you're going to come up empty.

There is no way to query the registered window class atom table directly. You only get indirect access to create items in it through Register­Class, and that atom can in turned by used as a synonym for the class name.

Now, it so happens that although the Get­Class­Info function formally returns a BOOL, i.e., an integer that is zero on failure or nonzero on success, the success case of Get­Class­Info actually returns the class atom. This behavior is not documented, but the ATL library relies on it, so if the undocumented behavior changes in the future, it'll have an app compat shim to keep ATL happy.

Comments (19)
  1. J says:

    Out if interest, how do situations like this (where there's no supported way of doing something, but there's an undocumented-yet-commonly-used solution) usually get resolved? Does the behaviour get formally documented and committed to, or is a new mechanism introduced and the undocumented solution deprecated (and potentially relegated to an app compat shim)?

    I'd guess that the answer is "both", but it would be interesting to know what approach is preferred when you have a choice.

  2. Cesar says:

    > This behavior is not documented, but the ATL library relies on it, so if the undocumented behavior changes in the future, it'll have an app compat shim to keep ATL happy.

    I wouldn't rely on it. It's possible that for instance the theoretical app compat shim only enables itself if it detects that the process uses ATL, or only does its work if it has been called by a DLL called "atlXX.dll".

    It's also possible that the app compat shim does only the minimum it has to do for ATL, and whatever ATL needs is less than what you need.

    It's ALSO possible that one of these annoying evil intercepting DLLs replaces your Get­Class­Info call with its own "shim" which returns only 0 or 1, because the DLL writer looked only at the documentation and treated it as a "bool".

    So yeah, better treat it as an opaque value as much as possible.

  3. poizan42 says:

    It's almost like someone should have realised that if ATL need the information then somebody else does, and made it officially supported. Though I'm going to guess that this is most likely a case of bad communication between teams.

  4. Eddie Lotter says:

    Raymond, a couple of problem sentences:

    "The customer found that the call to ERROR_CLASS_EXISTS" should be "The customer found that the call to Register­Class returned ERROR_CLASS_EXISTS"

    and

    "that atom can in turned by used as a synonym for the class name." should be "that atom can in turn be used as a synonym for the class name."

  5. Mike Dimmick says:

    Considering that ATL code usually winds up embedded in the EXE or DLL which consumes it, and it's usually used to create ActiveX controls that could be loaded by any process in the system, it would be a bad idea to change the return behaviour of GetClassInfoEx. Better to document it so that no-one does break it in future.

    Mind you, examining the source for ATL 6.0 (SP6), ATL 9.0 (SP1) and ATL 12.0 (Update 4), although CContainedWindowT::RegisterWndSuperclass does return an ATOM, CContainedWindowT::Create only tests whether the return value is 0. Unfortunately, CContainedWindowT::RegisterWndSuperclass is public and is documented in MSDN.

  6. I think "not documented" means "no public documentation exists for this" rather than "no documentation exists", i.e. the functionality is almost certainly documented internally within Microsoft.

  7. alegr1 says:

    Question:

    Why not make an option to pass all class info straight to CreateWindowEx, without having to register a class? Classes only make sense for reusable windows. For app windows, who cares?

  8. Azarien says:

    @alegr1: Well, then you would have two kinds of windows, reusable and one-off, with different ways of creating them. I'm not sure what is simpler.

    I always use the value returned by RegisterClass as a class name in CreateWindow, kind of "registered window class" handle.

    Makes the code prettier than using a magic string twice.

  9. skSdnW says:

    "There is no way to query the registered window class atom table directly" this is of course not 100% true, this atom table is shared with some other things and just by using public APIs you can glean useful information, for debugging purposes only of course...

  10. Sven2 says:

    @Chris Crowther: Are you implying that for all APIs, there's one version of public documentation and one version with "secret" details?

    I can understand not publishing documentation of an API at all if you don't want others to rely on it. But what would be the point of making an API public but keeping some details secret? This seems like a huge waste of resources to me.

  11. Neil says:

    > ● Possibly others, too.

    Thereby avoiding mention of registered window messages!

  12. @Sven2: Keeping track of we-need-this-for-compatibility behaviors without binding them as part of the public contract is one reason for having separate documentations.  Keep in mind this documentation could be captured in a source comment, changelog, or something more formal.  I don't think Chris was implying that Microsoft had a secret MSDN or anything like that.

  13. jonwil says:

    Why didn't they either make the function actually return a BOOL (by converting the value to TRUE or FALSE explicitly) or declare it to return an atom (which is what it actually returns)

  14. SimonRev says:

    I suspect the scenario is much less nefarious than hidden docs.  I suspect it was more along the lines like one of the ATL guys (at a much younger, smaller Microsoft) was talking to one of the Windows guys and mentioned that he needed this functionality.  The Windows guy realized he could sneak in the extra behavior without breaking the contract to Get­Class­Info and just did it.  The ATL guy used it and pretty much no one else noticed.

  15. Erik F says:

    @SimonRev: I suspect the scenario is even less nefarious than that. Back when the function was introduced memory was more precious, so I suspect that return the atom handle directly would save space and be faster than doing an additional check. Why the function was officially returning a BOOL is a mystery though; perhaps the designers didn't want to be hemmed-in by exposing an implementation detail in the future if they changed the behaviour of the function.

  16. MNGoldenEagle: That's exactly what I meant, yes :)

  17. kero says:

    About ClassName/ClassAtom table, by personal experience:

    from NT4 to Win7... we can build a table of names by scanning the range C000-FFFF via GetClipboardFormatName (not via GlobalGetAtomName), and this table will contain not only all ClipboardFormatNames, but also all Registered Window Messages and all Registered Window Classes.

    Actually formats, messages and the classes lie in different tables, but share common values: for example, if the Name of Registered WindowMessage and the Name of Registered WindowClass are equal, then its return values (atoms) are equal also.

    And so we can get ClassName/ClassAtom via GetClipboardFormatName/GetClassInfoEx, respectively.

    [And then the window manager team decides to split them into separate atom tables since lumping them into one table is getting full, and now your app breaks. -Raymond]
  18. kero says:

    > [And then the window manager team decides...]

    Oh, Raymond, "from NT4 to Win7" = 20 long years, I'm afraid I (with my hypothetical app) will not live to the moment "And then" :)

  19. Matt says:

    Serious question: Why doesn't Microsoft just change the return value of this function to ATOM in Windows.h, and take this as a formal dependency? There's no app-compat risk in this case, since code that previously relied on the function returning a BOOL will silently continue to work, but it will avoid the undocumented behavior by documenting it in the return type of the function itself.

    [sizeof(ATOM) != sizeof(BOOL) so this would be a breaking change. -Raymond]

Comments are closed.

Skip to main content