Welcome to MSDN Blogs Sign in | Join | Help

Other problems traced to violating COM single-threaded apartment rules in the shell

Probably the biggest category of problems that can be traced to violating COM single-threaded apartment rules in the shell is using an object from the wrong thread. Of course, nobody admits to doing this up front, They just report that the shell is broken.

We can't enumerate the items on the desktop any more. We take the pointer returned by SHGetDesktopFolder and call IShellFolder::EnumObjects, but no objects come out. This code used to work on Windows XP.

There isn't enough information to diagnose the problem, and if you just do what they claim doesn't work, you find that it works:

#include <windows.h>
#include <ole2.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <stdio.h>
#include <tchar.h>

INT __cdecl
_tmain(
    INT iArgc,
    __in_ecount(iArgc) PTSTR ppszArgv[]
    )
{
 if (SUCCEEDED(CoInitialize(NULL))) {
  IShellFolder *psf;
  if (SUCCEEDED(SHGetDesktopFolder(&psf))) {
   IEnumIDList *peidl;
   if (SUCCEEDED(psf->EnumObjects(NULL, SHCONTF_FOLDERS |
                         SHCONTF_NONFOLDERS, &peidl)) && peidl) {
    LPITEMIDLIST pidl;
    while (peidl->Next(1, &pidl, NULL) == S_OK) {
     STRRET str;
     if (SUCCEEDED(psf->GetDisplayNameOf(pidl,
                                         SHGDN_NORMAL, &str))) {
      TCHAR sz[MAX_PATH];
      if (SUCCEEDED(StrRetToBuf(&str, pidl, sz, MAX_PATH))) {
       _tprintf(TEXT("%s\n"), sz);
      }
     }
     ILFree(pidl);
    }
   }
   psf->Release();
  }
  CoUninitialize();
 }
 return 0;
}

When given this simple program that does what they claim doesn't work, the customer explained that they cache the desktop folder. It works for a while, and then stops working. The code is complicated, so they haven't been able to isolate the problem yet. They did find that if they didn't cache the pointer and just called SHGetDesktopFolder each time they needed it, then they didn't have the problem.

I never got a confirmation, but I'm pretty sure that they are violating COM apartment threading model rules and obtaining the desktop folder obtained on one thread, then using it on another. Apartment model rules specify that you must use an object on the same thread that created it. If you want to use it on another thread, you have to use a helper function like CoMarshalInterThreadInterfaceInStream. If you just dive in and use it on another thread (known informally as "smuggling"), then all sorts of strange things happen. In this case, the folder can't enumerate objects any more.

Moral of the story: Stick to the rules for COM objects. If you don't, you may get away with it for a little while, but someday your sins may catch up to you.

Published Friday, October 19, 2007 7:00 AM by oldnewthing
Filed under:

Comments

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 10:38 AM by Igor

I just love it when such a simple COM example needs 6 levels of indentation :)

[A flat API would have the same amount of indentation. It's the error checking that causes the indentation, not COM. But you knew that; you're just being snide. -Raymond]

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 11:06 AM by Robert

> It's the error checking that causes the indentation, not COM.

The question is whether all these opportunities for failure are really necessary. For example, StrRetToBuf can probably be designed in a way that it will not fail if it is passed a valid result from a successful call to GetDisplayNameOf. In that case, the code could become simpler and there would be no need to include a code path that is probably never tested anyway.

[Is there a way to guarantee that WideCharToMultiByte will never fail? -Raymond]

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 12:17 PM by Chad

Complaining about indentation?  Sheesh.  Have you ever heard of guard clauses?

http://c2.com/cgi/wiki?GuardClause

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 12:35 PM by John

If more people actually went through the trouble of checking return values like that then the world would be a better place.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 1:16 PM by Henrik

Poor Raymond,

if he excludes error checking people complain,

if he includes error checking people complain.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 1:56 PM by Gazpacho

If only Microsoft could publish the source code of the COM runtime, maybe people would finally understand how it works.

