Discussion:
MFC Icon Handler's IExtractIcon::GetIconLocation() never gets call
(too old to reply)
nomad
2007-08-01 22:32:04 UTC
Permalink
I'm using the MFC COM macros to implement an Icon Handler. The DLL gets
loaded, I eventually get a call to QueryInterface() (actully,
IPersistFile::QueryInterface()), and eventually a call to
IPersistFile::Load(). Next, I get another IPersistFile::QueryInterface call
which returns a pointer to the IExtractIcon interface, but none of the
IExtractIcon methods ever get called (except for AddRef() and Release());
specifically, GetIconLocation() is never called, and neither is ExtractIcon().

I've run this in the VS debugger, attaching to the explorer window and
putting breakpoints on all of my rouintes. Everything *seems* fine, except
that IExtractIcon never gets called.

I have to assume that I've done something wrong somewhere. My class
declaration looks like this:

class CCMIconHandler : public CWinApp
{
private: typedef CWinApp inherited;

public:
DECLARE_DYNCREATE(CCMIconHandler);
DECLARE_OLECREATE(CCMIconHandler);

CCMIconHandler (void);
virtual ~CCMIconHandler (void);

virtual BOOL InitInstance (void);

virtual int ExitInstance (void);

protected:
DECLARE_INTERFACE_MAP()

/*
IPersistFile.
*/
BEGIN_INTERFACE_PART(PersistFile, IPersistFile)

STDMETHOD_(HRESULT, GetClassID) (
CLSID* pClassID);

STDMETHOD_(HRESULT, Load) (
LPCOLESTR pszFileName,
DWORD dwMode);

STDMETHOD_(HRESULT, GetCurFile) (
LPOLESTR* pszFileName);

STDMETHOD_(HRESULT, IsDirty) (void);

STDMETHOD_(HRESULT, Save) (
LPCOLESTR pszFileName,
BOOL fRemember);

STDMETHOD_(HRESULT, SaveCompleted) (
LPCOLESTR pszFileName);

END_INTERFACE_PART(PersistFile)

/*
IExtractIcon
*/
BEGIN_INTERFACE_PART(ExtractIcon, IExtractIcon)

STDMETHOD_(HRESULT, Extract) (
LPCTSTR pszFile,
UINT nIconIndex,
HICON* phiconLarge,
HICON* phiconSmall,
UINT nIconSize);

STDMETHOD_(HRESULT, GetIconLocation)(
UINT uFlags,
LPTSTR smIconFile,
UINT cchMax,
int* piIndex,
UINT* pwFlags);

END_INTERFACE_PART(ExtractIcon)

protected:
CString fFileName; // Name of file for which icon is requested.

};

The code (more or less) looks like this:

/*
Allow dynamic creation of an object of this class.
*/
IMPLEMENT_DYNCREATE(CCMIconHandler, CWinApp);

/*
Implement a class factory for this object.
*/
IMPLEMENT_OLECREATE(CCMIconHandler, "CM Icon Handler",
0x897d3ada, 0xc314, 0x4e2e,
0xa9, 0x25, 0x84, 0x3b, 0x57, 0x82, 0x22, 0xce);

/*
Declarations for COM interface...
*/
BEGIN_INTERFACE_MAP(CCMIconHandler, CWinApp)
INTERFACE_PART(CCMIconHandler, IID_IPersistFile, PersistFile)
INTERFACE_PART(CCMIconHandler, IID_IExtractIcon, ExtractIcon)
END_INTERFACE_MAP()
;

/*
The one and only CCMIconHandler object
*/
CCMIconHandler theApp;

STDAPI
DllCanUnloadNow(void)
{
/*
Required for dynamic linking with MFC.
*/
AFX_MANAGE_STATE(::AfxGetStaticModuleState());

return (::AfxDllCanUnloadNow());

} // DllCanUnloadNow


STDAPI
DllGetClassObject(
REFCLSID rclsid,
REFIID riid,
LPVOID* ppv)
{
/*
Required for dynamic linking with MFC.
*/
AFX_MANAGE_STATE(::AfxGetStaticModuleState());

return (::AfxDllGetClassObject(rclsid, riid, ppv));

} // DllGetClassObject


