[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
This commit is contained in:
Mark Jansen 2019-08-03 15:14:20 +02:00
parent 730a99c860
commit d9e7c48c1a
No known key found for this signature in database
GPG key ID: B39240EE84BEAE8B
20 changed files with 1382 additions and 94 deletions

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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++;
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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()
};

View file

@ -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;
}

View 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)

View file

@ -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;
}

View file

@ -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;
}

View 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 */

View 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)

View file

@ -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;
}

View file

@ -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);

View 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

View file

@ -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 */

View file

@ -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();
}

View file

@ -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%'
}
}
}

View 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

View 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