[Here it is: "". When you create an in-apartment object, there is no COM runtime involved. You're talking directly to the object. -Raymond]

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 2:48 PM by Leo Davidson

If you want to understand something complex, documentation is usually better than source code. Having the source is a bonus when you need to resolve an ambiguity in the documentation but having better documentation is preferable, I think.

There is a lot of documentation on COM. The problem is that people aren't always reading/finding/understanding/remembering it. I don't know which of those four things is most to blame but I don't think any of them would be solved by having the source code behind COM unless the comments in the code are better than what you'd find in a book (I doubt that).

Having said that, I have yet to read a good explanation of COM threading and apartments. They seem to be something that is very difficult to explain, even for experts in the field. (I think Don Box said something like that himself but I may have a faulty memory.) Maybe I just never found the right article or book, though. (I think I understand them now but it's the result of piecing together a lot of different articles.)

# That's because nobody understands COM threading models

Friday, October 19, 2007 3:44 PM by Steve Loughran

Let's be honest. Who really understands COM's apartment model. Really? I'd view those people who admit to not understanding it to being honest, and either new to COM, or experienced enough to admit that it scares them.

I think only Don Box understood it all, and even then, its been long enough that that he's forgotten the details.

For anyone who does claim to understand COM, ask them what happens when a COM call triggers a C++ exception in the invoked class.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 4:06 PM by John

Straight from the COM spec:

<i>But before we get into error handling in COM, we’ll first take a small digression. Many readers might here be wondering about exceptions. How do exceptions relate to interfaces? In short, it is strictly illegal to throw an exception across an interface invocation; all such cross-interface exceptions which are thrown are in fact bugs in the offending interface implementation.</i>

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 4:09 PM by Nick

Another milestone!

REDMOND  Today Microsoft Corporation open-sourced the code to their veritable COM runtime.  This marks another step into the OSS realm by the software giant.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 5:28 PM by Cooney

I've heard that apartment model restricts the threads that use a particular object - why is this? I can deal with it being so, I just want to know why it was done this way.

[To make it easier for COM object implementors. If you think you're wicked smart, you can write a free-threaded COM object. If you think you're wicked stupid, you can write a main-threaded COM object. -Raymond]

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 6:40 PM by Cooney

Yeah, I was sort of hoping that someone had a link to a discussion of the tradeoffs in a bit more depth. Since we're on the sibject and all.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 8:27 PM by Maurits

> SUCCEEDED(CoInitialize(NULL))

GAH holy function calls in macros Batman!

Yes, I know SUCCEEDED only evaluates its argument once... so you got away with it that time.

(I see HRESULT_FROM_WIN32(GetLastError()) a lot too... that works *even though HRESULT_FROM_WIN32 evaluates its argument multiple times* because the return value is consistent over the life of the macro.)

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 9:56 PM by MS

"If only Microsoft could publish the source code of the COM runtime, maybe people would finally understand how it works."

I'm working on a lot of projects that are entirely F/OSS/GNU/OMGLINUX and I really don't have the time to dig through the 18 layers of source code to figure out a niggling detail.  I'd rather that they give a good manual instead.  This isn't a pot shot at Microsoft at all, because they actually have documentation, and while uneven, it usually gets the point across.

Further, everyone has access to the Linux kernel, but that doesn't mean everyone understands how OSes work.  It takes a lot of work and academic study to "get it."  So stop pouting about OSS and OMGNU crap.

As for the indentation, I guess error checking is a good thing.  I got used to it when I was writing a whole bunch of COM addins and it isn't that hard.  I even *gasp* refactored my code to make it less offensive.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Friday, October 19, 2007 10:10 PM by Triangle

"I'm working on a lot of projects that are entirely F/OSS/GNU/OMGLINUX and I really don't have the time to dig through the 18 layers of source code to figure out a niggling detail.  I'd rather that they give a good manual instead."

Or, you could put the manual as part of the comments in the source code. Like in Java.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 8:48 AM by KJK::Hyperion

The shell outright refuses to load free-threaded or apartment-neutral components as shell extensions. Just so you know

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 11:41 AM by mt

Isn't the shell multithreaded compatible?

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 12:36 PM by Triangle

mt: No, it isn't. See yesterdays blog entry.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 1:03 PM by Igor

I wasn't complaining about error checking or about indentation.

I just said that there are too many things which can fail in such a simple code sample.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 1:33 PM by Triangle

"I just said that there are too many things which can fail in such a simple code sample."

Well, some of the error checking could be extraneous. Like for example, I don't know how SHGetDesktopFolder(&psf) could fail, since all it does (assumedly) is copy a pointer stored somewhere in the bowels of the shell into psf. I know that would be "just an implementation detail", but that's how people think and you better not let that function fail unless you don't want to avoid keeping programs from crashing.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 3:02 PM by Yuhong Bao

I guess COM is complex, and you learn about many of the complexities the hard way.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 3:30 PM by Justin Olbrantz (Quantam)

I imagine it's possible for there to be a program running on a session without a graphic device (e.g. services, though I don't know if this is actually the case). In which case there would be no desktop.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 5:48 PM by James

"Or, you could put the manual as part of the comments in the source code. Like in Java."

Which is fine for function-specific documentation (like 'what are the arguments to DoStrangeThingWithPointers(..)?' or 'What's the return value for WeirdFunction()?') - but there's a nasty chicken and egg problem: how do I find the function I actually need to do a job?

Just look back a few stories to the mouse-button-swapping problem. The programmer had used a function with a nice obvious name, SwapMouseButton(), which was documented as returning the value he needed, with a documented side-effect he reversed straight afterwards if needed. How was he supposed to know the right function was actually GetSystemMetrics(), which has no obvious connection to swapping mouse buttons? Yes, looking at GetSystemMetrics would have told him - but only if he'd known already which function to look up!

OK, in this specific case Peter has now added 'Community Content' to the online MSDN page documenting this, to stop anyone else falling into the same trap, but how many more issues like this are lurking out there? How many more programmers fell into this trap in the decade or so between the function being released and Peter putting a warning online? (Just a quick glance across Raymond's blog finds another example, someone using a global mouse hook to identify idle time when they should apparently use GetLastInputInfo instead.)

It's a shame - I found a nice trick in Swing with a watchdog thread, which switched on an hourglass every time the event despatch thread went out to lunch for more than a second or so. Of course, Swing was routing everything into a single thread for UI calls behind the scenes, but it was nice and easy to program because that was all taken care of automatically. Try the same in SWT, it blows up in your face; in Win32...?

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 6:42 PM by KJK::Hyperion

Maurits: HRESULT_FROM_WIN32 is now an inline function

# God, I am sick of the argumentative whiners on this blog.

Saturday, October 20, 2007 8:44 PM by Andrew

I am talking about all you relentless nitpickers who pounce on Raymond almost every day.  I think you guys have inferiority complexes, and taking potshots at one of those rare exceptions to the corporate wall of Microsoft makes you feel important.  "Look at me!  I am not afraid to stick to to Microsoft!"  Pathetic!

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Saturday, October 20, 2007 10:16 PM by Justin Olbrantz (Quantam)

"I am talking about all you relentless nitpickers who pounce on Raymond almost every day.  I think you guys have inferiority complexes, and taking potshots at one of those rare exceptions to the corporate wall of Microsoft makes you feel important.  "Look at me!  I am not afraid to stick to to Microsoft!"  Pathetic!"

Hell yes. I am amazed every day that Raymond doesn't, like any normal person would, go insane and kill a bunch of people with a sharp object from the incessant needling these idiots give him.

If I were running this site, I would make a Hall of Shame with a list of every single nitpicking idiot that drops by, along with their e-mail addresses (for those that make their e-mail address available) and links to their blogs (for those that have blogs with comments enabled).

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Sunday, October 21, 2007 8:23 AM by Triangle

Saturday, October 20, 2007 10:16 PM by Justin Olbrantz (Quantam)

"Hell yes. I am amazed every day that Raymond doesn't, like any normal person would, go insane and kill a bunch of people with a sharp object from the incessant needling these idiots give him."

So nitpicking on a blog is acceptable grounds for murder to you?

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Sunday, October 21, 2007 10:40 AM by Jules

"If you want to understand something complex, documentation is usually better than source code. Having the source is a bonus when you need to resolve an ambiguity in the documentation but having better documentation is preferable, I think."

An ambiguity, or something that just isn't mentioned.  Or is just plain wrong.  See, for instance, the documentation for CreateWindowEx ( http://msdn2.microsoft.com/en-us/library/ms632680.aspx ).  Now tell me how to use the lpParam parameter when the extended style includes WS_EX_MDICHILD?

Now.  Go read the source code for Wine's version of the function at http://source.winehq.org/source/dlls/user32/win.c#L855 .

You see how it works differently to what the documentation suggests?  It's the same in Windows, too.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Sunday, October 21, 2007 12:01 PM by Wilfried

This coding style of rigorous error checking also makes it easy to spot interface leaks. Because each Release has its "natural" place. Like the missing peidl->Release() in Raymonds sample.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Sunday, October 21, 2007 2:21 PM by Igor

"Like the missing peidl->Release() in Raymonds sample."

/sarcasm on

Yes, that was really easy to spot.

/sarcasm off

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Sunday, October 21, 2007 3:33 PM by Justin Olbrantz (Quantam)

"So nitpicking on a blog is acceptable grounds for murder to you?"

*ambiguous yet unsettling grin*

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 12:30 AM by Merus

"So nitpicking on a blog is acceptable grounds for murder to you?"

Of course, what a silly question. How will people learn proper polite behaviour otherwise, seeing as they rarely even read the post?

Admittedly I'd probably just kneecap people instead of murdering them, but I'm a big ol' softy.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 5:53 AM by Goran

I think, by far the worst problem with COM is that _implementation_ has been refined over time, and wrt threading, too. Initially, people were doing strange faulty things and getting away with them. And when it started biting back, of course they were surprised. It's an upside-down way to refine software really, it should go from stricter to lax.

In COM, there's _staggering_ amount of low-level details that many people don't even see. Like with threading, or memory allocation rules wrt marshaling.

Then, if you used MFC's ( crap ;-) ) support for IDispatch, it changes greatly your view of interaction with COM, as it wraps all in "programmer-friendly" calls, along with data type changes (e.g. from BSTR to LPCTSTR for a BSTR idl property, VARIANT_BOOL to BOOL), or automatic protection against MFC exception.

Now, throw in use of any C++ library that needs exceptions (look no further than STL, that doesn't work without exceptions), and you're in for a lot of pain.

Sorry for ranting. Just wanted a say for a little guy.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 7:36 AM by Dean Harding

Goran: COM *did* go from "more restrictive" to "more lax" with time. The first threading model was the STA -- about as "strict" as you can get, in my opinion...

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 7:50 AM by me

[A flat API would have the same amount of indentation. It's the error checking that causes the indentation, not COM. But you knew that; you're just being snide. -Raymond]

Is the 'goto to cleanup' method considered bad style at Microsoft? It does save you all this indentation.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 9:33 AM by 640k

Would the company that invented BASIC think GOTO statement is a bad programming style?

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 10:04 AM by John

@Jules:  I don't follow you; the documentation and WINE source seem to be in agreement.

The documentation says that the WS_EX_MDICHILD flag is used to create a MDI child window.  Then it says that if you are creating a MDI child window you should pass a pointer to a MDICREATESTRUCT struct.  The WINE source seems to ...discussion of GNU-protected source code deleted...

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 1:15 PM by Andrew

@640k:

Microsoft didn't invent basic or goto.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 3:52 PM by Peter Ritchie

Raymond's just following structured programming practices, in particular single point of exit, and ensuring acquired resources are also freed.  Without the use of classes the only alternative is to duplicate code.

But, that's not the point of Raymond's post; but as usual the usual group of nickpickers has hijacked the topic...

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Monday, October 22, 2007 5:03 PM by Cooney

"Hell yes. I am amazed every day that Raymond doesn't, like any normal person would, go insane and kill a bunch of people with a sharp object from the incessant needling these idiots give him."

We try to keep Raymond away from sharp objects, although knitting needles seem to calm him.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Tuesday, October 23, 2007 4:20 AM by Goran

Dean: I was talking about enforcements on threading rules, not about the growth of threading models***. In that sense, before, if you cheated, you could get away thinking it's OK to do it or being completely oblivious to the issues. For an outsider and a little guy (me), it's a change for the worse.

***I think, for all the failures e.g. CORBA had, threading was simpler and more effective. (Well, it was the last time I looked into it). I know goals are not the same as in (D)COM, but still...

[It's kind of hard to enforce rules when you don't have any code running. -Raymond]

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Tuesday, October 23, 2007 2:59 PM by sandman

@john.

This isn't the right forum for this,but yes thats what the windows docs say.

But ... discussion of GNU-protected source code deleted...

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Tuesday, October 23, 2007 5:36 PM by John

Ok, I see what you're saying.  I traced the function calls in the Wine source and ... discussion of GNU-protected source code deleted ...

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Tuesday, October 23, 2007 9:16 PM by John

Anyway ... it appears you guys are correct.  The documentation and actual behavior do not match.

# re: Other problems traced to violating COM single-threaded apartment rules in the shell

Wednesday, October 24, 2007 9:31 AM by BryanK

(Nitpick: It's not GNU-protected code.  It's LGPL-protected code.  ;-) )

But yeah, I wondered how long it'd be until that got deleted, knowing what usually happens when stuff like it gets brought up.  :-)

[Thanks for the correction. It's the license that's the problem. -Raymond]
New Comments to this post are disabled
 
Page view tracker