STDAPI
DllRegisterServer(void)
{
/*
Required for dynamic linking with MFC.
*/
AFX_MANAGE_STATE(::AfxGetStaticModuleState());

HRESULT result(S_OK); // Result code.

if (not COleObjectFactory::UpdateRegistryAll())
{
/*
Something went wrong; tell the caller we were unable to register
all of the object classes.
*/
result = SELFREG_E_CLASS;
}

return (result);

} // DllRegisterServer


STDAPI
DllUnregisterServer(void)
{
/*
Required for dynamic linking with MFC.
*/
AFX_MANAGE_STATE(::AfxGetStaticModuleState());

HRESULT result(S_OK); // Result code.

if (not COleObjectFactory::UpdateRegistryAll(False))
{
/*
Something went wrong; tell the caller we were unable to unregister all
of the object classes.
*/
result = SELFREG_E_CLASS;
}

return (result);

} // DllUnregisterServer


ULONG
FAR EXPORT
CCMIconHandler::XPersistFile::AddRef(void)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (pThis->ExternalAddRef());

} // CCMIconHandler::XPersistFile::AddRef


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::GetClassID(
CLSID* pClassID)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (E_NOTIMPL);

} // CCMIconHandler::XPersistFile::GetClassID


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::GetCurFile(
LPOLESTR* /* pszFileName */)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (E_NOTIMPL);

} // CCMIconHandler::XPersistFile::GetCurFile


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::IsDirty(void)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (E_NOTIMPL);

} // CCMIconHandler::XPersistFile::IsDirty


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::Load(
LPCOLESTR pszFileName,
DWORD /* dwMode */)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

HRESULT result(E_FAIL); // Result of load operation.

// Copies the path to the <fFileName> field.
[...]

return (result);

} // CCMIconHandler::XPersistFile::Load


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::QueryInterface(
REFIID iid,
LPVOID far* ppvObj)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

HRESULT result(E_NOINTERFACE); // Result of query operation.

if (nil != ppvObj)
{
result = pThis->ExternalQueryInterface(&iid, ppvObj);
}

return (result);

} // CCMIconHandler::XPersistFile::QueryInterface


ULONG
FAR EXPORT
CCMIconHandler::XPersistFile::Release(void)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (pThis->ExternalRelease());

} // CCMIconHandler::XPersistFile::Release


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::Save(
LPCOLESTR /* pszFileName */,
BOOL /* fRemember */)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (E_NOTIMPL);

} // CCMIconHandler::XPersistFile::Save


HRESULT
FAR EXPORT
CCMIconHandler::XPersistFile::SaveCompleted(
LPCOLESTR /* pszFileName */)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

return (E_NOTIMPL);

} // CCMIconHandler::XPersistFile::SaveCompleted


ULONG
FAR EXPORT
CCMIconHandler::XExtractIcon::AddRef(void)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, ExtractIcon);

return (pThis->ExternalAddRef());

} // CCMIconHandler::XExtractIcon::AddRef


HRESULT
FAR EXPORT
CCMIconHandler::XExtractIcon::Extract(
LPCTSTR /* pszFile */,
UINT /* nIconIndex */,
HICON* phiconLarge,
HICON* phiconSmall,
UINT nIconSize)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, ExtractIcon);

// Code for extracting icons....
[...]

return (result);

} // CCMIconHandler::XExtractIcon::Extract


HRESULT
FAR EXPORT
CCMIconHandler::XExtractIcon::GetIconLocation(
UINT uFlags,
LPTSTR smIconFile,
UINT cchMax,
int* piIndex,
UINT* pwFlags)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, ExtractIcon);

// Code for locating icon.
[...]

return (result);

} // CCMIconHandler::XExtractIcon::GetIconLocation


HRESULT
FAR EXPORT
CCMIconHandler::XExtractIcon::QueryInterface(
REFIID iid,
LPVOID far* ppvObj)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, PersistFile);

HRESULT result(E_NOINTERFACE); // Result of query operation.

ASSERT(nil != ppvObj);

if (nil != ppvObj)
{
result = pThis->ExternalQueryInterface(&iid, ppvObj);
}

return (result);

} // CCMIconHandler::XExtractIcon::QueryInterface


ULONG
FAR EXPORT
CCMIconHandler::XExtractIcon::Release(void)
{
/*
Required for all interface methods...
*/
METHOD_PROLOGUE(CCMIconHandler, ExtractIcon);

return (pThis->ExternalRelease());

} // CCMIconHandler::XExtractIcon::Release


CCMIconHandler::CCMIconHandler(void)
{

} // CCMIconHandler::CCMIconHandler


