Windows Shell Extensions

enrichard

Grünschnabel
Hallo zusammen,

ich versuche gerade krampfhaft eine ShellExtension für Windows XP zu coden und komme einfach nicht weiter. Ich habe mich reich in der MSDN belesen und am Beispiel des Platform SDK ("ShellExt") orientiert. Das Beispiel mit Microsofts Command-Line Compiler zu kompilieren war keine große Hürde. Die Probleme gingen jedoch los, als ich versuchte, aus dem Beispiel-Code ein VC-Express-Projekt zu stricken.

Schließlich habe ich das als "Makefile"-Projekt hinbekommen, jedoch leider immernoch nicht als normales DLL-Projekt. Lege ich ein solches an und füge entsprechende Quell-Dateien aus dem Beispiel hinzu, kann ich zwar alles schön kompilieren, meine DLL läuft aber nicht.

Per Debug-View sehe ich, dass meine DLL zumindest geladen wird (OutputDebugString in DLLMain). In dem Moment, wo jedoch eigentlich DllGetClassObject aufgerufen werden müsste, um eine Instanz von CShellExtClassFactory zurückzugeben, passiert einfach nichts. Sprich, es bleibt bei dem call von DLLMain.

Nun dachte ich, ok ok, vielleicht hat es ja mit irgendwelchen Implementierungsdetails, Makros etc. zu tun, welche mit cl+makefile funktionieren, nicht jedoch mit VC Express. Deshalb habe ich versucht, schrittweise und von Anfang an in VC Express, eigene ShellExtension-Klassen zu erzeugen, ganz nach dem Vorbild des Beispiels. Das Ergebnis war leider wieder das gleiche.

Mein letzter Hoffnungsschimmer ist nun, dass u.U. irgendeine Compiler-Direktive in der cl-Variante steckt, die nicht in der VC-Variante enthalten ist. Das habe ich zwar schon Direktive für Direktive überprüpft, aber ich weiß nicht, ob für die VC-Variante ein Zusatz fehlt?! Eine andere Möglichkeit könnten fehlende Makros an den DLL-Methoden sein. Ich kenne mich hier leider zu wenig aus, weshalb ich hier nachfrage.

Kann mir irgendjemand bei meinem Problem helfen? Langsam macht's kein Spaß mehr :mad:.


Vielen Dank im voraus,
Henrik

Hier die relevanten Code-Auszüge:

ShellExt.h:
Code:
//
// The class ID of this Shell extension class.
// Copyright 1993 - 2000 Microsoft Corporation.  All Rights Reserved.
//
// class id:  87b9bd00-c65c-11cd-a259-00dd010e8c28
//
//
// NOTE  If you use this shell extension as a starting point,
//          you MUST change the GUID below.  Simply run UUIDGEN.EXE
//          to generate a new GUID.
//

#define ODS(sz) OutputDebugString(sz)

#ifndef _SHELLEXT_H
#define _SHELLEXT_H

DEFINE_GUID(CLSID_ShellExtension, 0x87b9bd00L, 0xc65c, 0x11cd, 0xa2, 0x59, 0x00, 0xdd, 0x01, 0x0e, 0x8c, 0x28 );

// this class factory object creates context menu handlers for Windows 95 shell
class CShellExtClassFactory : public IClassFactory
{
protected:
    ULONG   m_cRef;

public:
    CShellExtClassFactory();
    ~CShellExtClassFactory();

    //IUnknown members
    STDMETHODIMP            QueryInterface(REFIID, LPVOID FAR *);
    STDMETHODIMP_(ULONG)    AddRef();
    STDMETHODIMP_(ULONG)    Release();

    //IClassFactory members
    STDMETHODIMP        CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
    STDMETHODIMP        LockServer(BOOL);

};
typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY;

