diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.cpp index f96c34564fa..03b56e99336 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.cpp @@ -18,281 +18,14 @@ extern void *GetDWriteCreateFactoryFunctionPointer(); namespace MS { namespace Internal { namespace Text { namespace TextInterface { - Factory^ Factory::Create( - FactoryType factoryType, - IFontSourceCollectionFactory^ fontSourceCollectionFactory, - IFontSourceFactory^ fontSourceFactory - ) - { - return gcnew Factory(factoryType, fontSourceCollectionFactory, fontSourceFactory); - } - - Factory::Factory( - FactoryType factoryType, - IFontSourceCollectionFactory^ fontSourceCollectionFactory, - IFontSourceFactory^ fontSourceFactory - ) : CriticalHandle(IntPtr::Zero) - { - Initialize(factoryType); - _wpfFontFileLoader = gcnew FontFileLoader(fontSourceFactory); - _wpfFontCollectionLoader = gcnew FontCollectionLoader( - fontSourceCollectionFactory, - _wpfFontFileLoader - ); - - _fontSourceFactory = fontSourceFactory; - - IntPtr pIDWriteFontFileLoaderMirror = Marshal::GetComInterfaceForObject( - _wpfFontFileLoader, - IDWriteFontFileLoaderMirror::typeid); - - // Future improvement note: - // This seems a bit hacky, but unclear at this time how to implement this any better. - // When we attempt to unregister these, do we need to keep around the same IntPtr - // representing the result of GetComInterfaceForObject to free it ? Or will it - // be the same if we call it again? - - - - HRESULT hr = _pFactory->RegisterFontFileLoader( - (IDWriteFontFileLoader *)pIDWriteFontFileLoaderMirror.ToPointer() - ); - - Marshal::Release(pIDWriteFontFileLoaderMirror); - - ConvertHresultToException(hr, "Factory::Factory"); - - IntPtr pIDWriteFontCollectionLoaderMirror = Marshal::GetComInterfaceForObject( - _wpfFontCollectionLoader, - IDWriteFontCollectionLoaderMirror::typeid); - hr = _pFactory->RegisterFontCollectionLoader( - (IDWriteFontCollectionLoader *)pIDWriteFontCollectionLoaderMirror.ToPointer() - ); - - Marshal::Release(pIDWriteFontCollectionLoaderMirror); - - ConvertHresultToException(hr, "Factory::Factory"); - } - - __declspec(noinline) void Factory::Initialize( - FactoryType factoryType - ) - { - IUnknown* factoryTemp; - DWRITECREATEFACTORY pfnDWriteCreateFactory = (DWRITECREATEFACTORY)GetDWriteCreateFactoryFunctionPointer(); - - HRESULT hr = (*pfnDWriteCreateFactory)( - DWriteTypeConverter::Convert(factoryType), - __uuidof(IDWriteFactory), - &factoryTemp - ); - - ConvertHresultToException(hr, "Factory::Initialize"); - - _pFactory = (IDWriteFactory*)factoryTemp; - } - - __declspec(noinline) bool Factory::ReleaseHandle() - { - if (_wpfFontCollectionLoader != nullptr) - { - IntPtr pIDWriteFontCollectionLoaderMirror = Marshal::GetComInterfaceForObject( - _wpfFontCollectionLoader, - IDWriteFontCollectionLoaderMirror::typeid); - - _pFactory->UnregisterFontCollectionLoader((IDWriteFontCollectionLoader *)pIDWriteFontCollectionLoaderMirror.ToPointer()); - Marshal::Release(pIDWriteFontCollectionLoaderMirror); - _wpfFontCollectionLoader = nullptr; - } - - if (_wpfFontFileLoader != nullptr) - { - IntPtr pIDWriteFontFileLoaderMirror = Marshal::GetComInterfaceForObject( - _wpfFontFileLoader, - IDWriteFontFileLoaderMirror::typeid); - - _pFactory->UnregisterFontFileLoader((IDWriteFontFileLoader *)pIDWriteFontFileLoaderMirror.ToPointer()); - Marshal::Release(pIDWriteFontFileLoaderMirror); - _wpfFontFileLoader = nullptr; - } - - if (_pFactory != NULL) - { - _pFactory ->Release(); - _pFactory = NULL; - } - - return true; - } - - __declspec(noinline) FontFile^ Factory::CreateFontFile( - System::Uri^ filePathUri - ) - { - IDWriteFontFile* dwriteFontFile = NULL; - HRESULT hr = Factory::CreateFontFile(_pFactory, _wpfFontFileLoader, filePathUri, &dwriteFontFile); - - // If DWrite's CreateFontFileReference fails then try opening the file using WPF's logic. - // The failures that WPF returns are more granular than the HRESULTs that DWrite returns - // thus we use WPF's logic to open the file to throw the same exceptions that - // WPF would have thrown before. - if (FAILED(hr)) - { - IFontSource^ fontSource = _fontSourceFactory->Create(filePathUri->AbsoluteUri); - fontSource->TestFileOpenable(); - - } - - //This call is made to prevent this object from being collected and hence get its finalize method called - //While there are others referencing it. - System::GC::KeepAlive(this); - - ConvertHresultToException(hr, "FontFile^ Factory::CreateFontFile"); - - return gcnew FontFile(dwriteFontFile); - - } - - FontFace^ Factory::CreateFontFace( - System::Uri^ filePathUri, - unsigned int faceIndex - ) - { - return CreateFontFace( - filePathUri, - faceIndex, - FontSimulations::None - ); - } - - FontFace^ Factory::CreateFontFace( - System::Uri^ filePathUri, - unsigned int faceIndex, - FontSimulations fontSimulationFlags - ) - { - FontFile^ fontFile = CreateFontFile(filePathUri); - DWRITE_FONT_FILE_TYPE dwriteFontFileType; - DWRITE_FONT_FACE_TYPE dwriteFontFaceType; - unsigned int numberOfFaces = 0; - - HRESULT hr; - if (fontFile->Analyze( - dwriteFontFileType, - dwriteFontFaceType, - numberOfFaces, - hr - )) - { - if (faceIndex >= numberOfFaces) - { - throw gcnew System::ArgumentOutOfRangeException("faceIndex"); - } - - unsigned char dwriteFontSimulationsFlags = DWriteTypeConverter::Convert(fontSimulationFlags); - IDWriteFontFace* dwriteFontFace = NULL; - IDWriteFontFile* dwriteFontFile = fontFile->DWriteFontFileNoAddRef; - - HRESULT hr = _pFactory->CreateFontFace( - dwriteFontFaceType, - 1, - &dwriteFontFile, - faceIndex, - (DWRITE_FONT_SIMULATIONS) dwriteFontSimulationsFlags, - &dwriteFontFace - ); - System::GC::KeepAlive(fontFile); - System::GC::KeepAlive(this); - - ConvertHresultToException(hr, "FontFace^ Factory::CreateFontFace"); - - return gcnew FontFace(dwriteFontFace); - } - - // This path is here because there is a behavior mismatch between DWrite and WPF. - // If a directory was given instead of a font uri WPF previously throws - // System.UnauthorizedAccessException. We handle most of the exception behavior mismatch - // in FontFile^ Factory::CreateFontFile by opening the file using WPF's previous (prior to DWrite integration) logic if - // CreateFontFileReference fails (please see comments in FontFile^ Factory::CreateFontFile). - // However in this special case DWrite's CreateFontFileReference will succeed if given - // a directory instead of a font file and it is the Analyze() call will fail returning DWRITE_E_FILEFORMAT. - // Thus, incase the hr returned from Analyze() was DWRITE_E_FILEFORMAT we do as we did in FontFile^ Factory::CreateFontFile - // to try and open the file using WPF old logic and throw System.UnauthorizedAccessException as WPF used to do. - // If a file format exception is expected then opening the file should succeed and ConvertHresultToException() - // Should throw the correct exception. - // A final note would be that this overhead is only incurred in error conditions and so the normal execution path should - // not be affected. - else - { - if (hr == DWRITE_E_FILEFORMAT) - { - IFontSource^ fontSource = _fontSourceFactory->Create(filePathUri->AbsoluteUri); - fontSource->TestFileOpenable(); - } - ConvertHresultToException(hr, "FontFace^ Factory::CreateFontFace"); - } - - return nullptr; - } - - FontCollection^ Factory::GetSystemFontCollection() - { - return GetSystemFontCollection(false); - } - - __declspec(noinline) FontCollection^ Factory::GetSystemFontCollection( - bool checkForUpdates - ) - { - IDWriteFontCollection* dwriteFontCollection = NULL; - HRESULT hr = _pFactory->GetSystemFontCollection( - &dwriteFontCollection, - checkForUpdates - ); - System::GC::KeepAlive(this); - - ConvertHresultToException(hr, "FontCollection^ Factory::GetSystemFontCollection"); - - return gcnew FontCollection(dwriteFontCollection); - } - - __declspec(noinline) FontCollection^ Factory::GetFontCollection(System::Uri^ uri) - { - System::String^ uriString = uri->AbsoluteUri; - IDWriteFontCollection* dwriteFontCollection = NULL; - pin_ptr uriPathWChar = PtrToStringChars(uriString); - - IntPtr pIDWriteFontCollectionLoaderMirror = Marshal::GetComInterfaceForObject( - _wpfFontCollectionLoader, - IDWriteFontCollectionLoaderMirror::typeid); - - IDWriteFontCollectionLoader * pIDWriteFontCollectionLoader = - (IDWriteFontCollectionLoader *)pIDWriteFontCollectionLoaderMirror.ToPointer(); - - HRESULT hr = _pFactory->CreateCustomFontCollection( - pIDWriteFontCollectionLoader, - uriPathWChar, - (uriString->Length+1) * sizeof(WCHAR), - &dwriteFontCollection - ); - - Marshal::Release(pIDWriteFontCollectionLoaderMirror); - - System::GC::KeepAlive(this); - - ConvertHresultToException(hr, "FontCollection^ Factory::GetFontCollection"); - - return gcnew FontCollection(dwriteFontCollection); - } - - HRESULT Factory::CreateFontFile( + HRESULT InternalFactory::CreateFontFile( IDWriteFactory* factory, FontFileLoader^ fontFileLoader, System::Uri^ filePathUri, __out IDWriteFontFile** dwriteFontFile ) { - bool isLocal = Factory::IsLocalUri(filePathUri); + bool isLocal = InternalFactory::IsLocalUri(filePathUri); HRESULT hr = E_FAIL; if (isLocal) @@ -402,23 +135,10 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface return hr; } - __declspec(noinline) void Factory::CleanupTimeStampCache() + __declspec(noinline) void InternalFactory::CleanupTimeStampCache() { _timeStampCacheCleanupOp = nullptr; _timeStampCache->Clear(); } - __declspec(noinline) TextAnalyzer^ Factory::CreateTextAnalyzer() - { - IDWriteTextAnalyzer* textAnalyzer = NULL; - HRESULT hr = _pFactory->CreateTextAnalyzer(&textAnalyzer); - System::GC::KeepAlive(this); - ConvertHresultToException(hr, "TextAnalyzer^ Factory::CreateTextAnalyzer"); - return gcnew TextAnalyzer(textAnalyzer); - } - - bool Factory::IsInvalid::get() - { - return (_pFactory == NULL); - } }}}}//MS::Internal::Text::TextInterface diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.h b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.h index 894c1e9278f..e49f9bb81a8 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.h +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Factory.h @@ -25,159 +25,18 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface /// /// The root factory interface for all DWrite objects. /// - private ref class Factory sealed : public CriticalHandle + private ref class InternalFactory abstract sealed { private: - - /// - /// A pointer to the wrapped DWrite factory object. - /// - IDWriteFactory* _pFactory; - - /// - /// Constructs a factory object. - /// - /// Identifies whether the factory object will be shared or isolated. - /// A factory object that will create managed FontSourceCollection - /// objects that will be utilized to load embedded fonts. - /// A factory object that will create managed FontSource - /// objects that will be utilized to load embedded fonts. - /// - /// The factory just created. - /// - Factory( - FactoryType factoryType, - IFontSourceCollectionFactory^ fontSourceCollectionFactory, - IFontSourceFactory^ fontSourceFactory - ); - - /// - /// Initializes a factory object. - /// - /// Identifies whether the factory object will be shared or isolated. - void Initialize(FactoryType factoryType); - - /// - /// The custom loader used by WPF to load font collections. - /// - FontCollectionLoader^ _wpfFontCollectionLoader; - - /// - /// The custom loader used by WPF to load font files. - /// - FontFileLoader^ _wpfFontFileLoader; - - IFontSourceFactory^ _fontSourceFactory; - [ThreadStatic] - static Dictionary^ _timeStampCache; - + static Dictionary^ _timeStampCache; + [ThreadStatic] static DispatcherOperation^ _timeStampCacheCleanupOp; - - static void CleanupTimeStampCache(); - - protected: - - virtual bool ReleaseHandle() override; + static void CleanupTimeStampCache(); internal: - property IDWriteFactory* DWriteFactoryAddRef - { - IDWriteFactory* get() - { - _pFactory->AddRef(); - return _pFactory; - } - } - - /// - /// Creates a DirectWrite factory object that is used for subsequent creation of individual DirectWrite objects. - /// - /// Identifies whether the factory object will be shared or isolated. - /// A factory object that will create managed FontSourceCollection - /// objects that will be utilized to load embedded fonts. - /// A factory object that will create managed FontSource - /// objects that will be utilized to load embedded fonts. - /// - /// The factory just created. - /// - static Factory^ Create( - FactoryType factoryType, - IFontSourceCollectionFactory^ fontSourceCollectionFactory, - IFontSourceFactory^ fontSourceFactory - ); - - - /// - /// Creates a font file object from a local font file. - /// - /// file path uri. - /// - /// Newly created font file object, or NULL in case of failure. - /// - FontFile^ CreateFontFile(System::Uri^ filePathUri); - - /// - /// Creates a font face object. - /// - /// The file path of the font face. - /// The zero based index of a font face in cases when the font files contain a collection of font faces. - /// If the font files contain a single face, this value should be zero. - /// Font face simulation flags for algorithmic emboldening and italicization. - /// - /// Newly created font face object, or NULL in case of failure. - /// - FontFace^ CreateFontFace( - System::Uri^ filePathUri, - unsigned int faceIndex, - FontSimulations fontSimulationFlags - ); - - /// - /// Creates a font face object. - /// - /// The file path of the font face. - /// The zero based index of a font face in cases when the font files contain a collection of font faces. - /// If the font files contain a single face, this value should be zero. - /// - /// Newly created font face object, or NULL in case of failure. - /// - FontFace^ CreateFontFace( - System::Uri^ filePathUri, - unsigned int faceIndex - ); - - /// - /// Gets a font collection representing the set of installed fonts. - /// - /// - /// The system font collection. - /// - FontCollection^ GetSystemFontCollection(); - - /// - /// Gets a font collection in a custom location. - /// - /// The uri of the font collection. - /// - /// The font collection. - /// - FontCollection^ GetFontCollection(System::Uri^ uri); - - /// - /// Gets a font collection representing the set of installed fonts. - /// - /// If this parameter is true, the function performs an immediate check for changes to the set of - /// installed fonts. If this parameter is FALSE, the function will still detect changes if the font cache service is running, but - /// there may be some latency. For example, an application might specify TRUE if it has itself just installed a font and wants to - /// be sure the font collection contains that font. - /// - /// The system font collection. - /// - FontCollection^ GetSystemFontCollection(bool checkForUpdates); - static bool IsLocalUri(System::Uri^ uri) { return (uri->IsFile && uri->IsLoopback && !uri->IsUnc); @@ -213,18 +72,6 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface return transform; } - - TextAnalyzer^ CreateTextAnalyzer(); - - public: - - virtual property bool IsInvalid - { - #pragma warning (disable : 4950) // The Constrained Execution Region (CER) feature is not supported. - [ReliabilityContract(Consistency::WillNotCorruptState, Cer::Success)] - #pragma warning (default : 4950) // The Constrained Execution Region (CER) feature is not supported. - bool get() override; - } }; }}}}//MS::Internal::Text::TextInterface diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Font.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Font.cpp index 3d8008f5715..d7cc67b2aa4 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Font.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/Font.cpp @@ -306,7 +306,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface IDWriteFontFace* fontFace = NULL; HRESULT hr = _font->Value->CreateFontFace(&fontFace); ConvertHresultToException(hr, "FontMetrics^ Font::DisplayMetrics"); - DWRITE_MATRIX transform = Factory::GetIdentityTransform(); + DWRITE_MATRIX transform = InternalFactory::GetIdentityTransform(); hr = fontFace->GetGdiCompatibleMetrics( emSize, pixelsPerDip, diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFileEnumerator.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFileEnumerator.cpp index 896c016a9e8..9837aca7b9c 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFileEnumerator.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFileEnumerator.cpp @@ -46,7 +46,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface return E_INVALIDARG; } - return Factory::CreateFontFile( + return InternalFactory::CreateFontFile( _factory, _fontFileLoader, _fontSourceCollectionEnumerator->Current->Uri, diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.cpp index c06c4bc8ddf..b7186f36dfd 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.cpp @@ -20,7 +20,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface __in_ecount(length) const WCHAR* text, UINT32 length, CultureInfo^ culture, - Factory^ factory, + IDWriteFactory* pDWriteFactory, bool isRightToLeftParagraph, CultureInfo^ numberCulture, bool ignoreUserOverride, @@ -41,7 +41,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface // We obtain an AddRef factory so as not to worry about having to call GC::KeepAlive(factory) // which puts unnecessary maintenance cost on this code. - IDWriteFactory* pDWriteFactory = factory->DWriteFactoryAddRef; + pDWriteFactory->AddRef(); HRESULT hr = S_OK; try { @@ -607,7 +607,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface { String^ localeName = cultureInfo->IetfLanguageTag; pin_ptr pLocaleName = Native::Util::GetPtrToStringChars(localeName); - DWRITE_MATRIX transform = Factory::GetIdentityTransform(); + DWRITE_MATRIX transform = InternalFactory::GetIdentityTransform(); if (features != nullptr) { diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.h b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.h index 7e79bf93aca..437ad6ab4ba 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.h +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/TextAnalyzer.h @@ -32,12 +32,6 @@ using namespace MS::Internal::Text::TextInterface::Generics; namespace MS { namespace Internal { namespace Text { namespace TextInterface { - /*******************************************************************************************************************************/ - //Forward declaration of Factory since there was a circular reference between "TextAnalyzer" & "Factory" - ref class Factory; - /*******************************************************************************************************************************/ - - // The 4 delegates below are used to introduce a level of indirection so we can define // the external methods that reference PresentationNative*.dll in PresenationCore.dll. // The reason we define the methods in PresentationCore.dll is that the string values for the @@ -157,7 +151,7 @@ namespace MS { namespace Internal { namespace Text { namespace TextInterface __in_ecount(length) const WCHAR* text, UINT32 length, CultureInfo^ culture, - Factory^ factory, + IDWriteFactory* pDWriteFactory, bool isRightToLeftParagraph, CultureInfo^ numberCulture, bool ignoreUserOverride, diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp index 06ba7f1c1b5..61ff8727d81 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp @@ -50,49 +50,12 @@ private ref class NativeWPFDLLLoader sealed // static void LoadDwrite( ) { - // We load dwrite here because it's cleanup logic is different from the other native dlls - // and don't want to abstract that - VOID *pTemp = NULL; - m_hDWrite = System::IntPtr(WPFUtils::LoadDWriteLibraryAndGetProcAddress(&pTemp)); - if (m_hDWrite == IntPtr::Zero) - throw gcnew DllNotFoundException(gcnew String(L"dwrite.dll"), gcnew Win32Exception()); - if (pTemp == NULL) - throw gcnew InvalidOperationException(); - m_pfnDWriteCreateFactory = pTemp; - } - - __declspec(noinline) - static void UnloadDWrite() - { - ClearDWriteCreateFactoryFunctionPointer(); - - if (m_hDWrite != IntPtr::Zero) - { - if (!FreeLibrary((HMODULE)(m_hDWrite.ToPointer()))) - { - DWORD lastError = GetLastError(); - Marshal::ThrowExceptionForHR(__HRESULT_FROM_WIN32(lastError)); - } - - m_hDWrite = IntPtr::Zero; - } - } - - static void *GetDWriteCreateFactoryFunctionPointer() - { - return m_pfnDWriteCreateFactory; - } - - static void ClearDWriteCreateFactoryFunctionPointer() - { - m_pfnDWriteCreateFactory = NULL; + // Used to force the compiler to keep LoadDwrite in Release because it is called from PresentationCore. + m_temp = NULL; } private: - - static System::IntPtr m_hDWrite; - - static void *m_pfnDWriteCreateFactory; + static void *m_temp; }; }} // namespace MS.Internal @@ -101,49 +64,14 @@ private class CModuleInitialize public: // Constructor of class CModuleInitialize - __declspec(noinline) CModuleInitialize(void (*cleaningUpFunc)()) + __declspec(noinline) CModuleInitialize() { - MS::Internal::NativeWPFDLLLoader::LoadDwrite(); - // Initialize some global arrays. MS::Internal::TtfDelta::GlobalInit::Init(); MS::Internal::TtfDelta::ControlTableInit::Init(); - atexit(cleaningUpFunc); - } - - // Previously we had this as a class dtor but we found out that - // we can't use a destructor due to an issue with how it's registered to be called on exit: - // A compiler-generated function calls _atexit_m_appdomain(). But that generated function is transparenct, - // which causes a violation because _atexit_m_appdomain() is Critical. - __declspec(noinline) void UnInitialize() - { - MS::Internal::NativeWPFDLLLoader::UnloadDWrite(); - - MS::Internal::NativeWPFDLLLoader::ClearDWriteCreateFactoryFunctionPointer(); - // - // Finalizers run after this dtor so if we unload dwrite now - // we may end up making calls into unloaded code. Yes, this - // is a "leak" but it's only really a leak if no more WPF - // AppDomains are present and it's a single leak since only - // one instance of a version of a CLR may be in proc at - // once. - // - // We could also use a critical finalizer for the handle - // but that requires changing this code quite a bit plus - // if other critical finalizers ever call dwrite code - // we have the same problem again. - // - // MS::Internal::NativeWPFDLLLoader::UnloadDWrite(); - } - - void *GetDWriteCreateFactoryFunctionPointer() - { - return MS::Internal::NativeWPFDLLLoader::GetDWriteCreateFactoryFunctionPointer(); } }; -void CleanUp(); - /// /// This method is a workaround to bug in the compiler. /// The compiler generates a static unsafe method to initialize cmiStartupRunner @@ -152,7 +80,7 @@ void CleanUp(); /// __declspec(noinline) static System::IntPtr CreateCModuleInitialize() { - return System::IntPtr(new CModuleInitialize(CleanUp)); + return System::IntPtr(new CModuleInitialize()); } // Important Note: This variable is declared as System::IntPtr to fool the compiler into creating @@ -160,18 +88,3 @@ __declspec(noinline) static System::IntPtr CreateCModuleInitialize() // Then the generated method is unsafe, fails NGENing and causes Jitting. __declspec(appdomain) static System::IntPtr cmiStartupRunner = CreateCModuleInitialize(); -void CleanUp() -{ - CModuleInitialize* pCmiStartupRunner = static_cast(cmiStartupRunner.ToPointer()); - - pCmiStartupRunner->UnInitialize(); - delete pCmiStartupRunner; - cmiStartupRunner = System::IntPtr(NULL); - -} - -void *GetDWriteCreateFactoryFunctionPointer() -{ - return (static_cast(cmiStartupRunner.ToPointer()))->GetDWriteCreateFactoryFunctionPointer(); -} - diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_FACE_TYPE.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_FACE_TYPE.cs new file mode 100644 index 00000000000..8898971dc05 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_FACE_TYPE.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace MS.Internal.Interop.DWrite +{ + internal enum DWRITE_FONT_FACE_TYPE + { + DWRITE_FONT_FACE_TYPE_CFF, + DWRITE_FONT_FACE_TYPE_TRUETYPE, + DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION, + DWRITE_FONT_FACE_TYPE_TYPE1, + DWRITE_FONT_FACE_TYPE_VECTOR, + DWRITE_FONT_FACE_TYPE_BITMAP, + DWRITE_FONT_FACE_TYPE_UNKNOWN, + DWRITE_FONT_FACE_TYPE_RAW_CFF, + DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION = DWRITE_FONT_FACE_TYPE_OPENTYPE_COLLECTION, + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_SIMULATIONS.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_SIMULATIONS.cs new file mode 100644 index 00000000000..01fbd4619f7 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/DWRITE_FONT_SIMULATIONS.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace MS.Internal.Interop.DWrite +{ + internal enum DWRITE_FONT_SIMULATIONS + { + DWRITE_FONT_SIMULATIONS_NONE = 0x0000, + DWRITE_FONT_SIMULATIONS_BOLD = 0x0001, + DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002, + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFactory.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFactory.cs new file mode 100644 index 00000000000..1c41dd285c2 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFactory.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFactory : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFactory*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFactory*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFactory*)Unsafe.AsPointer(ref this)); + } + + public int GetSystemFontCollection(IDWriteFontCollection** fontCollection, int checkForUpdates = 0) + { + return ((delegate* unmanaged)(lpVtbl[3]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontCollection, checkForUpdates); + } + + public int CreateCustomFontCollection(IDWriteFontCollectionLoader* collectionLoader, void* collectionKey, uint collectionKeySize, IDWriteFontCollection** fontCollection) + { + return ((delegate* unmanaged)(lpVtbl[4]))((IDWriteFactory*)Unsafe.AsPointer(ref this), collectionLoader, collectionKey, collectionKeySize, fontCollection); + } + + public int RegisterFontCollectionLoader(IDWriteFontCollectionLoader* fontCollectionLoader) + { + return ((delegate* unmanaged)(lpVtbl[5]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontCollectionLoader); + } + + public int UnregisterFontCollectionLoader(IDWriteFontCollectionLoader* fontCollectionLoader) + { + return ((delegate* unmanaged)(lpVtbl[6]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontCollectionLoader); + } + + public int CreateFontFace(DWRITE_FONT_FACE_TYPE fontFaceType, uint numberOfFiles, IDWriteFontFile** fontFiles, uint faceIndex, DWRITE_FONT_SIMULATIONS fontFaceSimulationFlags, IDWriteFontFace** fontFace) + { + return ((delegate* unmanaged)(lpVtbl[9]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontFaceType, numberOfFiles, fontFiles, faceIndex, fontFaceSimulationFlags, fontFace); + } + + public int RegisterFontFileLoader(IDWriteFontFileLoader* fontFileLoader) + { + return ((delegate* unmanaged)(lpVtbl[13]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontFileLoader); + } + + public int UnregisterFontFileLoader(IDWriteFontFileLoader* fontFileLoader) + { + return ((delegate* unmanaged)(lpVtbl[14]))((IDWriteFactory*)Unsafe.AsPointer(ref this), fontFileLoader); + } + + public int CreateTextAnalyzer(IDWriteTextAnalyzer** textAnalyzer) + { + return ((delegate* unmanaged)(lpVtbl[21]))((IDWriteFactory*)Unsafe.AsPointer(ref this), textAnalyzer); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollection.cs new file mode 100644 index 00000000000..9107e4e8f53 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollection.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFontCollection : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFontCollection*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFontCollection*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFontCollection*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollectionLoader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollectionLoader.cs new file mode 100644 index 00000000000..44fddcccd47 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontCollectionLoader.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFontCollectionLoader : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFontCollectionLoader*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFontCollectionLoader*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFontCollectionLoader*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFace.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFace.cs new file mode 100644 index 00000000000..4ca49c06a39 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFace.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFontFace : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFontFace*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFontFace*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFontFace*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFile.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFile.cs new file mode 100644 index 00000000000..ddb46baae68 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFile.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFontFile : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFontFile*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFontFile*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFontFile*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFileLoader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFileLoader.cs new file mode 100644 index 00000000000..8841d32884a --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteFontFileLoader.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteFontFileLoader : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteFontFileLoader*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteFontFileLoader*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteFontFileLoader*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteTextAnalyzer.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteTextAnalyzer.cs new file mode 100644 index 00000000000..308d6b87c18 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/DWrite/IDWriteTextAnalyzer.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace MS.Internal.Interop.DWrite +{ + internal unsafe struct IDWriteTextAnalyzer : IUnknown + { + public void** lpVtbl; + + public int QueryInterface(Guid* riid, void** ppvObject) + { + return ((delegate* unmanaged)(lpVtbl[0]))((IDWriteTextAnalyzer*)Unsafe.AsPointer(ref this), riid, ppvObject); + } + + public uint AddRef() + { + return ((delegate* unmanaged)(lpVtbl[1]))((IDWriteTextAnalyzer*)Unsafe.AsPointer(ref this)); + } + + public uint Release() + { + return ((delegate* unmanaged)(lpVtbl[2]))((IDWriteTextAnalyzer*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/IUnknown.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/IUnknown.cs new file mode 100644 index 00000000000..fcab94b4427 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/IUnknown.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace MS.Internal.Interop +{ + internal unsafe interface IUnknown + { + int QueryInterface(Guid* guid, void** comObject); + + uint AddRef(); + + uint Release(); + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/NativePointerCriticalHandle.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/NativePointerCriticalHandle.cs new file mode 100644 index 00000000000..631330ba478 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Interop/NativePointerCriticalHandle.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace MS.Internal.Interop +{ + internal abstract unsafe class NativePointerCriticalHandle : CriticalHandle + where TClass : unmanaged + { + public NativePointerCriticalHandle(IntPtr nativePointer) + : base(IntPtr.Zero) + { + SetHandle(nativePointer); + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + public TClass* Value => (TClass*)handle; + } + + internal unsafe class NativeIUnknownWrapper : NativePointerCriticalHandle + where TClass : unmanaged, IUnknown + { + public NativeIUnknownWrapper(void* nativePointer) + : base((IntPtr)nativePointer) + { + } + + protected override bool ReleaseHandle() + { + Value->Release(); + + handle = IntPtr.Zero; + + return true; + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Shaping/TypefaceMap.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Shaping/TypefaceMap.cs index 9961ecde59d..358b84d3f0f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Shaping/TypefaceMap.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Shaping/TypefaceMap.cs @@ -120,7 +120,7 @@ TextFormattingMode textFormattingMode (char*)ptext.ToPointer(), (uint)stringLength, culture, - MS.Internal.FontCache.DWriteFactory.Instance, + (MS.Internal.Text.TextInterface.Native.IDWriteFactory*)MS.Internal.FontCache.DWriteFactory.Instance.DWriteFactory, isRightToLeftParagraph, digitCulture, ignoreUserOverride, diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteLoader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteLoader.cs new file mode 100644 index 00000000000..e60312cbb69 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteLoader.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace MS.Internal.Text.TextInterface +{ + internal static unsafe class DWriteLoader + { + private static IntPtr _dwrite; + private static delegate* unmanaged _dwriteCreateFactory; + + internal static void LoadDWrite() + { + // We load dwrite here because it's cleanup logic is different from the other native dlls + // and don't want to abstract that + _dwrite = LoadDWriteLibraryAndGetProcAddress(out delegate* unmanaged dwriteCreateFactory); + + if (_dwrite == IntPtr.Zero) + throw new DllNotFoundException("dwrite.dll", new Win32Exception()); + + if (dwriteCreateFactory == null) + throw new InvalidOperationException(); + + _dwriteCreateFactory = dwriteCreateFactory; + } + + internal static void UnloadDWrite() + { + ClearDWriteCreateFactoryFunctionPointer(); + + if (_dwrite != IntPtr.Zero) + { + NativeLibrary.Free(_dwrite); + + _dwrite = IntPtr.Zero; + } + } + + internal static delegate* unmanaged GetDWriteCreateFactoryFunctionPointer() + { + return _dwriteCreateFactory; + } + + private static void ClearDWriteCreateFactoryFunctionPointer() + { + _dwriteCreateFactory = null; + } + + private static IntPtr LoadDWriteLibraryAndGetProcAddress(out delegate* unmanaged DWriteCreateFactory) + { + IntPtr hDWriteLibrary = NativeLibrary.Load("dwrite.dll", typeof(DWriteLoader).Assembly, DllImportSearchPath.System32); + + if (hDWriteLibrary != IntPtr.Zero) + { + DWriteCreateFactory = (delegate* unmanaged)NativeLibrary.GetExport(hDWriteLibrary, "DWriteCreateFactory"); + } + else + { + DWriteCreateFactory = null; + } + + return hDWriteLibrary; + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteUtil.cs new file mode 100644 index 00000000000..03a8a2e9710 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/DWriteUtil.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace MS.Internal.Text.TextInterface +{ + internal static class DWriteUtil + { + private const int COR_E_INVALIDOPERATION = 0x1509; + private const int DWRITE_E_FILENOTFOUND = unchecked((int)0x88985003L); + private const int DWRITE_E_FILEACCESS = unchecked((int)0x88985004L); + private const int DWRITE_E_FILEFORMAT = unchecked((int)0x88985000L); + + internal static void ConvertHresultToException(int hr) + { + + if (hr != 0) + { + if (hr == DWRITE_E_FILENOTFOUND) + { + throw new System.IO.FileNotFoundException(); + } + else if (hr == DWRITE_E_FILEACCESS) + { + throw new System.UnauthorizedAccessException(); + } + else if (hr == DWRITE_E_FILEFORMAT) + { + throw new System.IO.FileFormatException(); + } + else + { + SanitizeAndThrowIfKnownException(hr); + + // ThrowExceptionForHR method returns an exception based on the IErrorInfo of + // the current thread if one is set. When this happens, the errorCode parameter + // is ignored. + // We pass an IntPtr that has a value of -1 so that ThrowExceptionForHR ignores + // IErrorInfo of the current thread. + System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr, new System.IntPtr(-1)); + } + } + } + + /// + /// Exceptions known to have security sensitive data are sanitized in this method, + /// by throwing a copy of the original exception without security sensitive data. + /// Or, to put another way - this function acts only on a list of security sensitive HRESULT/IErrorInfo combinations, throwing for matches. + /// The IErrorInfo is taken into account in a call to GetExceptionForHR(HRESULT), see MSDN for more details. + /// + + private static void SanitizeAndThrowIfKnownException(int hr) + { + if (hr == COR_E_INVALIDOPERATION) + { + System.Exception e = System.Runtime.InteropServices.Marshal.GetExceptionForHR(hr); + if (e is System.Net.WebException) + { + throw e; + } + } + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/Factory.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/Factory.cs new file mode 100644 index 00000000000..d7b077ea9f5 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Text/TextInterface/Factory.cs @@ -0,0 +1,382 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using MS.Internal.Interop; +using MS.Internal.Interop.DWrite; +using MS.Internal.Text.TextInterface.Interfaces; + +namespace MS.Internal.Text.TextInterface +{ + /// + /// The root factory interface for all DWrite objects. + /// + internal unsafe class Factory + { + private const int DWRITE_E_FILEFORMAT = unchecked((int)0x88985000L); + + // b859ee5a-d838-4b5b-a2e8-1adc7d93db48 + private static readonly Guid IID_IDWriteFactory = new Guid(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48); + + /// + /// A pointer to the wrapped DWrite factory object. + /// + private NativeFactoryWrapper _factory; + + /// + /// The custom loader used by WPF to load font files. + /// + private FontFileLoader _wpfFontFileLoader; + + /// + /// The custom loader used by WPF to load font collections. + /// + private FontCollectionLoader _wpfFontCollectionLoader; + + private readonly IFontSourceFactory _fontSourceFactory; + + /// + /// Constructs a factory object. + /// + /// Identifies whether the factory object will be shared or isolated. + /// A factory object that will create managed FontSourceCollection + /// objects that will be utilized to load embedded fonts. + /// A factory object that will create managed FontSource + /// objects that will be utilized to load embedded fonts. + /// + /// The factory just created. + /// + private Factory(FactoryType factoryType, IFontSourceCollectionFactory fontSourceCollectionFactory, IFontSourceFactory fontSourceFactory) + { + Initialize(factoryType); + + _wpfFontFileLoader = new FontFileLoader(fontSourceFactory); + _wpfFontCollectionLoader = new FontCollectionLoader( + fontSourceCollectionFactory, + _wpfFontFileLoader + ); + + _fontSourceFactory = fontSourceFactory; + + IntPtr pIDWriteFontFileLoaderMirror = Marshal.GetComInterfaceForObject( + _wpfFontFileLoader, + typeof(IDWriteFontFileLoaderMirror)); + + // Future improvement note: + // This seems a bit hacky, but unclear at this time how to implement this any better. + // When we attempt to unregister these, do we need to keep around the same IntPtr + // representing the result of GetComInterfaceForObject to free it ? Or will it + // be the same if we call it again? + + + + int hr = _factory.Value->RegisterFontFileLoader( + (IDWriteFontFileLoader*)pIDWriteFontFileLoaderMirror.ToPointer() + ); + + Marshal.Release(pIDWriteFontFileLoaderMirror); + + DWriteUtil.ConvertHresultToException(hr); + + IntPtr pIDWriteFontCollectionLoaderMirror = Marshal.GetComInterfaceForObject( + _wpfFontCollectionLoader, + typeof(IDWriteFontCollectionLoaderMirror)); + hr = _factory.Value->RegisterFontCollectionLoader( + (IDWriteFontCollectionLoader*)pIDWriteFontCollectionLoaderMirror.ToPointer() + ); + + Marshal.Release(pIDWriteFontCollectionLoaderMirror); + + DWriteUtil.ConvertHresultToException(hr); + } + + internal IDWriteFactory* DWriteFactory + => _factory.Value; + + /// + /// Initializes a factory object. + /// + /// Identifies whether the factory object will be shared or isolated. + private void Initialize(FactoryType factoryType) + { + Guid iid = IID_IDWriteFactory; + IDWriteFactory* factory = null; + + delegate* unmanaged pfnDWriteCreateFactory = DWriteLoader.GetDWriteCreateFactoryFunctionPointer(); + + int hr = pfnDWriteCreateFactory((int)DWriteTypeConverter.Convert(factoryType), &iid, &factory); + + DWriteUtil.ConvertHresultToException(hr); + + _factory = new NativeFactoryWrapper(factory, this); + } + + /// + /// Creates a DirectWrite factory object that is used for subsequent creation of individual DirectWrite objects. + /// + /// Identifies whether the factory object will be shared or isolated. + /// A factory object that will create managed FontSourceCollection + /// objects that will be utilized to load embedded fonts. + /// A factory object that will create managed FontSource + /// objects that will be utilized to load embedded fonts. + /// + /// The factory just created. + /// + internal static Factory Create( + FactoryType factoryType, + IFontSourceCollectionFactory fontSourceCollectionFactory, + IFontSourceFactory fontSourceFactory) + { + return new Factory(factoryType, fontSourceCollectionFactory, fontSourceFactory); + } + + /// + /// Creates a font file object from a local font file. + /// + /// file path uri. + /// + /// Newly created font file object, or NULL in case of failure. + /// + internal FontFile CreateFontFile(Uri filePathUri) + { + Native.IDWriteFontFile* dwriteFontFile = null; + int hr = InternalFactory.CreateFontFile((Native.IDWriteFactory*)_factory.Value, _wpfFontFileLoader, filePathUri, &dwriteFontFile); + + // If DWrite's CreateFontFileReference fails then try opening the file using WPF's logic. + // The failures that WPF returns are more granular than the HRESULTs that DWrite returns + // thus we use WPF's logic to open the file to throw the same exceptions that + // WPF would have thrown before. + if (hr != 0) + { + IFontSource fontSource = _fontSourceFactory.Create(filePathUri.AbsoluteUri); + fontSource.TestFileOpenable(); + + } + + //This call is made to prevent this object from being collected and hence get its finalize method called + //While there are others referencing it. + GC.KeepAlive(this); + + DWriteUtil.ConvertHresultToException(hr); + + return new FontFile(dwriteFontFile); + } + + /// + /// Creates a font face object. + /// + /// The file path of the font face. + /// The zero based index of a font face in cases when the font files contain a collection of font faces. + /// If the font files contain a single face, this value should be zero. + /// + /// Newly created font face object, or NULL in case of failure. + /// + internal FontFace CreateFontFace(Uri filePathUri, uint faceIndex) + { + return CreateFontFace( + filePathUri, + faceIndex, + FontSimulations.None + ); + } + + /// + /// Creates a font face object. + /// + /// The file path of the font face. + /// The zero based index of a font face in cases when the font files contain a collection of font faces. + /// If the font files contain a single face, this value should be zero. + /// Font face simulation flags for algorithmic emboldening and italicization. + /// + /// Newly created font face object, or NULL in case of failure. + /// + internal FontFace CreateFontFace(Uri filePathUri, uint faceIndex, FontSimulations fontSimulationFlags) + { + FontFile fontFile = CreateFontFile(filePathUri); + Native.DWRITE_FONT_FILE_TYPE dwriteFontFileType; + Native.DWRITE_FONT_FACE_TYPE dwriteFontFaceType; + uint numberOfFaces = 0; + + int hr; + if (fontFile.Analyze( + out dwriteFontFileType, + out dwriteFontFaceType, + out numberOfFaces, + &hr + )) + { + if (faceIndex >= numberOfFaces) + { + throw new ArgumentOutOfRangeException("faceIndex"); + } + + byte dwriteFontSimulationsFlags = DWriteTypeConverter.Convert(fontSimulationFlags); + IDWriteFontFace* dwriteFontFace = null; + IDWriteFontFile* dwriteFontFile = (IDWriteFontFile*)fontFile.DWriteFontFileNoAddRef; + + hr = _factory.Value->CreateFontFace( + (DWRITE_FONT_FACE_TYPE)dwriteFontFaceType, + 1, + &dwriteFontFile, + faceIndex, + (DWRITE_FONT_SIMULATIONS)dwriteFontSimulationsFlags, + &dwriteFontFace + ); + GC.KeepAlive(fontFile); + GC.KeepAlive(this); + + DWriteUtil.ConvertHresultToException(hr); + + return new FontFace((Native.IDWriteFontFace*)dwriteFontFace); + } + + // This path is here because there is a behavior mismatch between DWrite and WPF. + // If a directory was given instead of a font uri WPF previously throws + // System.UnauthorizedAccessException. We handle most of the exception behavior mismatch + // in FontFile^ Factory::CreateFontFile by opening the file using WPF's previous (prior to DWrite integration) logic if + // CreateFontFileReference fails (please see comments in FontFile^ Factory::CreateFontFile). + // However in this special case DWrite's CreateFontFileReference will succeed if given + // a directory instead of a font file and it is the Analyze() call will fail returning DWRITE_E_FILEFORMAT. + // Thus, incase the hr returned from Analyze() was DWRITE_E_FILEFORMAT we do as we did in FontFile^ Factory::CreateFontFile + // to try and open the file using WPF old logic and throw System.UnauthorizedAccessException as WPF used to do. + // If a file format exception is expected then opening the file should succeed and ConvertHresultToException() + // Should throw the correct exception. + // A final note would be that this overhead is only incurred in error conditions and so the normal execution path should + // not be affected. + else + { + if (hr == DWRITE_E_FILEFORMAT) + { + IFontSource fontSource = _fontSourceFactory.Create(filePathUri.AbsoluteUri); + fontSource.TestFileOpenable(); + } + DWriteUtil.ConvertHresultToException(hr); + } + + return null; + } + + /// + /// Gets a font collection representing the set of installed fonts. + /// + /// + /// The system font collection. + /// + internal FontCollection GetSystemFontCollection() + { + return GetSystemFontCollection(false); + } + + /// + /// Gets a font collection representing the set of installed fonts. + /// + /// If this parameter is true, the function performs an immediate check for changes to the set of + /// installed fonts. If this parameter is FALSE, the function will still detect changes if the font cache service is running, but + /// there may be some latency. For example, an application might specify TRUE if it has itself just installed a font and wants to + /// be sure the font collection contains that font. + /// + /// The system font collection. + /// + private FontCollection GetSystemFontCollection(bool checkForUpdates) + { + IDWriteFontCollection* fontCollection = null; + int checkForUpdatesInt = checkForUpdates ? 1 : 0; + int hr = _factory.Value->GetSystemFontCollection(&fontCollection, checkForUpdatesInt); + + DWriteUtil.ConvertHresultToException(hr); + + return new FontCollection((Native.IDWriteFontCollection*)fontCollection); + } + + /// + /// Gets a font collection in a custom location. + /// + /// The uri of the font collection. + /// + /// The font collection. + /// + internal FontCollection GetFontCollection(Uri uri) + { + string uriString = uri.AbsoluteUri; + IDWriteFontCollection* dwriteFontCollection = null; + + IntPtr pIDWriteFontCollectionLoaderMirror = Marshal.GetComInterfaceForObject( + _wpfFontCollectionLoader, + typeof(IDWriteFontCollectionLoaderMirror)); + + IDWriteFontCollectionLoader* pIDWriteFontCollectionLoader = + (IDWriteFontCollectionLoader*)pIDWriteFontCollectionLoaderMirror.ToPointer(); + + int hr; + + fixed (char* uriStringPtr = uriString) + { + uint collectionKeySize = (uint)((uriString.Length + 1) * sizeof(char)); + hr = _factory.Value->CreateCustomFontCollection(pIDWriteFontCollectionLoader, uriStringPtr, collectionKeySize, &dwriteFontCollection); + } + + Marshal.Release(pIDWriteFontCollectionLoaderMirror); + + GC.KeepAlive(this); + + DWriteUtil.ConvertHresultToException(hr); + + return new FontCollection((Native.IDWriteFontCollection*)dwriteFontCollection); + } + + internal TextAnalyzer CreateTextAnalyzer() + { + IDWriteTextAnalyzer* textAnalyzer = null; + + _factory.Value->CreateTextAnalyzer(&textAnalyzer); + + return new TextAnalyzer((Native.IDWriteTextAnalyzer*)textAnalyzer); + } + + internal static bool IsLocalUri(Uri uri) + { + return uri.IsFile && uri.IsLoopback && !uri.IsUnc; + } + + private sealed unsafe class NativeFactoryWrapper : NativeIUnknownWrapper + { + private readonly Factory _managedFactory; + + public NativeFactoryWrapper(void* nativePointer, Factory managedFactory) + : base(nativePointer) + { + _managedFactory = managedFactory; + } + + protected override bool ReleaseHandle() + { + FontCollectionLoader wpfFontCollectionLoader = _managedFactory._wpfFontCollectionLoader; + if (wpfFontCollectionLoader != null) + { + IntPtr pIDWriteFontCollectionLoaderMirror = Marshal.GetComInterfaceForObject( + wpfFontCollectionLoader, + typeof(IDWriteFontCollectionLoaderMirror)); + + Value->UnregisterFontCollectionLoader((IDWriteFontCollectionLoader*)pIDWriteFontCollectionLoaderMirror.ToPointer()); + Marshal.Release(pIDWriteFontCollectionLoaderMirror); + _managedFactory._wpfFontCollectionLoader = null; + } + + FontFileLoader wpfFontFileLoader = _managedFactory._wpfFontFileLoader; + if (wpfFontFileLoader != null) + { + IntPtr pIDWriteFontFileLoaderMirror = Marshal.GetComInterfaceForObject( + wpfFontFileLoader, + typeof(IDWriteFontFileLoaderMirror)); + + Value->UnregisterFontFileLoader((IDWriteFontFileLoader*)pIDWriteFontFileLoaderMirror.ToPointer()); + Marshal.Release(pIDWriteFontFileLoaderMirror); + _managedFactory._wpfFontFileLoader = null; + } + + return base.ReleaseHandle(); + } + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/ModuleInitializer.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/ModuleInitializer.cs index 2d68e3c6ce1..732e4302df5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/ModuleInitializer.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/ModuleInitializer.cs @@ -2,6 +2,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using MS.Internal.Text.TextInterface; internal static class ModuleInitializer { @@ -19,6 +20,13 @@ public static void Initialize() { IsProcessDpiAware(); + DWriteLoader.LoadDWrite(); + + AppDomain.CurrentDomain.ProcessExit += static (object sender, EventArgs e) => + { + DWriteLoader.UnloadDWrite(); + }; + MS.Internal.NativeWPFDLLLoader.LoadDwrite(); } #pragma warning restore CA2255 diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj index 113551ddcc2..41d91b7317a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj @@ -229,6 +229,17 @@ + + + + + + + + + + + @@ -262,6 +273,9 @@ + + +