CCMIconHandler::~CCMIconHandler(void)
{

} // CCMIconHandler::~CCMIconHandler


int
CCMIconHandler::ExitInstance(void)
{
return (inherited::ExitInstance());

} // CCMIconHandler::ExitInstance


BOOL
CCMIconHandler::InitInstance(void)
{
inherited::InitInstance();

// Register all OLE server (factories) as running. This enables the
// OLE libraries to create objects from other applications.
COleObjectFactory::RegisterAll();

return (True);

} // CCMIconHandler::InitInstance

It compiles fine and, as I said, it seems that *some* methods get called,
but the Shell never tries to extract an icon. Is there anything obvious that
anyone can see that I'm doing wrong? There doesn't seem to be much to it, so
I'm probably doing something stupid.
mike
2007-08-04 14:24:58 UTC
Permalink
So, the thundering silence means nobody has any idea or nobody cares.
Fair enough.

In either event, how about this: Is it reasonable to suppose that, if
I see my QueryInterface method returning a pointer to the correct
interface (i.e., XPersistFile::QueryInterface() has called pThis-
ExternalQueryInterface() and is returning a pointer to the
XExtractIcon interface, as it should, with a result code of S_OK),
that the shell should then call GetIconLocation() and Extract() as one
would expect?

To sum up: Does anyone have any idea why, after calling
QueryInterface() and getting back what appears to be a valid pointer,
the shell would fail to follow up with a call into my ExtractIcon
interface?

Thanks.
Michael Phillips, Jr.
2007-08-04 15:18:51 UTC
Permalink
Post by mike
In either event, how about this: Is it reasonable to suppose that, if
I see my QueryInterface method returning a pointer to the correct
interface (i.e., XPersistFile::QueryInterface() has called pThis-
ExternalQueryInterface() and is returning a pointer to the
XExtractIcon interface, as it should, with a result code of S_OK),
that the shell should then call GetIconLocation() and Extract() as one
would expect?
If you followed the guidelines listed here:
http://msdn2.microsoft.com/EN-US/library/aa969357.aspx

And your icon handler is registered properly,
then one would expect GetIconLocation and Extract
to be called for the file extension that you registered your icon handler
for.
nomad
2007-08-05 07:10:00 UTC
Permalink
Thanks, Michael. I've followed all the guidelines in that article (and
several others). The salient point here is that I'm pretty sure my
IPersistFile::Load() method would not have been called if the extension
weren't registered properly. I assume, therefore, that it must be, and that
something else is going wrong.

I can sit in the debugger and watch my XPersistFile::QueryInterface() get
called, and see it return a pointer to the correct vtable for my XExtractIcon
subclass (I can derference it and see the pointers to the functions), and yet
none of my XExtractIcon methods ever get called.

So I don't think it's a registration problem; I think there's something else
going on.
Michael Phillips, Jr.
2007-08-05 14:01:56 UTC
Permalink
Post by nomad
IPersistFile::Load() method would not have been called if the extension
weren't registered properly.
Are you able to read the arguments passed to you? Do they make any sense?

Most importantly, do you return S_OK?
Post by nomad
I can sit in the debugger and watch my XPersistFile::QueryInterface() get
called, and see it return a pointer to the correct vtable for my XExtractIcon
subclass
Is there any reason why you cannot implement the IExtractIcon and
IPersistFile methods in the same class?
Post by nomad
Thanks, Michael. I've followed all the guidelines in that article (and
several others). The salient point here is that I'm pretty sure my
IPersistFile::Load() method would not have been called if the extension
weren't registered properly. I assume, therefore, that it must be, and that
something else is going wrong.
I can sit in the debugger and watch my XPersistFile::QueryInterface() get
called, and see it return a pointer to the correct vtable for my XExtractIcon
subclass (I can derference it and see the pointers to the functions), and yet
none of my XExtractIcon methods ever get called.
So I don't think it's a registration problem; I think there's something else
going on.
Jim Barry
2007-08-05 15:18:46 UTC
Permalink
Post by Michael Phillips, Jr.
Is there any reason why you cannot implement the IExtractIcon and
IPersistFile methods in the same class?
That's the way COM is done in MFC - not pleasant!
--
Jim Barry, MVP (Windows SDK)
nomad
2007-08-05 16:10:01 UTC
Permalink
Yes, the MFC declarations for everything are much cleaner, but the
implementation is a bit ... squirrely.

