#include "pch.h" #include #pragma hdrstop TCHAR const c_szClass[] = TEXT("Class"); TCHAR const c_szClasses[] = TEXT("Classes"); TCHAR const c_szAllContainers[] = TEXT("AllContainers"); TCHAR const c_szAllObjects[] = TEXT("AllObjects"); TCHAR const c_szDsPropertyUI[] = TEXT("PropertiesHandler"); // // This function is supposed to ultimately shutdown COM // regardless of how many times CoInitialize(NULL) has // been called. // void ShutdownCOM() { for(;;) { // // Call CoUninitialze() twice // CoUninitialize(); CoUninitialize(); // // Call CoInitialize(NULL) to see whether this will be the first // COM initialization. S_OK means COM is initialized successfully, // S_FALSE means it has been initialized already. // HRESULT hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { // S_OK, S_FALSE case if (S_OK == hr) { CoUninitialize(); break; } else { // The internal COM ref count // still hasn't reached zero continue; } } else { // RPC_E_CHANGED_MODE case if (RPC_E_CHANGED_MODE == hr) { continue; } else { // Some other failure // E_OUTOFMEMORY for example break; } } } } /*----------------------------------------------------------------------------- / Globals etc used for icon extraction /----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- / GetKeysForClass / --------------- / Given a class and the flags assocaited with that extract the keys that / represent it. / / In: / pObjectClass = class name to fetch keys for / fIsConatiner = object is a container / cKeys = number of keys to fetch / aKeys = array to be filled with keys / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT GetKeysForClass(LPWSTR pObjectClass, BOOL fIsContainer, INT cKeys, HKEY* aKeys) { HRESULT hres; HKEY hkClasses = NULL; CLSID clsidBase; LPTSTR pMappedClass = NULL; TraceEnter(TRACE_UI, "GetKeysForClass"); if (cKeys < UIKEY_MAX) ExitGracefully(hres, E_INVALIDARG, "cKeys < UIKEY_MAX"); ZeroMemory(aKeys, SIZEOF(HKEY)*cKeys); hres = GetKeyForCLSID(CLSID_MicrosoftDS, c_szClasses, &hkClasses); FailGracefully(hres, "Failed to get Classes key from registry"); // // Attempt to look up the class name in the registery under the namespaces "classes" // sub key. A class can also be mapped onto another one, if this happens then we have // a base CLASS key which we indirect via // if (pObjectClass) { if (ERROR_SUCCESS == RegOpenKeyEx(hkClasses, pObjectClass, NULL, KEY_READ, &aKeys[UIKEY_CLASS])) { if (SUCCEEDED(LocalQueryString(&pMappedClass, aKeys[UIKEY_CLASS], c_szClass))) { if (ERROR_SUCCESS != RegOpenKeyEx(hkClasses, pMappedClass, NULL, KEY_READ, &aKeys[UIKEY_BASECLASS])) { aKeys[UIKEY_BASECLASS] = NULL; } } } } // // Finally we need the root class (container or object) // hres = GetKeyForCLSID(CLSID_MicrosoftDS, (fIsContainer) ? c_szAllContainers:c_szAllObjects, &aKeys[UIKEY_ROOT]); FailGracefully(hres, "Failed to get root key"); // hres = S_OK; // success exit_gracefully: LocalFreeString(&pMappedClass); if (hkClasses) RegCloseKey(hkClasses); TraceLeaveResult(hres); } /*----------------------------------------------------------------------------- / TidyKeys / -------- / Given an array of keys release them and set them back to zero. / / In: / cKeys = number of keys in array / aKeys = keys to be released / / Out: / void /----------------------------------------------------------------------------*/ void TidyKeys(INT cKeys, HKEY* aKeys) { TraceEnter(TRACE_UI, "TidyKeys"); while (--cKeys >= 0) { if (aKeys[cKeys]) { RegCloseKey(aKeys[cKeys]); aKeys[cKeys] = NULL; // key now closed } } TraceLeaveVoid(); } /*----------------------------------------------------------------------------- / ShowObjectProperties / -------------------- / Display properties for the given objects. This we do by invoking / the tab collector for the given IDataObject. First however we / look inside the object and see if we can find out what objects / were selected, having done that we can then find the HKEYs / / In: / hwndParent = parent dialog / pDataObject = data object that we must use / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT _OverrideProperties(HWND hwndParent, LPDATAOBJECT pDataObject, HKEY* aKeys, INT cKeys, LPCWSTR pPrefix) { HRESULT hres; LPTSTR pPropertiesGUID = NULL; GUID guidProperties; IDsFolderProperties* pDsFolderProperties = NULL; TCHAR szBuffer[MAX_PATH]; INT i; TraceEnter(TRACE_UI, "_OverrideProperties"); // walk all the keys we were given, some will be NULL so ignore those StrCpy(szBuffer, pPrefix); StrCat(szBuffer, c_szDsPropertyUI); Trace(TEXT("Prefixed property handler value: %s"), szBuffer); for (i = 0 ; i < cKeys ; i++) { LocalFreeString(&pPropertiesGUID); if (aKeys[i]) { // if we have a handle attempt to get the GUID string from the registry // and convert it to a GUID so that we can call CoCreateInstance for the // IDsFolderProperites interface. if (FAILED(LocalQueryString(&pPropertiesGUID, aKeys[i], szBuffer))) { TraceMsg("Trying non-prefixed property handler"); if (FAILED(LocalQueryString(&pPropertiesGUID, aKeys[i], c_szDsPropertyUI))) continue; } Trace(TEXT("GUID is: %s"), pPropertiesGUID); if (!GetGUIDFromString(pPropertiesGUID, &guidProperties)) { TraceMsg("Failed to parse GUID"); continue; } if (SUCCEEDED(CoCreateInstance(guidProperties, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDsFolderProperties, &pDsFolderProperties)))) { TraceMsg("Calling IDsFolderProperties::ShowProperties"); hres = pDsFolderProperties->ShowProperties(hwndParent, pDataObject); FailGracefully(hres, "Failed when calling ShowProperties"); goto exit_gracefully; } } } hres = S_FALSE; // S_FALSE indicates that the caller should display properties exit_gracefully: LocalFreeString(&pPropertiesGUID); DoRelease(pDsFolderProperties); TraceLeaveResult(hres); } typedef struct { HWND hwndParent; IStream* pStream; } PROPERTIESTHREADDATA; DWORD WINAPI _ShowObjectPropertiesThread(LPVOID lpParam) { HRESULT hres; PROPERTIESTHREADDATA* pThreadData = (PROPERTIESTHREADDATA*)lpParam; IADsPathname* pPathname = NULL; IDataObject* pDataObject = NULL; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM mediumNames = { TYMED_NULL, NULL, NULL }; STGMEDIUM mediumOptions = { TYMED_NULL, NULL, NULL }; LPDSOBJECTNAMES pDsObjects = NULL; LPDSDISPLAYSPECOPTIONS pDispSpecOptions = NULL; HKEY hKeys[3] = { NULL, NULL, NULL }; LPTSTR pTitle = NULL; LPCWSTR pPrefix = DS_PROP_SHELL_PREFIX; BSTR bstrName = NULL; INT i; TraceEnter(TRACE_UI, "ShowObjectPropertiesThread"); CoInitialize(NULL); hres = CoGetInterfaceAndReleaseStream(pThreadData->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); FailGracefully(hres, "Failed to get data object from stream"); // get the object names that we are showing properites on fmte.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES); hres = pDataObject->GetData(&fmte, &mediumNames); FailGracefully(hres, "Failed to get selected objects"); pDsObjects = (LPDSOBJECTNAMES)GlobalLock(mediumNames.hGlobal); // get the attribute prefix, use this to key information from the registry fmte.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS); if (SUCCEEDED(pDataObject->GetData(&fmte, &mediumOptions))) { pDispSpecOptions = (LPDSDISPLAYSPECOPTIONS)GlobalLock(mediumOptions.hGlobal); pPrefix = (LPCWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetAttribPrefix); } Trace(TEXT("Attribute prefix is: %s"), pPrefix); if (pDsObjects && (pDsObjects->cItems >= 1)) { LPWSTR pPath = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetName); LPWSTR pObjectClass = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetClass); BOOL fSelection = (pDsObjects->cItems > 1); Trace(TEXT("Items %d, 1st object: %s, 1st Class: %s"), pDsObjects->cItems, pPath, pObjectClass); // attempt to pick up the keys for the first element in the selection and get // the keys that map to that object hres = GetKeysForClass(pObjectClass, (pDsObjects->aObjects[0].dwFlags & DSOBJECT_ISCONTAINER), ARRAYSIZE(hKeys), hKeys); FailGracefully(hres, "Failed to get keys for class"); hres = _OverrideProperties(pThreadData->hwndParent, pDataObject, hKeys, ARRAYSIZE(hKeys), pPrefix); FailGracefully(hres, "Failed when trying to call out for properties"); // if the caller returns S_FALSE then we assume that they want us to display // the property pages for the given selection, so do so. if (hres == S_FALSE) { hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IADsPathname, &pPathname)); FailGracefully(hres, "Failed to get the IADsPathname interface"); hres = pPathname->Set(CComBSTR(pPath), ADS_SETTYPE_FULL); FailGracefully(hres, "Failed to set the path of the name"); pPathname->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); pPathname->put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX); hres = pPathname->Retrieve(ADS_FORMAT_LEAF, &bstrName); FailGracefully(hres, "Failed to get the leaf element"); hres = FormatMsgResource(&pTitle, GLOBAL_HINSTANCE, (fSelection) ? IDS_LARGESEL:IDS_SINGLESEL, bstrName); FailGracefully(hres, "Failed to format dialog title"); if (!SHOpenPropSheet(pTitle, hKeys, ARRAYSIZE(hKeys), NULL, pDataObject, NULL, NULL)) ExitGracefully(hres, E_FAIL, "Failed to open property pages"); } } hres = S_OK; exit_gracefully: if (pDsObjects) GlobalUnlock(mediumNames.hGlobal); if (pDispSpecOptions) GlobalUnlock(mediumOptions.hGlobal); ReleaseStgMedium(&mediumNames); ReleaseStgMedium(&mediumOptions); TidyKeys(ARRAYSIZE(hKeys), hKeys); LocalFreeString(&pTitle); SysFreeString(bstrName); DoRelease(pPathname); DoRelease(pDataObject); LocalFree(pThreadData); // // We need to shutdwn COM ultimately here as we don't // know how many times CoInitialize(NULL) has been called. // Otherwise COM is trying to shutdown itself inside compobj!DllMain, // while holding the loader lock which is causing a very bad deadlock. // For more information see bug #395293. // // However we need to brace this code as NT specific code because // otherwise it may cause problems with the Win9x DSUI client for // some weird reason. // ShutdownCOM(); TraceLeave(); DllRelease(); ExitThread(0); return 0; } HRESULT ShowObjectProperties(HWND hwndParent, LPDATAOBJECT pDataObject) { HRESULT hres; PROPERTIESTHREADDATA* pThreadData; DWORD dwId; HANDLE hThread; TraceEnter(TRACE_UI, "ShowObjectProperties"); // Allocate thread data for the new object we are launching pThreadData = (PROPERTIESTHREADDATA*)LocalAlloc(LPTR, SIZEOF(PROPERTIESTHREADDATA)); TraceAssert(pThreadData); if (!pThreadData) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate thread data"); // we have thread data lets fill it and spin the properties thread. pThreadData->hwndParent = hwndParent; hres = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &(pThreadData->pStream)); FailGracefully(hres, "Failed to create marshaling object"); DllAddRef(); hThread = CreateThread(NULL, 0, _ShowObjectPropertiesThread, (LPVOID)pThreadData, 0, &dwId); TraceAssert(hThread); if (!hThread) { if (pThreadData->pStream) pThreadData->pStream->Release(); LocalFree(pThreadData); DllRelease(); ExitGracefully(hres, E_UNEXPECTED, "Failed to kick off the thread"); } CloseHandle(hThread); hres = S_OK; exit_gracefully: TraceLeaveResult(hres); } /*---------------------------------------------------------------------------- / IDsFolderProperties /----------------------------------------------------------------------------*/ class CDsFolderProperties : public IDsFolderProperties { private: LONG _cRef; public: CDsFolderProperties(); ~CDsFolderProperties(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IDsFolderProperties STDMETHOD(ShowProperties)(HWND hwndParent, IDataObject *pDataObject); }; CDsFolderProperties::CDsFolderProperties() : _cRef(1) { DllAddRef(); } CDsFolderProperties::~CDsFolderProperties() { DllRelease(); } STDAPI CDsFolderProperties_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CDsFolderProperties *pdfp = new CDsFolderProperties(); if (!pdfp) return E_OUTOFMEMORY; HRESULT hres = pdfp->QueryInterface(IID_IUnknown, (void **)ppunk); pdfp->Release(); return hres; } // IUnknown HRESULT CDsFolderProperties::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDsFolderProperties, IDsFolderProperties), // IID_IDsFolderProperties {0, 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CDsFolderProperties::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CDsFolderProperties::Release() { TraceAssert( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // Show the property UI for the given selection STDMETHODIMP CDsFolderProperties::ShowProperties(HWND hwndParent, IDataObject *pDataObject) { HRESULT hres; TraceEnter(TRACE_UI, "CDsFolderProperties::ShowProperties"); if (!pDataObject) ExitGracefully(hres, E_INVALIDARG, "No pDataObject given"); CoInitialize(NULL); // ensure we have COM hres = ShowObjectProperties(hwndParent, pDataObject); FailGracefully(hres, "Failed to open property pages"); // hres = S_OK; // success exit_gracefully: TraceLeaveResult(hres); }