From d9e7c48c1a812dc9293f367875915288df7e1df2 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Sat, 3 Aug 2019 15:14:20 +0200 Subject: [PATCH] [FONTEXT] Initial implementation Create Fonts\desktop.ini when registering the shell ext Also list the shell extension as needing to be registered at install CORE-14690 --- dll/shellext/fontext/CDataObject.cpp | 141 +++++++++++ dll/shellext/fontext/CEnumFonts.cpp | 91 +++++++ dll/shellext/fontext/CFontCache.cpp | 173 +++++++++++++ dll/shellext/fontext/CFontCache.hpp | 48 ++++ dll/shellext/fontext/CFontExt.cpp | 365 +++++++++++++++++++++++++++ dll/shellext/fontext/CFontExt.hpp | 84 ++++++ dll/shellext/fontext/CFontMenu.cpp | 180 +++++++++++++ dll/shellext/fontext/CMakeLists.txt | 32 ++- dll/shellext/fontext/fontext.c | 48 ---- dll/shellext/fontext/fontext.cpp | 120 +++++++++ dll/shellext/fontext/fontext.h | 23 +- dll/shellext/fontext/fontext.rc | 2 + dll/shellext/fontext/fontpidl.cpp | 38 +++ dll/shellext/fontext/fontpidl.hpp | 24 ++ dll/shellext/fontext/lang/en-US.rc | 12 + dll/shellext/fontext/precomp.h | 39 +++ dll/shellext/fontext/regsvr.c | 35 --- dll/shellext/fontext/res/fontext.rgs | 14 + dll/shellext/fontext/resource.h | 6 + media/inf/syssetup.inf | 1 + 20 files changed, 1382 insertions(+), 94 deletions(-) create mode 100644 dll/shellext/fontext/CDataObject.cpp create mode 100644 dll/shellext/fontext/CEnumFonts.cpp create mode 100644 dll/shellext/fontext/CFontCache.cpp create mode 100644 dll/shellext/fontext/CFontCache.hpp create mode 100644 dll/shellext/fontext/CFontExt.cpp create mode 100644 dll/shellext/fontext/CFontExt.hpp create mode 100644 dll/shellext/fontext/CFontMenu.cpp delete mode 100644 dll/shellext/fontext/fontext.c create mode 100644 dll/shellext/fontext/fontext.cpp create mode 100644 dll/shellext/fontext/fontpidl.cpp create mode 100644 dll/shellext/fontext/fontpidl.hpp create mode 100644 dll/shellext/fontext/precomp.h delete mode 100644 dll/shellext/fontext/regsvr.c create mode 100644 dll/shellext/fontext/res/fontext.rgs diff --git a/dll/shellext/fontext/CDataObject.cpp b/dll/shellext/fontext/CDataObject.cpp new file mode 100644 index 00000000000..252b125fc92 --- /dev/null +++ b/dll/shellext/fontext/CDataObject.cpp @@ -0,0 +1,141 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CFontMenu implementation + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + + +#if 0 +static inline void DumpDataObjectFormats(IDataObject* pObject) +{ + CComPtr pEnumFmt; + HRESULT hr = pObject->EnumFormatEtc(DATADIR_GET, &pEnumFmt); + + if (FAILED_UNEXPECTEDLY(hr)) + return; + + FORMATETC fmt; + while (S_OK == pEnumFmt->Next(1, &fmt, NULL)) + { + char szBuf[512]; + GetClipboardFormatNameA(fmt.cfFormat, szBuf, sizeof(szBuf)); + ERR("Format: %s\n", szBuf); + ERR("Tymed: %u\n", fmt.tymed); + if (fmt.tymed & TYMED_HGLOBAL) + { + ERR("TYMED_HGLOBAL supported\n"); + } + } +} +#endif + + +HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, LPVOID* ppvOut) +{ + HRESULT hr = CIDLData_CreateFromIDArray(folder, cidl, apidl, (IDataObject**)ppvOut); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + // Now that we have an IDataObject with the shell itemid list (CFSTR_SHELLIDLIST, aka HIDA) format + // we will augment this IDataObject with the CF_HDROP format. (Full filepaths) + // This enabled the objects for the 'copy' and drag to copy actions + WCHAR FontsDir[MAX_PATH]; + hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir); + if (FAILED_UNEXPECTEDLY(hr)) + return S_OK; + StringCchCatW(FontsDir, _countof(FontsDir), L"\\"); + + CComHeapPtr data; + + // First we allocate room for the DROPFILES structure + data.AllocateBytes(sizeof(DROPFILES)); + UINT offset = sizeof(DROPFILES); + + // Then we walk all files + for (UINT n = 0; n < cidl; ++n) + { + const FontPidlEntry* fontEntry = _FontFromIL(apidl[n]); + if (fontEntry) + { + CStringW File = g_FontCache->Filename(fontEntry); + if (!File.IsEmpty()) + { + // Ensure this is a full path + if (PathIsRelativeW(File)) + { + File = FontsDir + File; + } + + // Now append the path (+ nullterminator) to the buffer + UINT len = offset + (File.GetLength() + 1) * sizeof(WCHAR); + data.ReallocateBytes(len); + if (!data) + { + ERR("Unable to allocate memory for the CF_HDROP\n"); + return hr; + } + BYTE* dataPtr = data; + StringCbCopyW((STRSAFE_LPWSTR)(dataPtr + offset), len - offset, File); + offset = len; + } + else + { + ERR("No file found for %S\n", fontEntry->Name); + } + } + } + + // Append the final nullterminator (double null terminated list) + data.ReallocateBytes(offset + sizeof(UNICODE_NULL)); + LPWSTR str = (LPWSTR)((BYTE*)data + offset); + *str = UNICODE_NULL; + offset += sizeof(UNICODE_NULL); + + // Fill in the required fields + DROPFILES* pDrop = (DROPFILES*)(BYTE*)data; + pDrop->fWide = 1; + pDrop->pFiles = sizeof(DROPFILES); + // Zero out the rest + pDrop->pt.x = pDrop->pt.y = 0; + pDrop-> fNC = NULL; + + // Prepare the format descriptors + STGMEDIUM medium = {0}; + medium.tymed = TYMED_HGLOBAL; + + // Copy the data to an HGLOBAL + medium.hGlobal = GlobalAlloc(GHND, offset); + if (medium.hGlobal) + { + LPVOID blob = GlobalLock(medium.hGlobal); + if (blob) + { + CopyMemory(blob, (BYTE*)data, offset); + GlobalUnlock(medium.hGlobal); + + CComPtr spDataObject(*(IDataObject**)ppvOut); + if (spDataObject) + { + FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + hr = spDataObject->SetData(&etc, &medium, TRUE); + } + } + else + { + ERR("Unable to lock the hGlobal?!\n"); + } + } + else + { + ERR("Unable to allocate %u bytes for the hGlobal\n", offset); + } + + return hr; +} + diff --git a/dll/shellext/fontext/CEnumFonts.cpp b/dll/shellext/fontext/CEnumFonts.cpp new file mode 100644 index 00000000000..7e197187cdf --- /dev/null +++ b/dll/shellext/fontext/CEnumFonts.cpp @@ -0,0 +1,91 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CEnumFonts implementation + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + + + +class CEnumFonts : + public CComObjectRootEx, + public IEnumIDList +{ +private: + DWORD m_dwFlags; + ULONG m_Index; + +public: + CEnumFonts() + :m_dwFlags(0) + ,m_Index(0) + { + } + + STDMETHODIMP Initialize(CFontExt* folder, DWORD flags) + { + m_dwFlags = flags; + m_Index = 0; + return S_OK; + } + + // *** IEnumIDList methods *** + STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) + { + if (!pceltFetched || !rgelt) + return E_POINTER; + + ULONG Fetched = 0; + + while (celt) + { + celt--; + + if (m_Index < g_FontCache->Size()) + { + CStringW Name = g_FontCache->Name(m_Index); + rgelt[Fetched] = _ILCreate(Name, m_Index); + + m_Index++; + Fetched++; + } + } + + *pceltFetched = Fetched; + return Fetched ? S_OK : S_FALSE; + } + STDMETHODIMP Skip(ULONG celt) + { + m_Index += celt; + return S_OK; + } + STDMETHODIMP Reset() + { + m_Index = 0; + return S_OK; + } + STDMETHODIMP Clone(IEnumIDList **ppenum) + { + return E_NOTIMPL; + } + + +public: + DECLARE_NOT_AGGREGATABLE(CEnumFonts) + DECLARE_PROTECT_FINAL_CONSTRUCT() + + BEGIN_COM_MAP(CEnumFonts) + COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) + END_COM_MAP() +}; + + +HRESULT _CEnumFonts_CreateInstance(CFontExt* zip, DWORD flags, REFIID riid, LPVOID * ppvOut) +{ + return ShellObjectCreatorInit(zip, flags, riid, ppvOut); +} + diff --git a/dll/shellext/fontext/CFontCache.cpp b/dll/shellext/fontext/CFontCache.cpp new file mode 100644 index 00000000000..4d3ae5fcdf5 --- /dev/null +++ b/dll/shellext/fontext/CFontCache.cpp @@ -0,0 +1,173 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: font list cache handling + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + + +#define FONT_HIVE HKEY_LOCAL_MACHINE +#define FONT_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" + +CFontCache* g_FontCache = NULL; + +CFontInfo::CFontInfo(LPCWSTR name) + : m_Name(name) + , m_FileRead(false) +{ +} + +const CStringW& CFontInfo::Name() const +{ + return m_Name; +} + +const bool CFontInfo::Valid() const +{ + return !m_Name.IsEmpty(); +} + +const CStringW& CFontInfo::File() +{ + if (!m_FileRead) + { + if (Valid()) + { + // Read the filename stored in the registry. + // This can be either a filename or a full path + CRegKey key; + if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) + { + CStringW Value; + DWORD dwAllocated = 128; + LSTATUS Status; + do + { + DWORD dwSize = dwAllocated; + PWSTR Buffer = Value.GetBuffer(dwSize); + Status = key.QueryStringValue(m_Name, Buffer, &dwSize); + Value.ReleaseBuffer(dwSize); + if (Status == ERROR_SUCCESS) + { + // Ensure we do not re-use the same string object, by passing it a PCWSTR + m_File = Value.GetString(); + break; + } + dwAllocated += 128; + } while (Status == ERROR_MORE_DATA); + } + } + m_FileRead = true; + } + return m_File; +} + + + +CFontCache::CFontCache() +{ +} + +size_t CFontCache::Size() +{ + if (m_Fonts.GetCount() == 0u) + Read(); + + return m_Fonts.GetCount(); +} + +CStringW CFontCache::Name(size_t Index) +{ + if (m_Fonts.GetCount() == 0u) + Read(); + + if (Index >= m_Fonts.GetCount()) + return CStringW(); + + return m_Fonts[Index].Name(); +} + +CStringW CFontCache::Filename(const FontPidlEntry* fontEntry) +{ + if (fontEntry->Index < m_Fonts.GetCount()) + { + CFontInfo& info = m_Fonts[fontEntry->Index]; + + if (info.Name().CompareNoCase(fontEntry->Name) == 0) + return info.File(); + } + + for (UINT n = 0; n < Size(); ++n) + { + if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0) + return m_Fonts[n].File(); + } + + return CStringW(); +} + +void CFontCache::Insert(CAtlList& fonts, const CStringW& KeyName) +{ + POSITION it = fonts.GetHeadPosition(); + while (it != NULL) + { + POSITION lastit = it; + const CFontInfo& info = fonts.GetNext(it); + if (info.Name().CompareNoCase(KeyName) >= 0) + { + fonts.InsertBefore(lastit, CFontInfo(KeyName)); + return; + } + } + fonts.AddTail(CFontInfo(KeyName)); +} + +void CFontCache::Read() +{ + CAtlList fonts; + CRegKey key; + + // Enumerate all registered font names + if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS) + { + LSTATUS Status; + DWORD dwAllocated = 128; + DWORD ilIndex = 0; + CStringW KeyName; + do + { + DWORD dwSize = dwAllocated; + PWSTR Buffer = KeyName.GetBuffer(dwSize); + Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL); + KeyName.ReleaseBuffer(dwSize); + if (Status == ERROR_SUCCESS) + { + // Insert will create an ordered list + Insert(fonts, KeyName); + ilIndex++; + continue; + } + if (Status == ERROR_NO_MORE_ITEMS) + break; + else if (Status == ERROR_MORE_DATA) + { + dwAllocated += 128; + } + } while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS); + } + + // Move the fonts from a list to an array (for easy indexing) + m_Fonts.SetCount(fonts.GetCount()); + size_t Index = 0; + POSITION it = fonts.GetHeadPosition(); + while (it != NULL) + { + m_Fonts[Index] = fonts.GetNext(it); + Index++; + } +} + diff --git a/dll/shellext/fontext/CFontCache.hpp b/dll/shellext/fontext/CFontCache.hpp new file mode 100644 index 00000000000..226c061f685 --- /dev/null +++ b/dll/shellext/fontext/CFontCache.hpp @@ -0,0 +1,48 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: font list cache handling + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#pragma once + + +class CFontInfo +{ +private: + CStringW m_Name; + CStringW m_File; + bool m_FileRead; +public: + CFontInfo(LPCWSTR name = L""); + + const CStringW& Name() const; + const CStringW& File(); + const bool Valid() const; +}; + + +class CFontCache +{ +private: + CAtlArray m_Fonts; + +protected: + CFontCache(); + + void Insert(CAtlList& fonts, const CStringW& KeyName); + void Read(); + +public: + size_t Size(); + CStringW Name(size_t Index); + CStringW Filename(const FontPidlEntry* fontEntry); + + friend class CFontExtModule; +}; + + +extern CFontCache* g_FontCache; + + diff --git a/dll/shellext/fontext/CFontExt.cpp b/dll/shellext/fontext/CFontExt.cpp new file mode 100644 index 00000000000..298c6b84c9f --- /dev/null +++ b/dll/shellext/fontext/CFontExt.cpp @@ -0,0 +1,365 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CFontExt implementation + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + + +struct FolderViewColumns +{ + int iResource; + DWORD dwDefaultState; + int cxChar; + int fmt; +}; + +static FolderViewColumns g_ColumnDefs[] = +{ + { IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT }, +}; + + + +// Should fix our headers.. +EXTERN_C HRESULT WINAPI SHCreateFileExtractIconW(LPCWSTR pszPath, DWORD dwFileAttributes, REFIID riid, void **ppv); + + +// Helper functions to translate a guid to a readable name +bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size) +{ + WCHAR LocalBuf[100]; + DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR); + + if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf), L"Interface\\%s", InterfaceString))) + return false; + + return RegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType, buf, &dwDataSize) == ERROR_SUCCESS; +} + +WCHAR* g2s(REFCLSID iid) +{ + static WCHAR buf[2][300]; + static int idx = 0; + + idx ^= 1; + + LPOLESTR tmp; + HRESULT hr = ProgIDFromCLSID(iid, &tmp); + if (SUCCEEDED(hr)) + { + wcscpy(buf[idx], tmp); + CoTaskMemFree(tmp); + return buf[idx]; + } + StringFromGUID2(iid, buf[idx], _countof(buf[idx])); + if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx]))) + { + return buf[idx]; + } + StringFromGUID2(iid, buf[idx], _countof(buf[idx])); + + return buf[idx]; +} + + +CFontExt::CFontExt() +{ + InterlockedIncrement(&g_ModuleRefCnt); +} + +CFontExt::~CFontExt() +{ + InterlockedDecrement(&g_ModuleRefCnt); +} + +// *** IShellFolder2 methods *** +STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::EnumSearches(IEnumExtraSearch **ppenum) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags) +{ + if (!pcsFlags || iColumn >= _countof(g_ColumnDefs)) + return E_INVALIDARG; + + *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState; + return S_OK; +} + +STDMETHODIMP CFontExt::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd) +{ + if (iColumn >= _countof(g_ColumnDefs)) + return E_FAIL; + + psd->cxChar = g_ColumnDefs[iColumn].cxChar; + psd->fmt = g_ColumnDefs[iColumn].fmt; + + // No item requested, so return the column name + if (pidl == NULL) + { + return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource); + } + + // Validate that this pidl is the last one + PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl); + if (curpidl->mkid.cb != 0) + { + ERR("ERROR, unhandled PIDL!\n"); + return E_FAIL; + } + + switch (iColumn) + { + case 0: /* Name, ReactOS specific? */ + return GetDisplayNameOf(pidl, 0, &psd->str); + default: + break; + } + + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) +{ + //ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +// *** IShellFolder2 methods *** +STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) +{ + return _CEnumFonts_CreateInstance(this, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); +} + +STDMETHODIMP CFontExt::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) +{ + ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid)); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) +{ + const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1); + const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2); + + if (!fontEntry1 || !fontEntry2) + return E_INVALIDARG; + + int result = (int)fontEntry1->Index - (int)fontEntry2->Index; + + return MAKE_COMPARE_HRESULT(result); +} + +STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) +{ + HRESULT hr = E_NOINTERFACE; + + *ppvOut = NULL; + + if (IsEqualIID(riid, IID_IDropTarget)) + { + // Needed to drop files into the fonts folder, we should probably install them? + ERR("IDropTarget not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID(riid, IID_IContextMenu)) + { + ERR("IContextMenu not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID(riid, IID_IShellView)) + { + // Just create a default shell folder view, and register ourself as folder + SFV_CREATE sfv = { sizeof(SFV_CREATE) }; + sfv.pshf = this; + hr = SHCreateShellFolderView(&sfv, (IShellView**)ppvOut); + } + + return hr; +} + +STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) +{ + if (!rgfInOut || !cidl || !apidl) + return E_INVALIDARG; + + DWORD rgf = 0; + while (cidl > 0 && *apidl) + { + const FontPidlEntry* fontEntry = _FontFromIL(*apidl); + if (fontEntry) + { + // We don't support delete yet + rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM); + } + else + { + rgf = 0; + break; + } + + apidl++; + cidl--; + } + + *rgfInOut = rgf; + return S_OK; +} + + +STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) +{ + if (riid == IID_IContextMenu || + riid == IID_IContextMenu2 || + riid == IID_IContextMenu3) + { + return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut); + } + else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW) + { + if (cidl == 1) + { + const FontPidlEntry* fontEntry = _FontFromIL(*apidl); + if (fontEntry) + { + DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL; + CStringW File = g_FontCache->Filename(fontEntry); + // Just create a default icon extractor based on the filename + // We might want to create a preview with the font to get really fancy one day. + return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut); + } + } + else + { + ERR("IID_IExtractIcon with cidl != 1 UNIMPLEMENTED\n"); + } + } + else if (riid == IID_IDataObject) + { + if (cidl >= 1) + { + return _CDataObject_CreateInstance(m_Folder, cidl, apidl, riid, ppvOut); + } + else + { + ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n"); + } + } + + //ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid)); + return E_NOTIMPL; +} + +STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) +{ + if (!pidl) + return S_FALSE; + + // Validate that this pidl is the last one + PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl); + if (curpidl->mkid.cb != 0) + { + ERR("ERROR, unhandled PIDL!\n"); + return E_FAIL; + } + + const FontPidlEntry* fontEntry = _FontFromIL(pidl); + if (!fontEntry) + return E_FAIL; + + return SHSetStrRet(strRet, fontEntry->Name); +} + +STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) +{ + ERR("%s() UNIMPLEMENTED\n", __FUNCTION__); + return E_NOTIMPL; +} + +// *** IPersistFolder2 methods *** +STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl) +{ + if (ppidl && m_Folder) + { + *ppidl = ILClone(m_Folder); + return S_OK; + } + + return E_POINTER; +} + + +// *** IPersistFolder methods *** +STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl) +{ + WCHAR PidlPath[MAX_PATH + 1] = {0}, Expected[MAX_PATH + 1]; + if (!SHGetPathFromIDListW(pidl, PidlPath)) + { + ERR("Unable to extract path from pidl\n"); + return E_FAIL; + } + + HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, Expected); + if (!SUCCEEDED(hr)) + { + ERR("Unable to get fonts path (0x%x)\n", hr); + return hr; + } + + if (_wcsicmp(PidlPath, Expected)) + { + ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath); + return E_FAIL; + } + + m_Folder.Attach(ILClone(pidl)); + + return S_OK; +} + + +// *** IPersist methods *** +STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId) +{ + *lpClassId = CLSID_CFontExt; + return S_OK; +} + diff --git a/dll/shellext/fontext/CFontExt.hpp b/dll/shellext/fontext/CFontExt.hpp new file mode 100644 index 00000000000..0e7a04482d9 --- /dev/null +++ b/dll/shellext/fontext/CFontExt.hpp @@ -0,0 +1,84 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CFontExt definition + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#pragma once + +class CFontExt : + public CComCoClass, + public CComObjectRootEx, + public IShellFolder2, + public IPersistFolder2 +{ + CComHeapPtr m_Folder; + +public: + + CFontExt(); + ~CFontExt(); + + // *** IShellFolder2 methods *** + virtual STDMETHODIMP GetDefaultSearchGUID(GUID *lpguid); + virtual STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum); + virtual STDMETHODIMP GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay); + virtual STDMETHODIMP GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags); + virtual STDMETHODIMP GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv); + virtual STDMETHODIMP GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd); + virtual STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid); + + // *** IShellFolder methods *** + virtual STDMETHODIMP ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes); + virtual STDMETHODIMP EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList); + virtual STDMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut); + virtual STDMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut); + virtual STDMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2); + virtual STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut); + virtual STDMETHODIMP GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut); + virtual STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut); + virtual STDMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet); + virtual STDMETHODIMP SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut); + + + // *** IPersistFolder2 methods *** + virtual STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl); + + // *** IPersistFolder methods *** + virtual STDMETHODIMP Initialize(LPCITEMIDLIST pidl); + + // *** IPersist methods *** + virtual STDMETHODIMP GetClassID(CLSID *lpClassId); + + +#if 0 + static HRESULT WINAPI log_stuff(void* pv, REFIID riid, LPVOID* ppv, DWORD_PTR dw) + { + WCHAR* g2s(REFCLSID iid); + + WCHAR buffer[MAX_PATH]; + StringCchPrintfW(buffer, _countof(buffer), L"CFontExt::QueryInterface(%s)\n", g2s(riid)); + OutputDebugStringW(buffer); + + return E_NOINTERFACE; + } +#endif + +protected: + +public: + + DECLARE_REGISTRY_RESOURCEID(IDR_FONTEXT) + DECLARE_NOT_AGGREGATABLE(CFontExt) + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + BEGIN_COM_MAP(CFontExt) + COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2) + COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder) + COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) + COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) + //COM_INTERFACE_ENTRY_FUNC_BLIND(0, log_stuff) + END_COM_MAP() +}; diff --git a/dll/shellext/fontext/CFontMenu.cpp b/dll/shellext/fontext/CFontMenu.cpp new file mode 100644 index 00000000000..eab4427202d --- /dev/null +++ b/dll/shellext/fontext/CFontMenu.cpp @@ -0,0 +1,180 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CFontMenu implementation + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + + +static inline PCUIDLIST_ABSOLUTE HIDA_GetPIDLFolder(CIDA const* pida) +{ + return (PCUIDLIST_ABSOLUTE)(((LPBYTE)pida) + (pida)->aoffset[0]); +} + +static inline PCUIDLIST_RELATIVE HIDA_GetPIDLItem(CIDA const* pida, SIZE_T i) +{ + return (PCUIDLIST_RELATIVE)(((LPBYTE)pida) + (pida)->aoffset[i + 1]); +} + +static CLIPFORMAT g_cfHIDA; +HRESULT _GetCidlFromDataObject(IDataObject *pDataObject, CIDA** ppcida) +{ + if (g_cfHIDA == NULL) + { + g_cfHIDA = (CLIPFORMAT)RegisterClipboardFormatW(CFSTR_SHELLIDLIST); + } + + FORMATETC fmt = { g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium; + + HRESULT hr = pDataObject->GetData(&fmt, &medium); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + LPVOID lpSrc = GlobalLock(medium.hGlobal); + SIZE_T cbSize = GlobalSize(medium.hGlobal); + + *ppcida = (CIDA *)::CoTaskMemAlloc(cbSize); + if (*ppcida) + { + memcpy(*ppcida, lpSrc, cbSize); + hr = S_OK; + } + else + { + hr = E_FAIL; + } + ReleaseStgMedium(&medium); + return hr; +} + +const char* DFM_TO_STR(UINT uMsg) +{ + switch(uMsg) + { + case DFM_MERGECONTEXTMENU: return "DFM_MERGECONTEXTMENU"; + case DFM_INVOKECOMMAND: return "DFM_INVOKECOMMAND"; + case DFM_MODIFYQCMFLAGS: return "DFM_MODIFYQCMFLAGS"; + case DFM_MERGECONTEXTMENU_TOP: return "DFM_MERGECONTEXTMENU_TOP"; + case DFM_MERGECONTEXTMENU_BOTTOM: return "DFM_MERGECONTEXTMENU_BOTTOM"; + case DFM_GETHELPTEXTW: return "DFM_GETHELPTEXTW"; + case DFM_GETVERBW: return "DFM_GETVERBW"; + case DFM_GETVERBA: return "DFM_GETVERBA"; + case DFM_WM_INITMENUPOPUP: return "DFM_WM_INITMENUPOPUP"; + case DFM_INVOKECOMMANDEX: return "DFM_INVOKECOMMANDEX"; + case DFM_GETDEFSTATICID: return "DFM_GETDEFSTATICID"; + case 3: return "MENU_BEGIN"; + case 4: return "MENU_END"; + default: return ""; + } +} + + +static void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry) +{ + WCHAR FontViewerPath[MAX_PATH] = L"%SystemRoot%\\System32\\fontview.exe"; + WCHAR FontPathArg[MAX_PATH + 3]; + + CStringW Path = g_FontCache->Filename(fontEntry); + if (!Path.IsEmpty()) + { + if (PathIsRelativeW(Path)) + { + WCHAR FontsDir[MAX_PATH]; + HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir); + if (!FAILED_UNEXPECTEDLY(hr)) + { + StringCchCatW(FontsDir, _countof(FontsDir), L"\\"); + Path = FontsDir + Path; + } + } + + // '/d' disables the install button + StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString()); + PathQuoteSpacesW(FontPathArg + 3); + + SHELLEXECUTEINFOW si = { sizeof(si) }; + si.fMask = SEE_MASK_DOENVSUBST; + si.hwnd = hwnd; + si.lpFile = FontViewerPath; + si.lpParameters = FontPathArg; + si.nShow = SW_SHOWNORMAL; + ShellExecuteExW(&si); + } +} + +static HRESULT CALLBACK FontFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, + UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg, DFM_TO_STR(uMsg)); + switch (uMsg) + { + case DFM_MERGECONTEXTMENU: + { + QCMINFO *pqcminfo = (QCMINFO *)lParam; + + CStringW menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW)); + MENUITEMINFOW cmi = { sizeof(cmi) }; + cmi.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE; + cmi.fType = MFT_STRING; + cmi.fState = MFS_DEFAULT; + cmi.wID = pqcminfo->idCmdFirst++; + cmi.dwTypeData = (LPWSTR)menuText.GetString(); + InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu, TRUE, &cmi); + + return S_OK; + } + case DFM_INVOKECOMMAND: + // Preview is the only item we can handle + if (wParam == 0) + { + CComHeapPtr cida; + HRESULT hr = _GetCidlFromDataObject(pdtobj, &cida); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + for (UINT n = 0; n < cida->cidl; ++n) + { + const FontPidlEntry* fontEntry = _FontFromIL(HIDA_GetPIDLItem(cida, n)); + RunFontViewer(hwnd, fontEntry); + } + return S_OK; + } + return S_FALSE; + + case DFM_INVOKECOMMANDEX: + return E_NOTIMPL; + case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default + return S_FALSE; + } + return E_NOTIMPL; +} + + +HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, + IShellFolder *psf, REFIID riid, LPVOID* ppvOut) +{ + if (cidl > 0) + { + HKEY keys[1] = {0}; + int nkeys = 0; + CComPtr spMenu; + + // Use the default context menu handler, but augment it from the callbacks + HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu); + + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + // See if the requested interface (e.g. IContextMenu3) is also available + return spMenu->QueryInterface(riid, ppvOut); + } + + // We can't create a background menu + return E_FAIL; +} + diff --git a/dll/shellext/fontext/CMakeLists.txt b/dll/shellext/fontext/CMakeLists.txt index b50844ee7e5..a5ae38822cf 100644 --- a/dll/shellext/fontext/CMakeLists.txt +++ b/dll/shellext/fontext/CMakeLists.txt @@ -1,18 +1,38 @@ +add_definitions( + -D_ATL_NO_EXCEPTIONS) + +remove_definitions(-D_WIN32_WINNT=0x502 -DWINVER=0x502) +add_definitions(-D_WIN32_WINNT=0x601 -DWINVER=0x601) + +set_cpp(WITH_RUNTIME) + spec2def(fontext.dll fontext.spec) +include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/atl) + list(APPEND SOURCE - fontext.c - regsvr.c - fontext.h) + CDataObject.cpp + CEnumFonts.cpp + CFontCache.cpp + CFontCache.hpp + CFontExt.cpp + CFontExt.hpp + CFontMenu.cpp + fontext.cpp + fontpidl.cpp + fontpidl.hpp + precomp.h) add_library(fontext MODULE ${SOURCE} fontext.rc + fontext.spec ${CMAKE_CURRENT_BINARY_DIR}/fontext.def) set_module_type(fontext win32dll UNICODE) -target_link_libraries(fontext uuid) -add_importlibs(fontext user32 gdi32 ole32 shlwapi lz32 advapi32 setupapi msvcrt kernel32 ntdll) -add_pch(fontext fontext.h SOURCE) +target_link_libraries(fontext uuid wine) +add_delay_importlibs(fontext ole32 oleaut32 shlwapi) +add_importlibs(fontext shell32 advapi32 user32 msvcrt kernel32 ntdll) +add_pch(fontext precomp.h SOURCE) add_cd_file(TARGET fontext DESTINATION reactos/system32 FOR all) diff --git a/dll/shellext/fontext/fontext.c b/dll/shellext/fontext/fontext.c deleted file mode 100644 index 3102a795f3c..00000000000 --- a/dll/shellext/fontext/fontext.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * PROJECT: fontext.dll - * FILE: dll/shellext/fontext/fontext.c - * PURPOSE: fontext.dll - * PROGRAMMER: Dmitry Chapyshev (dmitry@reactos.org) - * UPDATE HISTORY: - * 10-06-2008 Created - */ - -#include "fontext.h" - -#include -#include - -static HINSTANCE hInstance; - -HRESULT WINAPI -DllCanUnloadNow(VOID) -{ - DPRINT1("DllCanUnloadNow() stubs\n"); - return S_OK; -} - -HRESULT WINAPI -DllGetClassObject(REFCLSID rclsid, - REFIID riid, - LPVOID *ppv) -{ - DPRINT1("DllGetClassObject() stubs\n"); - return S_OK; -} - -BOOL WINAPI -DllMain(HINSTANCE hinstDLL, - DWORD dwReason, - LPVOID lpvReserved) -{ - switch (dwReason) - { - case DLL_PROCESS_ATTACH: - hInstance = hinstDLL; - DisableThreadLibraryCalls(hInstance); - break; - } - - return TRUE; -} diff --git a/dll/shellext/fontext/fontext.cpp b/dll/shellext/fontext/fontext.cpp new file mode 100644 index 00000000000..ab1ed51bbc7 --- /dev/null +++ b/dll/shellext/fontext/fontext.cpp @@ -0,0 +1,120 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Shell extension entry point + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + +const GUID CLSID_CFontExt = { 0xbd84b380, 0x8ca2, 0x1069, { 0xab, 0x1d, 0x08, 0x00, 0x09, 0x48, 0xf5, 0x34 } }; + + +class CFontExtModule : public CComModule +{ +public: + void Init(_ATL_OBJMAP_ENTRY *p, HINSTANCE h, const GUID *plibid) + { + g_FontCache = new CFontCache(); + CComModule::Init(p, h, plibid); + } + + void Term() + { + delete g_FontCache; + g_FontCache = NULL; + CComModule::Term(); + } +}; + +BEGIN_OBJECT_MAP(ObjectMap) + OBJECT_ENTRY(CLSID_CFontExt, CFontExt) +END_OBJECT_MAP() + + +LONG g_ModuleRefCnt; +CFontExtModule gModule; + + +STDAPI DllCanUnloadNow() +{ + if (g_ModuleRefCnt) + return S_FALSE; + return gModule.DllCanUnloadNow(); +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + return gModule.DllGetClassObject(rclsid, riid, ppv); +} + + +STDAPI DllRegisterServer() +{ + WCHAR Path[MAX_PATH] = { 0 }; + static const char DesktopIniContents[] = "[.ShellClassInfo]\r\nCLSID={BD84B380-8CA2-1069-AB1D-08000948F534}\r\n"; + HANDLE hFile; + HRESULT hr; + + hr = gModule.DllRegisterServer(FALSE); + if (FAILED(hr)) + return hr; + + hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, Path); + if (FAILED(hr)) + return hr; + + // Make this a system folder: + // Ideally this should not be done here, but when installing + // Otherwise, livecd won't have this set properly + DWORD dwAttributes = GetFileAttributesW(Path); + if (dwAttributes != INVALID_FILE_ATTRIBUTES) + { + dwAttributes |= FILE_ATTRIBUTE_SYSTEM; + SetFileAttributesW(Path, dwAttributes); + } + else + { + ERR("Unable to get attributes for fonts folder (%d)\n", GetLastError()); + } + + if (!PathAppendW(Path, L"desktop.ini")) + return E_FAIL; + + hFile = CreateFileW(Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(GetLastError()); + + DWORD BytesWritten, BytesToWrite = strlen(DesktopIniContents); + if (WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL)) + hr = (BytesToWrite == BytesWritten) ? S_OK : E_FAIL; + else + hr = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(hFile); + return hr; +} + +STDAPI DllUnregisterServer() +{ + return gModule.DllUnregisterServer(FALSE); +} + + +EXTERN_C +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstance); + gModule.Init(ObjectMap, hInstance, NULL); + break; + case DLL_PROCESS_DETACH: + gModule.Term(); + break; + } + + return TRUE; +} diff --git a/dll/shellext/fontext/fontext.h b/dll/shellext/fontext/fontext.h index aa9d56413c8..a5dd1d08839 100644 --- a/dll/shellext/fontext/fontext.h +++ b/dll/shellext/fontext/fontext.h @@ -1,11 +1,24 @@ -#ifndef _FONTEXT_PCH_ -#define _FONTEXT_PCH_ +#ifndef FONTEXT_PRECOMP_H +#define FONTEXT_PRECOMP_H -#include #define WIN32_NO_STATUS -#define COBJMACROS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H #include +#include +#include +#include +#include +#include +#include +#include +#include -#endif /* _FONTEXT_PCH_ */ +extern const GUID CLSID_CFontExt; + +#include "CFontExt.hpp" + + +#endif /* FONTEXT_PRECOMP_H */ diff --git a/dll/shellext/fontext/fontext.rc b/dll/shellext/fontext/fontext.rc index c2c4f2bd8cd..f4cb69b8af0 100644 --- a/dll/shellext/fontext/fontext.rc +++ b/dll/shellext/fontext/fontext.rc @@ -8,6 +8,8 @@ #define REACTOS_STR_ORIGINAL_FILENAME "fontext.dll" #include +IDR_FONTEXT REGISTRY "res/fontext.rgs" + /* UTF-8 */ #pragma code_page(65001) diff --git a/dll/shellext/fontext/fontpidl.cpp b/dll/shellext/fontext/fontpidl.cpp new file mode 100644 index 00000000000..021afe128a4 --- /dev/null +++ b/dll/shellext/fontext/fontpidl.cpp @@ -0,0 +1,38 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: pidl handling + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +LPITEMIDLIST _ILCreate(LPCWSTR lpString, ULONG Index) +{ + // Because the FontPidlEntry contains one WCHAR, we do not need to take the null terminator into account + size_t cbData = sizeof(FontPidlEntry) + wcslen(lpString) * sizeof(WCHAR); + FontPidlEntry* pidl = (FontPidlEntry*)CoTaskMemAlloc(cbData + sizeof(WORD)); + if (!pidl) + return NULL; + + ZeroMemory(pidl, cbData + sizeof(WORD)); + + pidl->cb = (WORD)cbData; + pidl->Magic = 'fp'; + pidl->Index = Index; + + wcscpy(pidl->Name, lpString); + // Should be zero already, but make sure it is + *(WORD*)((char*)pidl + cbData) = 0; + + return (LPITEMIDLIST)pidl; +} + + +const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl) +{ + const FontPidlEntry* zipPidl = (const FontPidlEntry*)pidl; + if (zipPidl->Magic == 'fp') + return zipPidl; + return NULL; +} diff --git a/dll/shellext/fontext/fontpidl.hpp b/dll/shellext/fontext/fontpidl.hpp new file mode 100644 index 00000000000..f5ef556ddff --- /dev/null +++ b/dll/shellext/fontext/fontpidl.hpp @@ -0,0 +1,24 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: pidl handling + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#pragma once + +#include +struct FontPidlEntry +{ + WORD cb; + WORD Magic; + ULONG Index; // Informative only + + WCHAR Name[1]; +}; +#include + + +LPITEMIDLIST _ILCreate(LPCWSTR lpString, ULONG Index); +const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl); + diff --git a/dll/shellext/fontext/lang/en-US.rc b/dll/shellext/fontext/lang/en-US.rc index 698f1bb2b4a..d7fcae09bef 100644 --- a/dll/shellext/fontext/lang/en-US.rc +++ b/dll/shellext/fontext/lang/en-US.rc @@ -6,3 +6,15 @@ STRINGTABLE BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS Font Folder" END + + +STRINGTABLE +BEGIN + IDS_COL_NAME "Name" +END + + +STRINGTABLE +BEGIN + IDS_FONT_PREVIEW "Preview" +END diff --git a/dll/shellext/fontext/precomp.h b/dll/shellext/fontext/precomp.h new file mode 100644 index 00000000000..28606824c1f --- /dev/null +++ b/dll/shellext/fontext/precomp.h @@ -0,0 +1,39 @@ +#ifndef FONTEXT_PRECOMP_H +#define FONTEXT_PRECOMP_H + + +#define WIN32_NO_STATUS +#define COM_NO_WINDOWS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern const GUID CLSID_CFontExt; +extern LONG g_ModuleRefCnt; + +#include "resource.h" +#include "fontpidl.hpp" +#include "CFontCache.hpp" +#include "CFontExt.hpp" + + +HRESULT _CEnumFonts_CreateInstance(CFontExt* zip, DWORD flags, REFIID riid, LPVOID* ppvOut); +HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, + IShellFolder *psf, REFIID riid, LPVOID* ppvOut); +HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, LPVOID* ppvOut); + + + +#endif /* FONTEXT_PRECOMP_H */ diff --git a/dll/shellext/fontext/regsvr.c b/dll/shellext/fontext/regsvr.c deleted file mode 100644 index 5cc6344bff3..00000000000 --- a/dll/shellext/fontext/regsvr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * PROJECT: fontext.dll - * FILE: dll/shellext/fontext/regsvr.c - * PURPOSE: fontext.dll - * PROGRAMMER: Dmitry Chapyshev (dmitry@reactos.org) - * UPDATE HISTORY: - * 10-06-2008 Created - */ - -#include "fontext.h" - -static HRESULT -REGSVR_RegisterServer() -{ - return S_OK; -} - -static HRESULT -REGSVR_UnregisterServer() -{ - return S_OK; -} - -HRESULT WINAPI -DllRegisterServer(VOID) -{ - return REGSVR_RegisterServer(); -} - -HRESULT WINAPI -DllUnregisterServer(VOID) -{ - return REGSVR_UnregisterServer(); -} diff --git a/dll/shellext/fontext/res/fontext.rgs b/dll/shellext/fontext/res/fontext.rgs new file mode 100644 index 00000000000..24de92faea8 --- /dev/null +++ b/dll/shellext/fontext/res/fontext.rgs @@ -0,0 +1,14 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {BD84B380-8CA2-1069-AB1D-08000948F534} = s 'Fonts' + { + InprocServer32 = s 'fontext.dll' + { + val ThreadingModel = s 'Apartment' + } + DefaultIcon = s '%MODULE%' + } + } +} diff --git a/dll/shellext/fontext/resource.h b/dll/shellext/fontext/resource.h index 2fdfda08f9c..49246c33524 100644 --- a/dll/shellext/fontext/resource.h +++ b/dll/shellext/fontext/resource.h @@ -1,3 +1,9 @@ #pragma once + + #define IDS_REACTOS_FONTS_FOLDER 151 +#define IDS_COL_NAME 301 +#define IDS_FONT_PREVIEW 401 + +#define IDR_FONTEXT 8000 diff --git a/media/inf/syssetup.inf b/media/inf/syssetup.inf index 249956de9d1..ead39397203 100644 --- a/media/inf/syssetup.inf +++ b/media/inf/syssetup.inf @@ -62,6 +62,7 @@ AddReg=Classes 11,,dplayx.dll,1 11,,dsound.dll,1 11,,dxdiagn.dll,1 +11,,fontext.dll,1 11,,hhctrl.ocx,1 11,,hlink.dll,1 11,,hnetcfg.dll,1