Anyway, another interesting data point: While stepping through the debugger
in my DllGetClassObject() entry point, I noticed that the object that's being
returned by my call to AfxDllGetClassObject() appears to be 4 bytes off. That
is, the fields, read in the debugger, of my CWinApp/CCmdTarget class object
appear to be offset 4 bytes from where they should be: the pointer to a
string that should be in one field appears to be in the next field down. If I
add 4 to the pointer, the fields as displayed in the debugger look correct
again, but if I let it run at that point I get a memory access error.

I haven't played with this enough to know whether this is a debugger
artifact or what, but does this perhaps ring any bells? It would certainly
explain why my interface methods arent' being called: the pointe to the
correct vtable is 4 bytes off from where it should be!
Jim Barry
2007-08-06 12:25:20 UTC
Permalink
Post by nomad
Yes, the MFC declarations for everything are much cleaner
Cleaner than what?
Post by nomad
Anyway, another interesting data point: While stepping through the
debugger in my DllGetClassObject() entry point, I noticed that the
object that's being returned by my call to AfxDllGetClassObject()
appears to be 4 bytes off.
The way that MFC uses a nested class for each interface makes it extremely hard to follow in the debugger. Another reason to steer well clear of it.
--
Jim Barry, MVP (Windows SDK)
nomad
2007-08-06 13:48:02 UTC
Permalink
Post by Jim Barry
Post by nomad
Yes, the MFC declarations for everything are much cleaner
Cleaner than what?
Well, ATL, for one thing.

Anyway, debugger aside, I've got a test app now and I can verify that I can
load the DLL and call into the IExtractIcon interface if I call
::CoInitialize(), CoCreateInstance(), do a QueryInterface(), and then call
the method off the pointer I get back:

::CoInitialize(NULL);

IUnknown* pUnk(NULL);

result = ::CoCreateInstance(kIHCLSID, NULL, CLSCTX_INPROC_SERVER,
IID_IUnknown,
reinterpret_cast<LPVOID*>(&pUnk));

IPersistFile* pIPF(NULL);

if (S_OK == pUnk->QueryInterface(IID_IPersistFile,
reinterpret_cast<LPVOID*>(&pIPF)))
{
ASSERT(S_OK == pIPF->Load(_T("<path to my data file>"), 0));
}

IExtractIcon* pIEI(NULL);
TCHAR path[MAX_PATH];

if (S_OK == pUnk->QueryInterface(IID_IExtractIcon,
reinterpret_cast<LPVOID*>(&pIEI)))
{
int iIndex(0);
UINT wFlags(0);

if (S_OK == pIEI->GetIconLocation(0, path, MAX_PATH, &iIndex, &wFlags))
{
if (S_OK == pIEI->Extract(path, iIndex, &fHBigIcon, &fHSmallIcon,
MAKELONG(32, 16)))
{
(void) fBigIcon.SetIcon(fHBigIcon);
(void) fSmallIcon.SetIcon(fHSmallIcon);
}
}
}

That all works fine. I can also do it using SHGetDesktopFolder() and the
various related calls:

::CoInitialize(NULL);

if (FAILED(SHGetDesktopFolder(&deskFolder)))
return TRUE;

if (FAILED(deskFolder->ParseDisplayName(NULL, NULL,
_T("g:\\development\\projects\\sample files\\files with
embedded previews\\biotite.cmdf"),
NULL, &pidlLocal, NULL)))
{
deskFolder->Release();
return TRUE;
}

pidlRelative = ILClone(ILFindLastID(pidlLocal));
ILRemoveLastID(pidlLocal);

if (FAILED(deskFolder->BindToObject(pidlLocal, NULL, IID_IShellFolder,
(void**)&appObject)))
{
deskFolder->Release();
ILFree(pidlLocal);
return TRUE;
}

ILFree(pidlLocal);

hr = appObject->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlRelative,
IID_IExtractIcon, NULL,
(void**)&extractIcon);

But if I attach to an explorer window with the debugger, what happens is
that my IPersistFile::Load() method gets called, then I load the interface
for IExtractIcon, and, right after that, my IExtractIcon::Release() method
gets called (apparently from CoWaitForMultipleHandles()). So the shell gets
the IExtractIcon interface, and releases it without (apparently) calling
anything.
Michael Phillips, Jr.
2007-08-06 15:14:10 UTC
Permalink
There appears to be a problem with your icon handler's interface
implementation.

