2025-11-28 00:35:46 +09:00

268 lines
8.6 KiB
C++

// 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 (c) Microsoft Corporation. All rights reserved
#include <windows.h>
#include <shlwapi.h>
#include <propkey.h>
#include "dll.h"
#include "RegisterExtension.h"
#include "PropertyStoreHelpers.h"
class COpenMetadataHandler : public IPropertyStore, public IInitializeWithStream
{
public:
COpenMetadataHandler() : _cRef(1), _grfMode(0), _pStream(NULL), _pCache(NULL)
{
DllAddRef();
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
static const QITAB qit[] =
{
QITABENT(COpenMetadataHandler, IPropertyStore),
QITABENT(COpenMetadataHandler, IInitializeWithStream),
{0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (cRef == 0)
{
delete this;
}
return cRef;
}
// IPropertyStore
IFACEMETHODIMP GetCount(DWORD *pcProps);
IFACEMETHODIMP GetAt(DWORD iProp, PROPERTYKEY *pkey);
IFACEMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *pPropVar);
IFACEMETHODIMP SetValue(REFPROPERTYKEY key, REFPROPVARIANT propVar);
IFACEMETHODIMP Commit();
// IInitializeWithStream
IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
private:
HRESULT _SaveToStream();
~COpenMetadataHandler()
{
SafeRelease(&_pStream);
SafeRelease(&_pCache);
DllRelease();
}
long _cRef;
DWORD _grfMode;
IStream *_pStream;
IPropertyStoreCache *_pCache; // internal value cache to abstract IPropertyStore operations from the DOM back-end
};
HRESULT COpenMetadataHandler_CreateInstance(REFIID riid, void **ppv)
{
HRESULT hr = E_OUTOFMEMORY;
COpenMetadataHandler *pirm = new (std::nothrow) COpenMetadataHandler();
if (pirm)
{
hr = pirm->QueryInterface(riid, ppv);
pirm->Release();
}
return hr;
}
HRESULT COpenMetadataHandler::GetCount(DWORD *pcProps)
{
*pcProps = 0;
return _pCache ? _pCache->GetCount(pcProps) : E_UNEXPECTED;
}
HRESULT COpenMetadataHandler::GetAt(DWORD iProp, PROPERTYKEY *pkey)
{
*pkey = PKEY_Null;
return _pCache ? _pCache->GetAt(iProp, pkey) : E_UNEXPECTED;
}
HRESULT COpenMetadataHandler::GetValue(REFPROPERTYKEY key, PROPVARIANT *pPropVar)
{
PropVariantInit(pPropVar);
return _pCache ? _pCache->GetValue(key, pPropVar) : E_UNEXPECTED;
}
// SetValue just updates the internal value cache
HRESULT COpenMetadataHandler::SetValue(REFPROPERTYKEY key, REFPROPVARIANT propVar)
{
HRESULT hr = E_UNEXPECTED;
if (_pCache)
{
// check grfMode to ensure writes are allowed
hr = STG_E_ACCESSDENIED;
if (_grfMode & STGM_READWRITE)
{
hr = _pCache->SetValueAndState(key, &propVar, PSC_DIRTY);
}
}
return hr;
}
HRESULT COpenMetadataHandler::_SaveToStream()
{
IStream *pStreamSaveTo;
HRESULT hr = GetSafeSaveStream(_pStream, &pStreamSaveTo);
if (SUCCEEDED(hr))
{
// write the XML out to the temprorary stream and commit it
hr = SavePropertyStoreToStream(_pCache, pStreamSaveTo);
if (SUCCEEDED(hr))
{
hr = _pStream->Commit(STGC_DEFAULT); // also commits pStreamSaveTo
}
pStreamSaveTo->Release();
}
return hr;
}
// Commit writes the internal value cache back out to the stream passed to Initialize
HRESULT COpenMetadataHandler::Commit()
{
HRESULT hr = E_UNEXPECTED;
if (_pCache)
{
hr = STG_E_ACCESSDENIED;
if (_grfMode & STGM_READWRITE) // must be opened for writing
{
hr = _SaveToStream();
}
}
return hr;
}
HRESULT COpenMetadataHandler::Initialize(IStream *pStream, DWORD grfMode)
{
HRESULT hr = E_UNEXPECTED;
if (!_pStream)
{
hr = LoadPropertyStoreFromStream(pStream, IID_PPV_ARGS(&_pCache));
if (SUCCEEDED(hr))
{
// save a reference to the stream as well as the grfMode
hr = pStream->QueryInterface(&_pStream);
if (SUCCEEDED(hr))
{
_grfMode = grfMode;
}
}
}
return hr;
}
const WCHAR c_szOpenMetadataFileExtension[] = L".openmetadata-ms";
const WCHAR c_szOpenMetadataProgID[] = L"Windows.OpenMetadata";
const WCHAR c_szOpenMetadataDescription[] = L"Open Metadata File";
HRESULT RegisterOpenMetadata()
{
// register the property handler COM object, and set the options it uses
CRegisterExtension re(__uuidof(COpenMetadataHandler), HKEY_LOCAL_MACHINE);
HRESULT hr = re.RegisterInProcServer(c_szOpenMetadataDescription, L"Both");
if (SUCCEEDED(hr))
{
hr = re.RegisterInProcServerAttribute(L"ManualSafeSave", TRUE);
if (SUCCEEDED(hr))
{
hr = re.RegisterInProcServerAttribute(L"EnableShareDenyWrite", TRUE);
if (SUCCEEDED(hr))
{
hr = re.RegisterInProcServerAttribute(L"EnableShareDenyNone", TRUE);
}
}
}
// Property Handler and Kind registrations use a different mechanism than the rest of the filetype association system, and do not use ProgIDs
if (SUCCEEDED(hr))
{
hr = re.RegisterPropertyHandler(c_szOpenMetadataFileExtension);
if (SUCCEEDED(hr))
{
hr = re.RegisterPropertyHandlerOverride(L"System.Kind");
}
}
// Associate our ProgID with the file extension, and write the remainder of the registration data to the ProgID to minimize conflicts with other applications and facilitate easy unregistration
if (SUCCEEDED(hr))
{
hr = re.RegisterExtensionWithProgID(c_szOpenMetadataFileExtension, c_szOpenMetadataProgID);
if (SUCCEEDED(hr))
{
hr = re.RegisterProgID(c_szOpenMetadataProgID, c_szOpenMetadataDescription, IDI_ICON_OPENMETADATA);
if (SUCCEEDED(hr))
{
hr = re.RegisterProgIDValue(c_szOpenMetadataProgID, L"NoOpen", L"This is a sample file type and does not have any apps installed to handle it");
if (SUCCEEDED(hr))
{
hr = re.RegisterNewMenuNullFile(c_szOpenMetadataFileExtension, c_szOpenMetadataProgID);
if (SUCCEEDED(hr))
{
hr = re.RegisterProgIDValue(c_szOpenMetadataProgID, L"FullDetails", c_szDocFullDetails);
if (SUCCEEDED(hr))
{
hr = re.RegisterProgIDValue(c_szOpenMetadataProgID, L"InfoTip", c_szDocInfoTip);
if (SUCCEEDED(hr))
{
hr = re.RegisterProgIDValue(c_szOpenMetadataProgID, L"PreviewDetails", c_szDocPreviewDetails);
}
}
}
}
}
}
}
// also register the property-driven thumbnail handler on the ProgID
if (SUCCEEDED(hr))
{
re.SetHandlerCLSID(__uuidof(PropertyThumbnailHandler));
hr = re.RegisterThumbnailHandler(c_szOpenMetadataProgID);
}
return hr;
}
HRESULT UnregisterOpenMetadata()
{
// Unregister the property handler COM object.
CRegisterExtension re(__uuidof(COpenMetadataHandler), HKEY_LOCAL_MACHINE);
HRESULT hr = re.UnRegisterObject();
if (SUCCEEDED(hr))
{
// Unregister the property handler and kind for the file extension.
hr = re.UnRegisterPropertyHandler(c_szOpenMetadataFileExtension);
if (SUCCEEDED(hr))
{
hr = re.UnRegisterKind(c_szOpenMetadataFileExtension);
if (SUCCEEDED(hr))
{
// Remove the whole ProgID since we own all of those settings.
// Don't try to remove the file extension association since some other application may have overridden it with their own ProgID in the meantime.
// Leaving the association to a non-existing ProgID is handled gracefully by the Shell.
// NOTE: If the file extension is unambiguously owned by this application, the association to the ProgID could be safely removed as well,
// along with any other association data stored on the file extension itself.
hr = re.UnRegisterProgID(c_szOpenMetadataProgID, c_szOpenMetadataFileExtension);
}
}
}
return hr;
}