/*++ Copyright (c) 1995 Microsoft Corporation Module Name: nwshext.cxx Abstract: This module implements the basics of shell extension classes. It includes AddRef(), Release(), QueryInterface() of the following classes. CNWObjContextMenuClassFactory, CNWObjContextMenu CNWFldContextMenuClassFactory, CNWFldContextMenu CNWHoodContextMenuClassFactory, CNWHoodContextMenu Author: Yi-Hsin Sung (yihsins) 25-Oct-1995 --*/ #include #include #include #include #include #include #include #define DONT_WANT_SHELLDEBUG #include #include #include "nwshcmn.h" #include "nwshext.h" // // Initialize GUIDs (should be done only and at-least once per DLL/EXE) // #pragma data_seg(".text") #define INITGUID #include #include #include "nwclsid.h" #pragma data_seg() // // Global variables // LONG g_cRefThisDll = 0; // Reference count of this DLL. WCHAR g_szProviderName[256]; // Store the provider name HINSTANCE g_hShellLibrary = NULL; SHELLGETNETRESOURCE g_pFuncSHGetNetResource = NULL; SHELLDRAGQUERYFILE g_pFuncSHDragQueryFile = NULL; SHELLCHANGENOTIFY g_pFuncSHChangeNotify = NULL; SHELLEXECUTEEX g_pFuncSHExecuteEx = NULL; #if DBG WCHAR szDebugStr[256]; // For Debug Output #endif BOOL LoadShellDllEntries( VOID ); extern "C" { //--------------------------------------------------------------------------- // NwCleanupShellExtension //--------------------------------------------------------------------------- VOID NwCleanupShellExtensions( VOID ) { if ( g_hShellLibrary ) { FreeLibrary( g_hShellLibrary ); g_hShellLibrary = NULL; } } } //--------------------------------------------------------------------------- // DllCanUnloadNow //--------------------------------------------------------------------------- STDAPI DllCanUnloadNow(void) { #if DBG wsprintf( szDebugStr,L"In DLLCanUnloadNow: g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif return ResultFromScode((g_cRefThisDll == 0) ? S_OK : S_FALSE); } //--------------------------------------------------------------------------- // DllGetClassObject //--------------------------------------------------------------------------- STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) { *ppvOut = NULL; if ( !LoadShellDllEntries() ) return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE); if (IsEqualIID(rclsid, CLSID_NetWareObjectExt)) { CNWObjContextMenuClassFactory *pcf = new CNWObjContextMenuClassFactory; if ( pcf == NULL ) return ResultFromScode(E_OUTOFMEMORY); HRESULT hr = pcf->QueryInterface(riid, ppvOut); if ( FAILED(hr) ) delete pcf; return hr; } else if (IsEqualIID(rclsid, CLSID_NetWareFolderMenuExt)) { CNWFldContextMenuClassFactory *pcf = new CNWFldContextMenuClassFactory; if ( pcf == NULL ) return ResultFromScode(E_OUTOFMEMORY); HRESULT hr = pcf->QueryInterface(riid, ppvOut); if ( FAILED(hr) ) delete pcf; return hr; } else if (IsEqualIID(rclsid, CLSID_NetworkNeighborhoodMenuExt)) { CNWHoodContextMenuClassFactory *pcf= new CNWHoodContextMenuClassFactory; if ( pcf == NULL ) return ResultFromScode(E_OUTOFMEMORY); HRESULT hr = pcf->QueryInterface(riid, ppvOut); if ( FAILED(hr) ) delete pcf; return hr; } return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE); } BOOL LoadShellDllEntries( VOID ) { static BOOL s_fLoaded = FALSE; if ( !s_fLoaded ) { DWORD err; HKEY hkey; g_hShellLibrary = LoadLibrary( L"shell32.dll"); if ( g_hShellLibrary != NULL ) { s_fLoaded = TRUE; g_pFuncSHGetNetResource = (SHELLGETNETRESOURCE) GetProcAddress( g_hShellLibrary, (LPCSTR)(MAKELONG(SHGetNetResourceORD, 0)) ); g_pFuncSHDragQueryFile = (SHELLDRAGQUERYFILE) GetProcAddress( g_hShellLibrary, (LPCSTR) "DragQueryFileW"); g_pFuncSHChangeNotify = (SHELLCHANGENOTIFY) GetProcAddress( g_hShellLibrary, (LPCSTR) "SHChangeNotify"); g_pFuncSHExecuteEx = (SHELLEXECUTEEX) GetProcAddress( g_hShellLibrary, (LPCSTR) "ShellExecuteExW"); } // Set the default provider name in case we fail to read // it from the registry. wcscpy( g_szProviderName, L"NetWare or Compatible Network"); // // Read the Network Provider Name. // // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services // \NWCWorkstation\networkprovider // err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, NW_WORKSTATION_PROVIDER_PATH, REG_OPTION_NON_VOLATILE, // options KEY_READ, // desired access &hkey ); if ( err == NO_ERROR ) { LPWSTR pszProviderName = NULL; // // ignore the return code. if fail, pszProviderName is NULL // err = NwReadRegValue( hkey, NW_PROVIDER_VALUENAME, &pszProviderName // free with LocalFree ); if ( err == NO_ERROR && pszProviderName != NULL ) { wcscpy( g_szProviderName, pszProviderName ); LocalFree( pszProviderName ); } RegCloseKey( hkey ); } } return s_fLoaded; } //--------------------------------------------------------------------------- // CNWObjContextMenuClassFactory //--------------------------------------------------------------------------- CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory() { _cRef = 0L; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory() { InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWObjContextMenuClassFactory::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *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 ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::Release() { if (--_cRef) return _cRef; delete this; return 0L; } STDMETHODIMP CNWObjContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { *ppvObj = NULL; // Shell extensions typically don't support aggregation (inheritance) if (pUnkOuter) return ResultFromScode(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. LPCNWOBJCONTEXTMENU pShellExt = new CNWObjContextMenu(); // Create the CNWObjContextMenu object if (NULL == pShellExt) return ResultFromScode(E_OUTOFMEMORY); // // We set the reference count of CNWObjContextMenu to one at initialization. // Hence, we can call Release() after QueryInterface. // So, if QueryInterface failed, Release will free the object. // HRESULT hr = pShellExt->QueryInterface(riid, ppvObj); pShellExt->Release(); return hr; } STDMETHODIMP CNWObjContextMenuClassFactory::LockServer(BOOL fLock) { return NOERROR; } //--------------------------------------------------------------------------- // CNWObjContextMenu //--------------------------------------------------------------------------- CNWObjContextMenu::CNWObjContextMenu() { _cRef = 1L; _pDataObj = NULL; _fGotClusterInfo = FALSE; _dwTotal = 0; _dwFree = 0; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWObjContextMenu::CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWObjContextMenu::~CNWObjContextMenu() { if (_pDataObj) _pDataObj->Release(); InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWObjContextMenu::~CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWObjContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *ppv = NULL; if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) { *ppv = (LPSHELLEXTINIT)this; } else if (IsEqualIID(riid, IID_IContextMenu)) { *ppv = (LPCONTEXTMENU)this; } else if (IsEqualIID(riid, IID_IShellPropSheetExt)) { *ppv = (LPSHELLPROPSHEETEXT)this; } if (*ppv) { AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWObjContextMenu::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWObjContextMenu::Release() { if (--_cRef) return _cRef; delete this; return 0L; } // // FUNCTION: CNWObjContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) // // PURPOSE: Called by the shell when initializing a context menu or property // sheet extension. // // PARAMETERS: // pIDFolder - Specifies the parent folder // pDataObj - Spefifies the set of items selected in that folder. // hRegKey - Specifies the type of the focused item in the selection. // // RETURN VALUE: // // NOERROR in all cases. // // COMMENTS: Note that at the time this function is called, we don't know // (or care) what type of shell extension is being initialized. // It could be a context menu or a property sheet. // STDMETHODIMP CNWObjContextMenu::Initialize( LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey) { // We do not need the registry handle so ignore it. // Initialize can be called more than once if (_pDataObj) _pDataObj->Release(); // Duplicate the object pointer if (pDataObj) { _pDataObj = pDataObj; pDataObj->AddRef(); } return NOERROR; } //--------------------------------------------------------------------------- // CNWFldContextMenuClassFactory //--------------------------------------------------------------------------- CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory() { _cRef = 0L; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory() { InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWFldContextMenuClassFactory::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *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 ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::Release() { if (--_cRef) return _cRef; delete this; return 0L; } STDMETHODIMP CNWFldContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { *ppvObj = NULL; // Shell extensions typically don't support aggregation (inheritance) if (pUnkOuter) return ResultFromScode(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. LPCNWFLDCONTEXTMENU pShellExt = new CNWFldContextMenu(); // Create the CNWFldContextMenu object if (NULL == pShellExt) return ResultFromScode(E_OUTOFMEMORY); // // We set the reference count of CNWFldContextMenu to one at initialization. // Hence, we can call Release() after QueryInterface. // So, if QueryInterface failed, Release will free the object. // HRESULT hr = pShellExt->QueryInterface(riid, ppvObj); pShellExt->Release(); return hr; } STDMETHODIMP CNWFldContextMenuClassFactory::LockServer(BOOL fLock) { return NOERROR; } //--------------------------------------------------------------------------- // CNWFldContextMenu //--------------------------------------------------------------------------- CNWFldContextMenu::CNWFldContextMenu() { _cRef = 1L; _pDataObj = NULL; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWFldContextMenu::CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWFldContextMenu::~CNWFldContextMenu() { if (_pDataObj) _pDataObj->Release(); InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWFldContextMenu::~CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWFldContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *ppv = NULL; if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) { *ppv = (LPSHELLEXTINIT)this; } else if (IsEqualIID(riid, IID_IContextMenu)) { *ppv = (LPCONTEXTMENU)this; } if (*ppv) { AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWFldContextMenu::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWFldContextMenu::Release() { if (--_cRef) return _cRef; delete this; return 0L; } // // FUNCTION: CNWFldContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) // // PURPOSE: Called by the shell when initializing a context menu or property // sheet extension. // // PARAMETERS: // pIDFolder - Specifies the parent folder // pDataObj - Spefifies the set of items selected in that folder. // hRegKey - Specifies the type of the focused item in the selection. // // RETURN VALUE: // // NOERROR in all cases. // // COMMENTS: Note that at the time this function is called, we don't know // (or care) what type of shell extension is being initialized. // It could be a context menu or a property sheet. // STDMETHODIMP CNWFldContextMenu::Initialize( LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey) { // We do not need the registry handle so ignore it. // Initialize can be called more than once if (_pDataObj) _pDataObj->Release(); // Duplicate the object pointer if (pDataObj) { _pDataObj = pDataObj; pDataObj->AddRef(); } return NOERROR; } //--------------------------------------------------------------------------- // CNWHoodContextMenuClassFactory //--------------------------------------------------------------------------- CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory() { _cRef = 0L; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory() { InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWHoodContextMenuClassFactory::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *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 ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::Release() { if (--_cRef) return _cRef; delete this; return 0L; } STDMETHODIMP CNWHoodContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { *ppvObj = NULL; // Shell extensions typically don't support aggregation (inheritance) if (pUnkOuter) return ResultFromScode(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. LPCNWHOODCONTEXTMENU pShellExt = new CNWHoodContextMenu(); // Create the CNWHoodContextMenu object if (NULL == pShellExt) return ResultFromScode(E_OUTOFMEMORY); // // We set the reference count of CNWHoodContextMenu to one at initialization. // Hence, we can call Release() after QueryInterface. // So, if QueryInterface failed, Release will free the object. // HRESULT hr = pShellExt->QueryInterface(riid, ppvObj); pShellExt->Release(); return hr; } STDMETHODIMP CNWHoodContextMenuClassFactory::LockServer(BOOL fLock) { return NOERROR; } //--------------------------------------------------------------------------- // CNWHoodContextMenu //--------------------------------------------------------------------------- CNWHoodContextMenu::CNWHoodContextMenu() { _cRef = 1L; _pDataObj = NULL; InterlockedIncrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWHoodContextMenu::CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } CNWHoodContextMenu::~CNWHoodContextMenu() { if (_pDataObj) _pDataObj->Release(); InterlockedDecrement( &g_cRefThisDll ); #if DBG wsprintf( szDebugStr,L"CNWHoodContextMenu::~CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll); ODS( szDebugStr ); #endif } STDMETHODIMP CNWHoodContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv) { *ppv = NULL; if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) { *ppv = (LPSHELLEXTINIT)this; } else if (IsEqualIID(riid, IID_IContextMenu)) { *ppv = (LPCONTEXTMENU)this; } if (*ppv) { AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CNWHoodContextMenu::AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) CNWHoodContextMenu::Release() { if (--_cRef) return _cRef; delete this; return 0L; } // // FUNCTION: CNWHoodContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) // // PURPOSE: Called by the shell when initializing a context menu or property // sheet extension. // // PARAMETERS: // pIDFolder - Specifies the parent folder // pDataObj - Spefifies the set of items selected in that folder. // hRegKey - Specifies the type of the focused item in the selection. // // RETURN VALUE: // // NOERROR in all cases. // // COMMENTS: Note that at the time this function is called, we don't know // (or care) what type of shell extension is being initialized. // It could be a context menu or a property sheet. // STDMETHODIMP CNWHoodContextMenu::Initialize( LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey) { // We do not need the registry handle so ignore it. // Initialize can be called more than once if (_pDataObj) _pDataObj->Release(); // Duplicate the object pointer if (pDataObj) { _pDataObj = pDataObj; pDataObj->AddRef(); } return NOERROR; }