You should see the following in your debugger:
1) The shell should load your icon handler and call your IClassFactory and
get an IExtractIcon interface.
2) It then calls QueryInteface on your IExtractIcon interface pointer to get
the IPersistFile interface.
3) If QueryInterface returns an IPersistFile interface, it then calls Load
and looks for S_OK on return.
4) If S_OK, then the shell should call your icon handler's GetIconLocation
and Extract methods.
Post by nomad
Post by Jim Barry
Post by nomad
Yes, the MFC declarations for everything are much cleaner
Cleaner than what?
Well, ATL, for one thing.
Anyway, debugger aside, I've got a test app now and I can verify that I can
load the DLL and call into the IExtractIcon interface if I call
::CoInitialize(), CoCreateInstance(), do a QueryInterface(), and then call
::CoInitialize(NULL);
IUnknown* pUnk(NULL);
result = ::CoCreateInstance(kIHCLSID, NULL, CLSCTX_INPROC_SERVER,
IID_IUnknown,
reinterpret_cast<LPVOID*>(&pUnk));
IPersistFile* pIPF(NULL);
if (S_OK == pUnk->QueryInterface(IID_IPersistFile,
reinterpret_cast<LPVOID*>(&pIPF)))
{
ASSERT(S_OK == pIPF->Load(_T("<path to my data file>"), 0));
}
IExtractIcon* pIEI(NULL);
TCHAR path[MAX_PATH];
if (S_OK == pUnk->QueryInterface(IID_IExtractIcon,
reinterpret_cast<LPVOID*>(&pIEI)))
{
int iIndex(0);
UINT wFlags(0);
if (S_OK == pIEI->GetIconLocation(0, path, MAX_PATH, &iIndex, &wFlags))
{
if (S_OK == pIEI->Extract(path, iIndex, &fHBigIcon, &fHSmallIcon,
MAKELONG(32, 16)))
{
(void) fBigIcon.SetIcon(fHBigIcon);
(void) fSmallIcon.SetIcon(fHSmallIcon);
}
}
}
That all works fine. I can also do it using SHGetDesktopFolder() and the
::CoInitialize(NULL);
if (FAILED(SHGetDesktopFolder(&deskFolder)))
return TRUE;
if (FAILED(deskFolder->ParseDisplayName(NULL, NULL,
_T("g:\\development\\projects\\sample files\\files with
embedded previews\\biotite.cmdf"),
NULL, &pidlLocal, NULL)))
{
deskFolder->Release();
return TRUE;
}
pidlRelative = ILClone(ILFindLastID(pidlLocal));
ILRemoveLastID(pidlLocal);
if (FAILED(deskFolder->BindToObject(pidlLocal, NULL, IID_IShellFolder,
(void**)&appObject)))
{
deskFolder->Release();
ILFree(pidlLocal);
return TRUE;
}
ILFree(pidlLocal);
hr = appObject->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlRelative,
IID_IExtractIcon, NULL,
(void**)&extractIcon);
But if I attach to an explorer window with the debugger, what happens is
that my IPersistFile::Load() method gets called, then I load the interface
for IExtractIcon, and, right after that, my IExtractIcon::Release() method
gets called (apparently from CoWaitForMultipleHandles()). So the shell gets
the IExtractIcon interface, and releases it without (apparently) calling
anything.
nomad
2007-08-06 15:42:00 UTC
Permalink
Here's the sequence of events, dumped from my implementation. In each case I
print out the incoming parameters on entry, and then the outgoing ones (e.g.,
ppv, result) upon completion. To me, it all looks fine until it calls the
QueryInterface immediately after the Load() call.

