I have code that implements drag & drop for a ShowPicturesOnArrival event.
The code works fine under Windows XP but fails under Vista with an:
Invalid FORMATETC structure
error. For completeness, the relevant Registry keys I've added are below.
Items preceeded by a "v" are values for the above key, not subkeys.
HKEY_CLASSES_ROOT
MyApp
shell
open
DropTarget
v CLSID [REG_SZ] = {My-GUID}
HKEY_LOCAL_MACHINE
SOFTWARE
Classes
CLSID
{My-GUID}
v (default) [REG_SZ] MyApp
InprocServer32
v (default) [REG_SZ] = C:\path\to\My.dll
v ThreadingModel [REG_SZ] = Apartment
Microsoft
Windows
CurrentVersion
Explorer
AutoplayHandlers
EventHandlers
ShowPicturesOnArrival
v MyApp [REG_SZ]
Handlers
MyApp
v Action [REG_SZ] = "Copy pictures ..."
v DefaultIcon [REG_SZ] = C:\path\to\MyApp.exe,0
v InvokeProgID [REG_SZ] = MyApp
v InvokeVerb [REG_SZ] = open
v Provider [REG_SZ] = MyApp's name
My code follows. (Yes, I know it's a lot to look through, but any help much
appreciated. The failure occurs where the comment containing "fails" below
is.) First, I have a utility reference-counting class:
class LC_DLLRefCount {
public:
static ULONG get() {
return m_refCount;
}
protected:
LC_DLLRefCount() { ++m_refCount; }
~LC_DLLRefCount() { --m_refCount; }
private:
static ULONG volatile m_refCount;
};
ULONG volatile LC_DLLRefCount::m_refCount;
Next, my IClassFactory implementation:
class LC_ClassFactory : public IClassFactory, public LC_DLLRefCount {
public:
LC_ClassFactory() : m_refCount( 0 ) { }
ULONG STDMETHODCALLTYPE AddRef() {
return ++m_refCount;
}
ULONG STDMETHODCALLTYPE Release() {
ULONG const remaining = --m_refCount;
if ( !remaining )
delete this;
return remaining;
}
HRESULT STDMETHODCALLTYPE CreateInstance( IUnknown*, REFIID,
LPVOID* );
HRESULT STDMETHODCALLTYPE LockServer( BOOL );
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID, LPVOID* );
private:
ULONG volatile m_refCount;
};
HRESULT STDMETHODCALLTYPE LC_ClassFactory::CreateInstance
( IUnknown *pUnkOuter, REFIID iid, LPVOID *ppv )
{
if ( ::IsBadWritePtr( ppv, sizeof( LPVOID ) ) )
return E_POINTER;
if ( pUnkOuter )
return CLASS_E_NOAGGREGATION;
*ppv = NULL;
LC_AutoplayHandler *const handler = new LC_AutoplayHandler();
if ( !handler )
return E_OUTOFMEMORY;
HRESULT result = handler->QueryInterface( iid, ppv );
if ( FAILED( result ) )
delete handler;
return result;
}
HRESULT STDMETHODCALLTYPE LC_ClassFactory::LockServer( BOOL lock ) {
if ( lock )
++m_refCount;
else
--m_refCount;
return S_OK;
}
HRESULT STDMETHODCALLTYPE LC_ClassFactory::QueryInterface
( REFIID iid, LPVOID *ppv )
{
if ( ::IsBadWritePtr( ppv, sizeof( LPVOID ) ) )
return E_POINTER;
if ( ::IsEqualIID( iid, IID_IUnknown ) )
*ppv = dynamic_cast<IUnknown*>( this );
else if ( ::IsEqualIID( iid, IID_IClassFactory ) )
*ppv = dynamic_cast<IClassFactory*>( this );
else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
Next, my IDropTarget implementation:
class LC_AutoplayHandler : public IDropTarget, public LC_DLLRefCount {
public:
LC_AutoplayHandler() : m_refCount( 0 ) { }
ULONG STDMETHODCALLTYPE AddRef() {
return ++m_refCount;
}
ULONG STDMETHODCALLTYPE Release() {
ULONG const remaining = --m_refCount;
if ( !remaining )
delete this;
return remaining;
}
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID, LPVOID* );
HRESULT STDMETHODCALLTYPE Drop( LPDATAOBJECT, DWORD, POINTL,
PDWORD );
HRESULT STDMETHODCALLTYPE DragEnter( LPDATAOBJECT, DWORD, POINTL,
PDWORD );
HRESULT STDMETHODCALLTYPE DragLeave();
HRESULT STDMETHODCALLTYPE DragOver( DWORD, POINTL, PDWORD );
private:
ULONG volatile m_refCount;
};
HRESULT STDMETHODCALLTYPE LC_AutoplayHandler::DragEnter
( LPDATAOBJECT, DWORD, POINTL, PDWORD pEffect )
{
*pEffect = DROPEFFECT_COPY;
return S_OK;
}
HRESULT STDMETHODCALLTYPE LC_AutoplayHandler::DragLeave() {
return S_OK;
}
HRESULT STDMETHODCALLTYPE LC_AutoplayHandler::DragOver
( DWORD, POINTL, PDWORD pEffect )
{
*pEffect = DROPEFFECT_COPY;
return S_OK;
}
HRESULT STDMETHODCALLTYPE LC_AutoplayHandler::Drop
( LPDATAOBJECT pDO, DWORD, POINTL, PDWORD )
{
UINT autoplayShellIDLists = ::RegisterClipboardFormat(
TEXT("Autoplay Enumerated IDList Array")
);
if ( !autoplayShellIDLists )
return ::GetLastError();
FORMATETC formatEtc = {
autoplayShellIDLists, NULL, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL
};
STGMEDIUM medium = { 0 };
HRESULT result = pDO->GetData( &formatEtc, &medium );
if ( FAILED( result ) )
return result;
// The call to GetData() above fails with:
//
// Invalid FORMATETC structure
//
// ... elided ...
::ReleaseStgMedium( &medium );
return result;
}
HRESULT STDMETHODCALLTYPE LC_AutoplayHandler::QueryInterface
( REFIID iid, LPVOID *ppv )
{
if ( ::IsBadWritePtr( ppv, sizeof( LPVOID ) ) )
return E_POINTER;
if ( ::IsEqualIID( iid, IID_IUnknown ) )
*ppv = dynamic_cast<IUnknown*>( this );
else if ( ::IsEqualIID( iid, IID_IDropTarget ) )
*ppv = dynamic_cast<IDropTarget*>( this );
else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
Finally, my Dll* functions implementation:
#define LC_DLL_EXPORT(T) extern "C" __declspec(dllexport) T __stdcall
LC_DLL_EXPORT(HRESULT) DllCanUnloadNow() {
bool const canUnload = LC_DLLRefCount::get() == 0;
return canUnload ? S_OK : S_FALSE;
}
LC_DLL_EXPORT(HRESULT) DllGetClassObject( REFCLSID clsid, REFIID iid,
LPVOID *ppv ) {
if ( !::IsEqualCLSID( clsid, LC_AP_GUID ) )
return CLASS_E_CLASSNOTAVAILABLE;
if ( ::IsBadWritePtr( ppv, sizeof( LPVOID ) ) )
return E_POINTER;
*ppv = NULL;
LC_ClassFactory *const factory = new LC_ClassFactory();
if ( !factory )
return E_OUTOFMEMORY;
factory->AddRef();
HRESULT result = factory->QueryInterface( iid, ppv );
factory->Release();
return result;
}
Are there any mistakes in the above code but it just happens to work in XP
but not Vista? How can this be made to work in Vista? Thanks.
- Paul