https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d9e7c48c1a812dc9293f3…
commit d9e7c48c1a812dc9293f367875915288df7e1df2
Author: Mark Jansen <mark.jansen(a)reactos.org>
AuthorDate: Sat Aug 3 15:14:20 2019 +0200
Commit: Mark Jansen <mark.jansen(a)reactos.org>
CommitDate: Sat Oct 19 18:42:15 2019 +0200
[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(-)
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(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(fontext);
+
+
+#if 0
+static inline void DumpDataObjectFormats(IDataObject* pObject)
+{
+ CComPtr<IEnumFORMATETC> 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<BYTE> 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<IDataObject> 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(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(fontext);
+
+
+
+class CEnumFonts :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ 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<CEnumFonts>(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(a)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<CFontInfo>& 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<CFontInfo> 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(a)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<CFontInfo> m_Fonts;
+
+protected:
+ CFontCache();
+
+ void Insert(CAtlList<CFontInfo>& 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(a)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(a)reactos.org)
+ */
+
+#pragma once
+
+class CFontExt :
+ public CComCoClass<CFontExt, &CLSID_CFontExt>,
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IShellFolder2,
+ public IPersistFolder2
+{
+ CComHeapPtr<ITEMIDLIST> 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(a)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> 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<IContextMenu> 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(a)reactos.org)
- * UPDATE HISTORY:
- * 10-06-2008 Created
- */
-
-#include "fontext.h"
-
-#include <winbase.h>
-#include <debug.h>
-
-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(a)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 <stdarg.h>
#define WIN32_NO_STATUS
-#define COBJMACROS
+#define _INC_WINDOWS
+#define COM_NO_WINDOWS_H
#include <windef.h>
+#include <winbase.h>
+#include <shlobj.h>
+#include <tchar.h>
+#include <strsafe.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlsimpcoll.h>
+#include <atlstr.h>
-#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 <reactos/version.rc>
+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(a)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(a)reactos.org)
+ */
+
+#pragma once
+
+#include <pshpack1.h>
+struct FontPidlEntry
+{
+ WORD cb;
+ WORD Magic;
+ ULONG Index; // Informative only
+
+ WCHAR Name[1];
+};
+#include <poppack.h>
+
+
+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 <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <tchar.h>
+#include <strsafe.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlcoll.h>
+#include <atlstr.h>
+#include <wine/debug.h>
+#include <shellutils.h>
+
+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(a)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