*** CMyIconHandler::InitInstance() ***
DllGetClassObject()
rclsid = 897d3ada-c314-4e2e-a925-843b578222ce
riid = 00000001-0000-0000-c000-000000000046
ppv = 0x0182af2c
result = 0x00000000
CMyIconHandler::XPersistFile::AddRef()
result = 0x00000002
CMyIconHandler::XPersistFile::Release()
result = 0x00000001
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000003-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000000-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::AddRef()
result = 0x00000003
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000018-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000019-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 4c1e39e1-e3e3-4296-aa86-ec938d896e92
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::Release()
result = 0x00000002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000010b-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000010b-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::Release()
result = 0x00000003
CMyIconHandler::XPersistFile::QueryInterface()
iid = 1c733a30-2a1c-11ce-ade5-00aa0044773d
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::Load()
filename = F:\Documents and Settings\<my file here>
dwMode = 0
result = 0x00000000
CMyIconHandler::XPersistFile::QueryInterface()
iid = 000214fa-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b8
result = 0x00000000
CMyIconHandler::XExtractIcon::Release()
result = 0x00000003
CMyIconHandler::XPersistFile::Release()
result = 0x00000002
CMyIconHandler::XPersistFile::Release()
result = 0x00000001
CMyIconHandler::XPersistFile::Release()
result = 0x00000000
Michael Phillips, Jr.
2007-08-06 16:12:51 UTC
Permalink
Post by nomad
DllGetClassObject()
rclsid = 897d3ada-c314-4e2e-a925-843b578222ce
riid = 00000001-0000-0000-c000-000000000046 <------IClassFactory
ppv = 0x0182af2c
result = 0x00000000
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046 <-------IPersistFile
Your IClassFactory implementation is returning an IPersistFile interface.
It should return an IExtractIcon interface(e.g.,
IExtractIconA{000214eb-0000-0000-c000-000000000046}).
Post by nomad
Here's the sequence of events, dumped from my implementation. In each case I
print out the incoming parameters on entry, and then the outgoing ones (e.g.,
ppv, result) upon completion. To me, it all looks fine until it calls the
QueryInterface immediately after the Load() call.
*** CMyIconHandler::InitInstance() ***
DllGetClassObject()
rclsid = 897d3ada-c314-4e2e-a925-843b578222ce
riid = 00000001-0000-0000-c000-000000000046
ppv = 0x0182af2c
result = 0x00000000
CMyIconHandler::XPersistFile::AddRef()
result = 0x00000002
CMyIconHandler::XPersistFile::Release()
result = 0x00000001
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000003-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000000-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::AddRef()
result = 0x00000003
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000018-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000019-0000-0000-c000-000000000046
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 4c1e39e1-e3e3-4296-aa86-ec938d896e92
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::Release()
result = 0x00000002
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000010b-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000010b-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b4
result = 0x00000000
CMyIconHandler::XPersistFile::Release()
result = 0x00000003
CMyIconHandler::XPersistFile::QueryInterface()
iid = 1c733a30-2a1c-11ce-ade5-00aa0044773d
ppvObj = 0x00000000
result = 0x80004002
CMyIconHandler::XPersistFile::Load()
filename = F:\Documents and Settings\<my file here>
dwMode = 0
result = 0x00000000
CMyIconHandler::XPersistFile::QueryInterface()
iid = 000214fa-0000-0000-c000-000000000046
ppvObj = 0x00e6d1b8
result = 0x00000000
CMyIconHandler::XExtractIcon::Release()
result = 0x00000003
CMyIconHandler::XPersistFile::Release()
result = 0x00000002
CMyIconHandler::XPersistFile::Release()
result = 0x00000001
CMyIconHandler::XPersistFile::Release()
result = 0x00000000
Jim Barry
2007-08-06 16:51:19 UTC
Permalink
Post by Michael Phillips, Jr.
Post by nomad
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046 <-------IPersistFile
Actually, that's IID_IStdIdentity. (IID_IPersistFile starts with 0000010b.)
Post by Michael Phillips, Jr.
Your IClassFactory implementation is returning an IPersistFile
interface.
In fact, "iid" is just the IID being passed to QueryInterface.
--
Jim Barry, MVP (Windows SDK)
Michael Phillips, Jr.
2007-08-06 17:24:58 UTC
Permalink
You are correct.

However, maybe his IClassFactory implementation is returning an IPersistFile
interface instead of an IExtractIcon interface.

