When you're dealing with application compatibility, you discover all sorts of things that worked only by accident. Today, I'll talk about some of the "creative" ways people mess up the IUnknown::QueryInterface method.
Now, you'd think, "This interface is so critical to COM, how could anybody possible mess it up?"
- Forgetting to respond to IUnknown.
Sometimes you get so excited about responding to all these great interfaces that you forget to respond to IUnknown itself. We have found objects where
IShellFolder *psf = some object; IUnknown *punk; psf->QueryInterface(IID_IUnknown, (void**)&punk);
fails with E_NOINTERFACE!
- Forgetting to respond to your own interface.
There are some methods which return an object with a specific interface. And if you query that object for its own interface, its sole reason for existing, it says "Huh?"
IShellFolder *psf = some object; IEnumIDList *peidl, *peidl2; psf->EnumObjects(..., &peidl); peidl->QueryInterface(IID_IEnumIDList, (void**)&peidl2);
There are some objects which return E_NOINTERFACE to the QueryInterface call, even though you're asking the object for itself! "Sorry, I don't exist," it seems they're trying to say.
- Forgetting to respond to base interfaces.
When you implement a derived interface, you implicitly implement the base interfaces, so don't forget to respond to them, too.
IShellView *psv = some object; IOleView *pow; psv->QueryInterface(IID_IOleView, (void**)&pow);
Some objects forget and the QueryInterface fails with E_NOINTERFACE.
- Requiring a secret knock.
In principle, the following two code fragments are equivalent:
IShellFolder *psf; IUnknown *punk; CoCreateInstance(CLSID_xyz, ..., IID_IShellFolder, (void**)&psf); psf->QueryInterface(IID_IUnknown, (void**)&punk); CoCreateInstance(CLSID_xyz, ..., IID_IUnknown, (void**)&punk); punk->QueryInterface(IID_IShellFolder, (void**)&psf);
In reality, some implementations mess up and fail the second call to CoCreateInstance. The only way to create the object successfully is to create it with the IShellFolder interface.
- Forgetting to say "no" properly.
One of the rules for saying "no" is that you have to set the output pointer to NULL before returning. Some people forget to do that.
IMumble *pmbl; punk->QueryInterface(IID_IMumble, (void**)&pmbl);
If the QueryInterface succeeds, then pmbl must be non-NULL on return. If it fails, then pmbl must be NULL on return.
The shell has to be compatible with all these buggy objects because if it weren't, customers would get upset and the press would have a field day. Some of the offenders are big-name programs. If they broke, people would report, "Don't upgrade to Windows XYZ, it's not compatible with <big-name program>." Conspiracy-minded folks would shout, "Microsoft intentionally broke <big-name program>! Proof of unfair business tactics!"
[Raymond is currently on vacation; this message was pre-recorded.]