// this is the actual OLE Shell context menu handler
class CShellExt : public IContextMenu,
                         IShellExtInit
                         //IExtractIcon,
                         //IPersistFile,
                         //IShellPropSheetExt,
                         //ICopyHook
{
public:
    char         m_szPropSheetFileUserClickedOn[MAX_PATH];  //This will be the same as
                                                            //m_szFileUserClickedOn but I include
                                                            //here for demonstration.  That is,
                                                            //m_szFileUserClickedOn gets filled in
                                                            //as a result of this sample supporting
                                                            //the IExtractIcon and IPersistFile
                                                            //interface.  If this sample *only* showed
                                                            //a Property Sheet extesion, you would
                                                            //need to use the method I do here to find
                                                            //the filename the user clicked on.


protected:
    ULONG        m_cRef;
    LPDATAOBJECT m_pDataObj;
    char         m_szFileUserClickedOn[MAX_PATH];

    STDMETHODIMP DoGAKMenu1(HWND hParent,
                            LPCSTR pszWorkingDir,
                            LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

    STDMETHODIMP DoGAKMenu2(HWND hParent,
                            LPCSTR pszWorkingDir,
                            LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

    STDMETHODIMP DoGAKMenu3(HWND hParent,
                            LPCSTR pszWorkingDir,
                            LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

    STDMETHODIMP DoGAKMenu4(HWND hParent,
                            LPCSTR pszWorkingDir,
                            LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);
public:
    CShellExt();
    ~CShellExt();

    //IUnknown members
    STDMETHODIMP            QueryInterface(REFIID, LPVOID FAR *);
    STDMETHODIMP_(ULONG)    AddRef();
    STDMETHODIMP_(ULONG)    Release();

    //IShell members
    STDMETHODIMP            QueryContextMenu(HMENU hMenu,
                                             UINT indexMenu,
                                             UINT idCmdFirst,
                                             UINT idCmdLast,
                                             UINT uFlags);

    STDMETHODIMP            InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);

    STDMETHODIMP            GetCommandString(UINT_PTR idCmd,
                                             UINT uFlags,
                                             UINT FAR *reserved,
                                             LPSTR pszName,
                                             UINT cchMax);

    //IShellExtInit methods
    STDMETHODIMP            Initialize(LPCITEMIDLIST pIDFolder,
                                       LPDATAOBJECT pDataObj,
                                       HKEY hKeyID);

    ////IExtractIcon methods
    //STDMETHODIMP GetIconLocation(UINT   uFlags,
    //                             LPSTR  szIconFile,
    //                             UINT   cchMax,
    //                             int   *piIndex,
    //                             UINT  *pwFlags);

    //STDMETHODIMP Extract(LPCSTR pszFile,
    //                     UINT   nIconIndex,
    //                     HICON  *phiconLarge,
    //                     HICON  *phiconSmall,
    //                     UINT   nIconSize);

    ////IPersistFile methods
    //STDMETHODIMP GetClassID(LPCLSID lpClassID);

    //STDMETHODIMP IsDirty();

    //STDMETHODIMP Load(LPCOLESTR lpszFileName, DWORD grfMode);

    //STDMETHODIMP Save(LPCOLESTR lpszFileName, BOOL fRemember);

    //STDMETHODIMP SaveCompleted(LPCOLESTR lpszFileName);

    //STDMETHODIMP GetCurFile(LPOLESTR FAR* lplpszFileName);

    ////IShellPropSheetExt methods
    //STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);

    //STDMETHODIMP ReplacePage(UINT uPageID,
    //                         LPFNADDPROPSHEETPAGE lpfnReplaceWith,
    //                         LPARAM lParam);

    ////ICopyHook method
    //STDMETHODIMP_(UINT) CopyCallback(HWND hwnd,
    //                                 UINT wFunc,
    //                                 UINT wFlags,
    //                                 LPCSTR pszSrcFile,
    //                                 DWORD dwSrcAttribs,
    //                                 LPCSTR pszDestFile,
    //                                 DWORD dwDestAttribs);

};
typedef CShellExt *LPCSHELLEXT;

#endif // _SHELLEXT_H

ShellExt.cpp:
Code:
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 2000 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   shellext.cpp
//
// Purpose:  Implements the class factory code as well as CShellExt::QI,
//           CShellExt::AddRef and CShellExt::Release code.

// #include "priv.h"
#include "stdafx.h"

//
// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
//
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#include "shellext.h"
#pragma data_seg()

//
// Global variables
//
UINT      g_cRefThisDll = 0;    // Reference count of this DLL.
HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself.

//#pragma unmanaged
//
//
//#pragma unmanaged
//BOOL WINAPI 
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        ODS("In DLLMain, DLL_PROCESS_ATTACH\r\n");

        // Extension DLL one-time initialization

        g_hmodThisDll = hInstance;
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        ODS("In DLLMain, DLL_PROCESS_DETACH\r\n");
    }

    return 1;   // ok
}