I believe that it would be easier to diagnose the problem if the icon
handler was coded in ATL instead of Mfc.
Post by Michael Phillips, Jr.
Post by nomad
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046 <-------IPersistFile
Actually, that's IID_IStdIdentity. (IID_IPersistFile starts with 0000010b.)
Post by Michael Phillips, Jr.
Your IClassFactory implementation is returning an IPersistFile
interface.
In fact, "iid" is just the IID being passed to QueryInterface.
--
Jim Barry, MVP (Windows SDK)
nomad
2007-08-06 17:12:02 UTC
Permalink
Post by Michael Phillips, Jr.
Post by nomad
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046 <-------IPersistFile
Your IClassFactory implementation is returning an IPersistFile interface.
It should return an IExtractIcon interface(e.g.,
IExtractIconA{000214eb-0000-0000-c000-000000000046}).
As I understand it, the class factory can pass back the pointer to any of
the internal interfaces, as long as they all implement IUnknown properly.
DllGetClassObject() just returns the first one it finds, in this case
IPersistFile. IPersistFile::QueryInterface() is being asked for a number of
interfaces which I don't implement (something involving IMarshall or some
such, and a number of others which aren't relevant to the problem of
extracting an icon). Eventually, as the listing shows, it calls
IPersistFile::Load(), as it's supposed to. It then asks for the interface to
IExtractIcon, which I return. I've verified that, in fact, this is a pointer
to the vtable for my XExtractIcon internal subclass. It then immediately
calls IExtractIcon::Release(), not having called anything.

As I noted, however, if I call the required routines in my test app,
everything works.
Michael Phillips, Jr.
2007-08-06 17:41:37 UTC
Permalink
I feel your frustration.

On MSDN here:
http://msdn2.microsoft.com/EN-US/library/aa969289.aspx

Microsoft states that IPersistFile is optional for an icon handler and that
IInitializeWithItem is preferred.

My only other suggestion is write a test case icon handler using ATL.

I seem to find ATL much more pleasant to work with than Mfc.
Post by nomad
Post by Michael Phillips, Jr.
Post by nomad
CMyIconHandler::XPersistFile::QueryInterface()
iid = 0000001b-0000-0000-c000-000000000046 <-------IPersistFile
Your IClassFactory implementation is returning an IPersistFile interface.
It should return an IExtractIcon interface(e.g.,
IExtractIconA{000214eb-0000-0000-c000-000000000046}).
As I understand it, the class factory can pass back the pointer to any of
the internal interfaces, as long as they all implement IUnknown properly.
DllGetClassObject() just returns the first one it finds, in this case
IPersistFile. IPersistFile::QueryInterface() is being asked for a number of
interfaces which I don't implement (something involving IMarshall or some
such, and a number of others which aren't relevant to the problem of
extracting an icon). Eventually, as the listing shows, it calls
IPersistFile::Load(), as it's supposed to. It then asks for the interface to
IExtractIcon, which I return. I've verified that, in fact, this is a pointer
to the vtable for my XExtractIcon internal subclass. It then immediately
calls IExtractIcon::Release(), not having called anything.
As I noted, however, if I call the required routines in my test app,
everything works.
Jim Barry
2007-08-06 18:22:42 UTC
Permalink
Post by Michael Phillips, Jr.
Microsoft states that IPersistFile is optional for an icon handler
and that IInitializeWithItem is preferred.
IInitializeWithFile, IInitializeWithItem, and IInitializeWithStream are all brand spanking new for Vista, however, and certainly won't be called on XP or earlier.
--
Jim Barry, MVP (Windows SDK)
Jim Barry
2007-08-06 18:51:50 UTC
Permalink
Post by nomad
*** CMyIconHandler::InitInstance() ***
DllGetClassObject()
rclsid = 897d3ada-c314-4e2e-a925-843b578222ce
riid = 00000001-0000-0000-c000-000000000046
ppv = 0x0182af2c
result = 0x00000000
CMyIconHandler::XPersistFile::AddRef()
result = 0x00000002
CMyIconHandler::XPersistFile::Release()
result = 0x00000001
CMyIconHandler::XPersistFile::QueryInterface()
iid = 00000003-0000-0000-c000-000000000046
I think I see what the problem is. Your object is being queried for IMarshal, which means that COM marshalling is being done. This only happens when calls are being made between apartments. If your component is not apartment threaded, marshalling will fail for IExtractIcon, because there is no proxy-stub provided for that interface. Make sure your component is apartment threaded, i.e. it is registered with ThreadingModel=Apartment under the InprocServer32 key.
--
Jim Barry, MVP (Windows SDK)
nomad
2007-08-07 07:16:01 UTC
Permalink
Post by Jim Barry
Make sure your component is apartment threaded, i.e. it is registered with
ThreadingModel=Apartment
Post by Jim Barry
under the InprocServer32 key.
Well, halleluljah, that was it! Works great now (well, okay, a few minor
bugs, but those are all my doing).

Many, many thanks to Jim and Michael for your patience and suggestions. I
was almost ready to give up on the whole thing.

Cheers,

-Mike

Loading...