windows-nt/Source/XPSP1/NT/shell/ext/dsui/dsquery/ui.cpp
2020-09-26 16:20:57 +08:00

533 lines
15 KiB
C++

#include "pch.h"
#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;
USES_CONVERSION;
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, W2T(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;
USES_CONVERSION;
TraceEnter(TRACE_UI, "_OverrideProperties");
// walk all the keys we were given, some will be NULL so ignore those
StrCpy(szBuffer, W2CT(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_IDsFolderProperties, (LPVOID*)&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;
LPDSDISPLAYSPECOPTIONS pDispSpecOptions;
HKEY hKeys[3] = { NULL, NULL, NULL };
LPTSTR pTitle = NULL;
LPCWSTR pPrefix = DS_PROP_SHELL_PREFIX;
BSTR bstrName = NULL;
INT i;
USES_CONVERSION;
TraceEnter(TRACE_UI, "ShowObjectPropertiesThread");
CoInitialize(NULL);
hres = CoGetInterfaceAndReleaseStream(pThreadData->pStream, IID_IDataObject, (void**)&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)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)mediumOptions.hGlobal;
pPrefix = (LPCWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetAttribPrefix);
}
Trace(TEXT("Attribute prefix is: %s"), W2CT(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, W2T(pPath), W2T(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_IADsPathname, (LPVOID*)&pPathname);
FailGracefully(hres, "Failed to get the IADsPathname interface");
hres = pPathname->Set(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,
W2T(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:
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)
{
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()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
// 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);
}