//---------------------------------------------------------------------------
// DllCanUnloadNow
//---------------------------------------------------------------------------

STDAPI DllCanUnloadNow(void)
{
    ODS("In DLLCanUnloadNow\r\n");

    return (g_cRefThisDll == 0 ? S_OK : S_FALSE);
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{
    ODS("In DllGetClassObject\r\n");

    *ppvOut = NULL;

    if (IsEqualIID(rclsid, CLSID_ShellExtension))
    {
        CShellExtClassFactory *pcf = new CShellExtClassFactory;

        return pcf->QueryInterface(riid, ppvOut);
    }

    return CLASS_E_CLASSNOTAVAILABLE;
}

CShellExtClassFactory::CShellExtClassFactory()
{
    ODS("CShellExtClassFactory::CShellExtClassFactory()\r\n");

    m_cRef = 0L;

    g_cRefThisDll++;
}

CShellExtClassFactory::~CShellExtClassFactory()
{
    g_cRefThisDll--;
}

STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid,
                                                   LPVOID FAR *ppv)
{
    ODS("CShellExtClassFactory::QueryInterface()\r\n");

    *ppv = NULL;

    // Any interface on this object is the object pointer

    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
    {
        *ppv = (LPCLASSFACTORY)this;

        AddRef();

        return NOERROR;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
{
    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}

STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
                                                      REFIID riid,
                                                      LPVOID *ppvObj)
{
    ODS("CShellExtClassFactory::CreateInstance()\r\n");

    *ppvObj = NULL;

    // Shell extensions typically don't support aggregation (inheritance)

    if (pUnkOuter)
        return CLASS_E_NOAGGREGATION;

    // Create the main shell extension object.  The shell will then call
    // QueryInterface with IID_IShellExtInit--this is how shell extensions are
    // initialized.

    LPCSHELLEXT pShellExt = new CShellExt();  //Create the CShellExt object

    if (NULL == pShellExt)
        return E_OUTOFMEMORY;

    return pShellExt->QueryInterface(riid, ppvObj);
}


STDMETHODIMP CShellExtClassFactory::LockServer(BOOL fLock)
{
    return NOERROR;
}

// *********************** CShellExt *************************
CShellExt::CShellExt()
{
    ODS("CShellExt::CShellExt()\r\n");

    m_cRef = 0L;
    m_pDataObj = NULL;

    g_cRefThisDll++;
}

CShellExt::~CShellExt()
{
    if (m_pDataObj)
        m_pDataObj->Release();

    g_cRefThisDll--;
}

STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    *ppv = NULL;

    if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
    {
        ODS("CShellExt::QueryInterface()==>IID_IShellExtInit\r\n");

        *ppv = (LPSHELLEXTINIT)this;
    }
    else if (IsEqualIID(riid, IID_IContextMenu))
    {
        ODS("CShellExt::QueryInterface()==>IID_IContextMenu\r\n");

        *ppv = (LPCONTEXTMENU)this;
    }
    else if (IsEqualIID(riid, IID_IExtractIcon))
    {
        ODS("CShellExt::QueryInterface()==>IID_IExtractIcon\r\n");

        *ppv = (LPEXTRACTICON)this;
    }
    else if (IsEqualIID(riid, IID_IPersistFile))
    {
        ODS("CShellExt::QueryInterface()==>IPersistFile\r\n");

        *ppv = (LPPERSISTFILE)this;
    }
    else if (IsEqualIID(riid, IID_IShellPropSheetExt))
    {
        ODS("CShellExt::QueryInterface()==>IShellPropSheetExt\r\n");

        *ppv = (LPSHELLPROPSHEETEXT)this;
    }
    else if (IsEqualIID(riid, IID_IShellCopyHook))
    {
        ODS("CShellExt::QueryInterface()==>ICopyHook\r\n");

        *ppv = (LPCOPYHOOK)this;
    }

    if (*ppv)
    {
        AddRef();

        return NOERROR;
    }

    ODS("CShellExt::QueryInterface()==>Unknown Interface!\r\n");

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CShellExt::AddRef()
{
    ODS("CShellExt::AddRef()\r\n");

    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CShellExt::Release()
{
    ODS("CShellExt::Release()\r\n");

    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}
 
Zurück