[FONTEXT] Initial implementation 1907/head
authorMark Jansen <mark.jansen@reactos.org>
Sat, 3 Aug 2019 13:14:20 +0000 (15:14 +0200)
committerMark Jansen <mark.jansen@reactos.org>
Sat, 19 Oct 2019 16:42:15 +0000 (18:42 +0200)
Create Fonts\desktop.ini when registering the shell ext
Also list the shell extension as needing to be registered at install
CORE-14690

20 files changed:
dll/shellext/fontext/CDataObject.cpp [new file with mode: 0644]
dll/shellext/fontext/CEnumFonts.cpp [new file with mode: 0644]
dll/shellext/fontext/CFontCache.cpp [new file with mode: 0644]
dll/shellext/fontext/CFontCache.hpp [new file with mode: 0644]
dll/shellext/fontext/CFontExt.cpp [new file with mode: 0644]
dll/shellext/fontext/CFontExt.hpp [new file with mode: 0644]
dll/shellext/fontext/CFontMenu.cpp [new file with mode: 0644]
dll/shellext/fontext/CMakeLists.txt
dll/shellext/fontext/fontext.c [deleted file]
dll/shellext/fontext/fontext.cpp [new file with mode: 0644]
dll/shellext/fontext/fontext.h
dll/shellext/fontext/fontext.rc
dll/shellext/fontext/fontpidl.cpp [new file with mode: 0644]
dll/shellext/fontext/fontpidl.hpp [new file with mode: 0644]
dll/shellext/fontext/lang/en-US.rc
dll/shellext/fontext/precomp.h [new file with mode: 0644]
dll/shellext/fontext/regsvr.c [deleted file]
dll/shellext/fontext/res/fontext.rgs [new file with mode: 0644]
dll/shellext/fontext/resource.h
media/inf/syssetup.inf

diff --git a/dll/shellext/fontext/CDataObject.cpp b/dll/shellext/fontext/CDataObject.cpp
new file mode 100644 (file)
index 0000000..252b125
--- /dev/null
@@ -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<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 (file)
index 0000000..7e19718
--- /dev/null
@@ -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<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 (file)
index 0000000..4d3ae5f
--- /dev/null
@@ -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<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 (file)
index 0000000..226c061
--- /dev/null
@@ -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<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 (file)
index 0000000..298c6b8
--- /dev/null
@@ -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 (file)
index 0000000..0e7a044
--- /dev/null
@@ -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<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 (file)
index 0000000..eab4427
--- /dev/null
@@ -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> 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;
+}
+
index b50844e..a5ae388 100644 (file)
@@ -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 (file)
index 3102a79..0000000
+++ /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 <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 (file)
index 0000000..ab1ed51
--- /dev/null
@@ -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;
+}
index aa9d564..a5dd1d0 100644 (file)
@@ -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 */
index c2c4f2b..f4cb69b 100644 (file)
@@ -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 (file)
index 0000000..021afe1
--- /dev/null
@@ -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 (file)
index 0000000..f5ef556
--- /dev/null
@@ -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 <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);
+
index 698f1bb..d7fcae0 100644 (file)
@@ -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 (file)
index 0000000..2860682
--- /dev/null
@@ -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 (file)
index 5cc6344..0000000
+++ /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 (file)
index 0000000..24de92f
--- /dev/null
@@ -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%'
+        }
+    }
+}
index 2fdfda0..49246c3 100644 (file)
@@ -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
index 249956d..ead3939 100644 (file)
@@ -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